UNPKG

genkitx-mcp

Version:

A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.

1 lines 11.9 kB
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Genkit,\n GenkitError,\n Message,\n MessageData,\n PromptAction,\n} from 'genkit';\nimport type { McpServerOptions } from './index.js';\n\nimport { toJsonSchema } from '@genkit-ai/core/schema';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js' with { 'resolution-mode': 'import' };\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' with { 'resolution-mode': 'import' };\nimport type {\n CallToolRequest,\n CallToolResult,\n GetPromptRequest,\n GetPromptResult,\n ListPromptsRequest,\n ListPromptsResult,\n ListToolsRequest,\n ListToolsResult,\n Prompt,\n PromptMessage,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport { logger } from 'genkit/logging';\nimport { ToolAction, toToolDefinition } from 'genkit/tool';\nexport class GenkitMcpServer {\n ai: Genkit;\n options: McpServerOptions;\n server?: Server;\n actionsResolved: boolean = false;\n toolActions: ToolAction[] = [];\n promptActions: PromptAction[] = [];\n\n constructor(ai: Genkit, options: McpServerOptions) {\n this.ai = ai;\n this.options = options;\n this.setup();\n }\n\n async setup(): Promise<void> {\n if (this.actionsResolved) return;\n const { Server } = await import(\n '@modelcontextprotocol/sdk/server/index.js'\n );\n\n this.server = new Server(\n { name: this.options.name, version: this.options.version || '1.0.0' },\n {\n capabilities: {\n prompts: {},\n tools: {},\n },\n }\n );\n\n const {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n } = await import('@modelcontextprotocol/sdk/types.js');\n\n this.server.setRequestHandler(\n ListToolsRequestSchema,\n this.listTools.bind(this)\n );\n this.server.setRequestHandler(\n CallToolRequestSchema,\n this.callTool.bind(this)\n );\n this.server.setRequestHandler(\n ListPromptsRequestSchema,\n this.listPrompts.bind(this)\n );\n this.server.setRequestHandler(\n GetPromptRequestSchema,\n this.getPrompt.bind(this)\n );\n\n const allActions = await this.ai.registry.listActions();\n const toolList: ToolAction[] = [];\n const promptList: PromptAction[] = [];\n for (const k in allActions) {\n if (k.startsWith('/tool/')) {\n toolList.push(allActions[k] as ToolAction);\n } else if (k.startsWith('/prompt/')) {\n promptList.push(allActions[k] as PromptAction);\n }\n }\n this.toolActions = toolList;\n this.promptActions = promptList;\n this.actionsResolved = true;\n }\n\n async listTools(req: ListToolsRequest): Promise<ListToolsResult> {\n await this.setup();\n return {\n tools: this.toolActions.map((t): Tool => {\n const def = toToolDefinition(t);\n return {\n name: def.name,\n inputSchema: (def.inputSchema as any) || { type: 'object' },\n description: def.description,\n };\n }),\n };\n }\n\n async callTool(req: CallToolRequest): Promise<CallToolResult> {\n await this.setup();\n const tool = this.toolActions.find(\n (t) => t.__action.name === req.params.name\n );\n if (!tool)\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `Tried to call tool '${req.params.name}' but it could not be found.`,\n });\n const result = await tool(req.params.arguments);\n return { content: [{ type: 'text', text: JSON.stringify(result) }] };\n }\n\n async listPrompts(req: ListPromptsRequest): Promise<ListPromptsResult> {\n await this.setup();\n return {\n prompts: this.promptActions.map((p): Prompt => {\n return {\n name: p.__action.name,\n description: p.__action.description,\n arguments: toMcpPromptArguments(p),\n };\n }),\n };\n }\n\n async getPrompt(req: GetPromptRequest): Promise<GetPromptResult> {\n await this.setup();\n const prompt = this.promptActions.find(\n (p) => p.__action.name === req.params.name\n );\n if (!prompt)\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `[@genkit-ai/mcp] Tried to call prompt '${req.params.name}' but it could not be found.`,\n });\n const result = await prompt(req.params.arguments);\n return {\n description: prompt.__action.description,\n messages: result.messages.map(toMcpPromptMessage),\n };\n }\n\n async start(transport?: Transport) {\n if (!transport) {\n const { StdioServerTransport } = await import(\n '@modelcontextprotocol/sdk/server/stdio.js'\n );\n transport = new StdioServerTransport();\n }\n await this.setup();\n await this.server!.connect(transport);\n logger.info(\n `[@genkit-ai/mcp] MCP server '${this.options.name}' started successfully.`\n );\n }\n}\n\nfunction toMcpPromptArguments(\n p: PromptAction\n): Prompt['arguments'] | undefined {\n const jsonSchema = toJsonSchema({\n schema: p.__action.inputSchema,\n jsonSchema: p.__action.inputJsonSchema,\n });\n\n if (!jsonSchema) return undefined;\n if (!jsonSchema.properties)\n throw new GenkitError({\n status: 'FAILED_PRECONDITION',\n message:\n '[@genkit-ai/mcp] MCP prompts must take objects as input schema.',\n });\n\n const args: Prompt['arguments'] = [];\n for (const k in jsonSchema.properties) {\n const { type, description } = jsonSchema.properties[k];\n if (\n type !== 'string' &&\n (!Array.isArray(type) || !type.includes('string'))\n ) {\n throw new GenkitError({\n status: 'FAILED_PRECONDITION',\n message: `[@genkit-ai/mcp] MCP prompts may only take string arguments, but ${p.__action.name} has property '${k}' of type '${type}'.`,\n });\n }\n args.push({\n name: k,\n description,\n required: jsonSchema.required?.includes(k),\n });\n }\n return args;\n}\n\nconst ROLE_MAP = { model: 'assistant', user: 'user' } as const;\n\nfunction toMcpPromptMessage(messageData: MessageData): PromptMessage {\n if (messageData.role !== 'model' && messageData.role !== 'user') {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[@genkit-ai/mcp] MCP prompt messages do not support role '${messageData.role}'. Only 'user' and 'model' messages are supported.`,\n });\n }\n const message = new Message(messageData);\n const common = { role: ROLE_MAP[messageData.role] };\n if (message.media) {\n const { url, contentType } = message.media;\n if (!url.startsWith('data:'))\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[@genkit-ai/mcp] MCP prompt messages only support base64 data images.`,\n });\n const mimeType =\n contentType || url.substring(url.indexOf(':')! + 1, url.indexOf(';'));\n const data = url.substring(url.indexOf(',') + 1);\n return { ...common, content: { type: 'image', mimeType, data } };\n } else {\n return { ...common, content: { type: 'text', text: message.text } };\n }\n}\n"],"mappings":"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","names":[]}