mcp-framework
Version:
Framework for building Model Context Protocol (MCP) servers in Typescript
125 lines (124 loc) • 4.1 kB
TypeScript
import { z } from 'zod';
import { Tool as SDKTool } from '@modelcontextprotocol/sdk/types.js';
import { ImageContent } from '../transports/utils/image-handler.js';
export type ToolInputSchema<T> = {
[K in keyof T]: {
type: z.ZodType<T[K]>;
description: string;
};
};
export type ToolInput<T extends ToolInputSchema<any>> = {
[K in keyof T]: z.infer<T[K]['type']>;
};
export type InferSchemaType<TSchema> = TSchema extends z.ZodObject<any> ? z.infer<TSchema> : TSchema extends ToolInputSchema<infer T> ? T : never;
export type MCPInput<T extends MCPTool<any, any> = MCPTool<any, any>> = InferSchemaType<T['schema']>;
export type TextContent = {
type: 'text';
text: string;
};
export type ErrorContent = {
type: 'error';
text: string;
};
export type ToolContent = TextContent | ErrorContent | ImageContent;
export type ToolResponse = {
content: ToolContent[];
};
export interface ToolProtocol extends SDKTool {
name: string;
description: string;
toolDefinition: {
name: string;
description: string;
inputSchema: {
type: 'object';
properties?: Record<string, unknown>;
required?: string[];
};
};
toolCall(request: {
params: {
name: string;
arguments?: Record<string, unknown>;
};
}): Promise<ToolResponse>;
}
/**
* Base class for MCP tools using Zod schemas for input validation and type inference.
*
* Define your tool schema using Zod with descriptions:
* ```typescript
* const schema = z.object({
* message: z.string().describe("The message to process")
* });
*
* class MyTool extends MCPTool {
* name = "my_tool";
* description = "My tool description";
* schema = schema;
*
* async execute(input: McpInput<this>) {
* // input is fully typed from your schema
* return input.message;
* }
* }
* ```
*/
export declare abstract class MCPTool<TInput extends Record<string, any> = any, TSchema = any> implements ToolProtocol {
abstract name: string;
abstract description: string;
protected abstract schema: TSchema extends z.ZodObject<any> ? TSchema : TSchema extends ToolInputSchema<any> ? TSchema : z.ZodObject<any> | ToolInputSchema<TInput>;
protected useStringify: boolean;
[key: string]: unknown;
/**
* Validates the tool schema. This is called automatically when the tool is registered
* with an MCP server, but can also be called manually for testing.
*/
validate(): void;
private isZodObjectSchema;
get inputSchema(): {
type: 'object';
properties?: Record<string, unknown>;
required?: string[];
};
private generateSchemaFromZodObject;
private extractFieldInfo;
private getJsonSchemaTypeFromZod;
private generateSchemaFromLegacyFormat;
get toolDefinition(): {
name: string;
description: string;
inputSchema: {
type: "object";
properties?: Record<string, unknown>;
required?: string[];
};
};
protected abstract execute(input: TSchema extends z.ZodObject<any> ? z.infer<TSchema> : TInput): Promise<unknown>;
toolCall(request: {
params: {
name: string;
arguments?: Record<string, unknown>;
};
}): Promise<ToolResponse>;
private validateInput;
private getJsonSchemaType;
protected createSuccessResponse(data: unknown): ToolResponse;
protected createErrorResponse(error: Error): ToolResponse;
private isImageContent;
private isTextContent;
private isErrorContent;
private isValidContent;
protected fetch<T>(url: string, init?: RequestInit): Promise<T>;
}
/**
* Helper function to define tool schemas with required descriptions.
* This ensures all fields have descriptions at build time.
*
* @example
* const schema = defineSchema({
* name: z.string().describe("User's name"),
* age: z.number().describe("User's age")
* });
*/
export declare function defineSchema<T extends z.ZodRawShape>(shape: T): z.ZodObject<T>;