UNPKG

mcp-framework

Version:

Framework for building Model Context Protocol (MCP) servers in Typescript

112 lines (111 loc) 3.48 kB
import { z } from "zod"; export class MCPTool { get inputSchema() { return { type: "object", properties: Object.fromEntries(Object.entries(this.schema).map(([key, schema]) => [ key, { type: this.getJsonSchemaType(schema.type), description: schema.description, }, ])), }; } get toolDefinition() { return { name: this.name, description: this.description, inputSchema: this.inputSchema, }; } async toolCall(request) { try { const args = request.params.arguments || {}; const validatedInput = await this.validateInput(args); const result = await this.execute(validatedInput); return this.createSuccessResponse(result); } catch (error) { return this.createErrorResponse(error); } } async validateInput(args) { const zodSchema = z.object(Object.fromEntries(Object.entries(this.schema).map(([key, schema]) => [key, schema.type]))); return zodSchema.parse(args); } getJsonSchemaType(zodType) { if (zodType instanceof z.ZodString) return "string"; if (zodType instanceof z.ZodNumber) return "number"; if (zodType instanceof z.ZodBoolean) return "boolean"; if (zodType instanceof z.ZodArray) return "array"; if (zodType instanceof z.ZodObject) return "object"; return "string"; } createSuccessResponse(data) { if (this.isImageContent(data)) { return { content: [data], }; } if (Array.isArray(data)) { const validContent = data.filter(item => this.isValidContent(item)); if (validContent.length > 0) { return { content: validContent, }; } } return { content: [{ type: "text", text: JSON.stringify(data) }], }; } createErrorResponse(error) { return { content: [{ type: "error", text: error.message }], }; } isImageContent(data) { return (typeof data === "object" && data !== null && "type" in data && data.type === "image" && "data" in data && "mimeType" in data && typeof data.data === "string" && typeof data.mimeType === "string"); } isTextContent(data) { return (typeof data === "object" && data !== null && "type" in data && data.type === "text" && "text" in data && typeof data.text === "string"); } isErrorContent(data) { return (typeof data === "object" && data !== null && "type" in data && data.type === "error" && "text" in data && typeof data.text === "string"); } isValidContent(data) { return (this.isImageContent(data) || this.isTextContent(data) || this.isErrorContent(data)); } async fetch(url, init) { const response = await fetch(url, init); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } }