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.

220 lines 7.55 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var server_exports = {}; __export(server_exports, { GenkitMcpServer: () => GenkitMcpServer }); module.exports = __toCommonJS(server_exports); var import_genkit = require("genkit"); var import_schema = require("@genkit-ai/core/schema"); var import_logging = require("genkit/logging"); var import_tool = require("genkit/tool"); class GenkitMcpServer { ai; options; server; actionsResolved = false; toolActions = []; promptActions = []; constructor(ai, options) { this.ai = ai; this.options = options; this.setup(); } async setup() { if (this.actionsResolved) return; const { Server } = await import("@modelcontextprotocol/sdk/server/index.js"); this.server = new Server( { name: this.options.name, version: this.options.version || "1.0.0" }, { capabilities: { prompts: {}, tools: {} } } ); const { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema } = await import("@modelcontextprotocol/sdk/types.js"); this.server.setRequestHandler( ListToolsRequestSchema, this.listTools.bind(this) ); this.server.setRequestHandler( CallToolRequestSchema, this.callTool.bind(this) ); this.server.setRequestHandler( ListPromptsRequestSchema, this.listPrompts.bind(this) ); this.server.setRequestHandler( GetPromptRequestSchema, this.getPrompt.bind(this) ); const allActions = await this.ai.registry.listActions(); const toolList = []; const promptList = []; for (const k in allActions) { if (k.startsWith("/tool/")) { toolList.push(allActions[k]); } else if (k.startsWith("/prompt/")) { promptList.push(allActions[k]); } } this.toolActions = toolList; this.promptActions = promptList; this.actionsResolved = true; } async listTools(req) { await this.setup(); return { tools: this.toolActions.map((t) => { const def = (0, import_tool.toToolDefinition)(t); return { name: def.name, inputSchema: def.inputSchema || { type: "object" }, description: def.description }; }) }; } async callTool(req) { await this.setup(); const tool = this.toolActions.find( (t) => t.__action.name === req.params.name ); if (!tool) throw new import_genkit.GenkitError({ status: "NOT_FOUND", message: `Tried to call tool '${req.params.name}' but it could not be found.` }); const result = await tool(req.params.arguments); return { content: [{ type: "text", text: JSON.stringify(result) }] }; } async listPrompts(req) { await this.setup(); return { prompts: this.promptActions.map((p) => { return { name: p.__action.name, description: p.__action.description, arguments: toMcpPromptArguments(p) }; }) }; } async getPrompt(req) { await this.setup(); const prompt = this.promptActions.find( (p) => p.__action.name === req.params.name ); if (!prompt) throw new import_genkit.GenkitError({ status: "NOT_FOUND", message: `[@genkit-ai/mcp] Tried to call prompt '${req.params.name}' but it could not be found.` }); const result = await prompt(req.params.arguments); return { description: prompt.__action.description, messages: result.messages.map(toMcpPromptMessage) }; } async start(transport) { if (!transport) { const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js"); transport = new StdioServerTransport(); } await this.setup(); await this.server.connect(transport); import_logging.logger.info( `[@genkit-ai/mcp] MCP server '${this.options.name}' started successfully.` ); } } function toMcpPromptArguments(p) { const jsonSchema = (0, import_schema.toJsonSchema)({ schema: p.__action.inputSchema, jsonSchema: p.__action.inputJsonSchema }); if (!jsonSchema) return void 0; if (!jsonSchema.properties) throw new import_genkit.GenkitError({ status: "FAILED_PRECONDITION", message: "[@genkit-ai/mcp] MCP prompts must take objects as input schema." }); const args = []; for (const k in jsonSchema.properties) { const { type, description } = jsonSchema.properties[k]; if (type !== "string" && (!Array.isArray(type) || !type.includes("string"))) { throw new import_genkit.GenkitError({ status: "FAILED_PRECONDITION", message: `[@genkit-ai/mcp] MCP prompts may only take string arguments, but ${p.__action.name} has property '${k}' of type '${type}'.` }); } args.push({ name: k, description, required: jsonSchema.required?.includes(k) }); } return args; } const ROLE_MAP = { model: "assistant", user: "user" }; function toMcpPromptMessage(messageData) { if (messageData.role !== "model" && messageData.role !== "user") { throw new import_genkit.GenkitError({ status: "UNIMPLEMENTED", message: `[@genkit-ai/mcp] MCP prompt messages do not support role '${messageData.role}'. Only 'user' and 'model' messages are supported.` }); } const message = new import_genkit.Message(messageData); const common = { role: ROLE_MAP[messageData.role] }; if (message.media) { const { url, contentType } = message.media; if (!url.startsWith("data:")) throw new import_genkit.GenkitError({ status: "UNIMPLEMENTED", message: `[@genkit-ai/mcp] MCP prompt messages only support base64 data images.` }); const mimeType = contentType || url.substring(url.indexOf(":") + 1, url.indexOf(";")); const data = url.substring(url.indexOf(",") + 1); return { ...common, content: { type: "image", mimeType, data } }; } else { return { ...common, content: { type: "text", text: message.text } }; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { GenkitMcpServer }); //# sourceMappingURL=server.js.map