@genkit-ai/mcp 1.20.0 → 1.22.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/server.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Genkit, PromptAction, ResourceAction } from 'genkit';
2
- import { McpClientOptions, GenkitMcpClient } from './client/client.mjs';
3
- import { McpHostOptions, GenkitMcpHost } from './client/host.mjs';
2
+ import { McpClientOptions, GenkitMcpClient, McpClientOptionsWithCache } from './client/client.mjs';
3
+ import { McpHostOptions, GenkitMcpHost, McpHostOptionsWithCache } from './client/host.mjs';
4
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
5
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
6
6
  import { ListToolsRequest, ListToolsResult, CallToolRequest, CallToolResult, ListPromptsRequest, ListPromptsResult, ListResourcesRequest, ListResourcesResult, ListResourceTemplatesRequest, ListResourceTemplatesResult, ReadResourceRequest, ReadResourceResult, GetPromptRequest, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
@@ -169,6 +169,30 @@ interface McpServerOptions {
169
169
  * @returns A new instance of GenkitMcpHost.
170
170
  */
171
171
  declare function createMcpHost(options: McpHostOptions): GenkitMcpHost;
172
+ /**
173
+ * Creates an MCP Client Host that connects to one or more MCP servers.
174
+ * Each server is defined in the `mcpClients` option, where the key is a
175
+ * client-side name for the server and the value is the server's configuration.
176
+ *
177
+ * By default, all servers in the config will be attempted to connect unless
178
+ * their configuration includes `{disabled: true}`.
179
+ *
180
+ * ```ts
181
+ * const clientHost = defineMcpHost(ai, {
182
+ * name: "my-mcp-client-host", // Name for the host itself
183
+ * mcpServers: {
184
+ * // Each key is a name for this client/server configuration
185
+ * // Each value is an McpServerConfig object
186
+ * gitToolServer: { command: "uvx", args: ["mcp-server-git"] },
187
+ * customApiServer: { url: "http://localhost:1234/mcp" }
188
+ * }
189
+ * });
190
+ * ```
191
+ *
192
+ * @param options Configuration for the MCP Client Host, including the definitions of MCP servers to connect to.
193
+ * @returns A new instance of GenkitMcpHost.
194
+ */
195
+ declare function defineMcpHost(ai: Genkit, options: McpHostOptionsWithCache): GenkitMcpHost;
172
196
  /**
173
197
  * Creates an MCP Client that connects to a single MCP server.
174
198
  * This is useful when you only need to interact with one MCP server,
@@ -190,6 +214,33 @@ declare function createMcpHost(options: McpHostOptions): GenkitMcpHost;
190
214
  * @returns A new instance of GenkitMcpClient.
191
215
  */
192
216
  declare function createMcpClient(options: McpClientOptions): GenkitMcpClient;
217
+ /**
218
+ * Defines an MCP Client that connects to a single MCP server.
219
+ * This is useful when you only need to interact with one MCP server,
220
+ * or if you want to manage client instances individually.
221
+ *
222
+ * ```ts
223
+ * const client = defineMcpClient(ai, {
224
+ * name: "mySingleMcpClient", // A name for this client instance
225
+ * command: "npx", // Example: Launching a local server
226
+ * args: ["-y", "@modelcontextprotocol/server-everything", "/path/to/allowed/dir"],
227
+ * });
228
+ *
229
+ * // To get tools from this client:
230
+ * // const tools = await client.getActiveTools(ai);
231
+ *
232
+ * // Or in a generate call you can use:
233
+ * ai.generate({
234
+ prompt: `<a prompt requiring tools>`,
235
+ tools: ['mySingleMcpClient:tool/*'],
236
+ });
237
+ * ```
238
+ *
239
+ * @param options Configuration for the MCP Client, defining how it connects
240
+ * to the MCP server and its behavior.
241
+ * @returns A new instance of GenkitMcpClient.
242
+ */
243
+ declare function defineMcpClient(ai: Genkit, options: McpClientOptionsWithCache): GenkitMcpClient;
193
244
  /**
194
245
  * Creates an MCP server based on the supplied Genkit instance. All tools and prompts
195
246
  * will be automatically converted to MCP compatibility.
@@ -207,4 +258,4 @@ declare function createMcpClient(options: McpClientOptions): GenkitMcpClient;
207
258
  */
208
259
  declare function createMcpServer(ai: Genkit, options: McpServerOptions): GenkitMcpServer;
209
260
 
210
- export { GenkitMcpServer, type McpServerOptions as M, createMcpClient as a, createMcpServer as b, createMcpHost as c };
261
+ export { GenkitMcpServer, type McpServerOptions as M, createMcpClient as a, defineMcpClient as b, createMcpHost as c, defineMcpHost as d, createMcpServer as e };
package/lib/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Genkit, PromptAction, ResourceAction } from 'genkit';
2
- import { McpClientOptions, GenkitMcpClient } from './client/client.js';
3
- import { McpHostOptions, GenkitMcpHost } from './client/host.js';
2
+ import { McpClientOptions, GenkitMcpClient, McpClientOptionsWithCache } from './client/client.js';
3
+ import { McpHostOptions, GenkitMcpHost, McpHostOptionsWithCache } from './client/host.js';
4
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
5
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
6
6
  import { ListToolsRequest, ListToolsResult, CallToolRequest, CallToolResult, ListPromptsRequest, ListPromptsResult, ListResourcesRequest, ListResourcesResult, ListResourceTemplatesRequest, ListResourceTemplatesResult, ReadResourceRequest, ReadResourceResult, GetPromptRequest, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
@@ -169,6 +169,30 @@ interface McpServerOptions {
169
169
  * @returns A new instance of GenkitMcpHost.
170
170
  */
171
171
  declare function createMcpHost(options: McpHostOptions): GenkitMcpHost;
172
+ /**
173
+ * Creates an MCP Client Host that connects to one or more MCP servers.
174
+ * Each server is defined in the `mcpClients` option, where the key is a
175
+ * client-side name for the server and the value is the server's configuration.
176
+ *
177
+ * By default, all servers in the config will be attempted to connect unless
178
+ * their configuration includes `{disabled: true}`.
179
+ *
180
+ * ```ts
181
+ * const clientHost = defineMcpHost(ai, {
182
+ * name: "my-mcp-client-host", // Name for the host itself
183
+ * mcpServers: {
184
+ * // Each key is a name for this client/server configuration
185
+ * // Each value is an McpServerConfig object
186
+ * gitToolServer: { command: "uvx", args: ["mcp-server-git"] },
187
+ * customApiServer: { url: "http://localhost:1234/mcp" }
188
+ * }
189
+ * });
190
+ * ```
191
+ *
192
+ * @param options Configuration for the MCP Client Host, including the definitions of MCP servers to connect to.
193
+ * @returns A new instance of GenkitMcpHost.
194
+ */
195
+ declare function defineMcpHost(ai: Genkit, options: McpHostOptionsWithCache): GenkitMcpHost;
172
196
  /**
173
197
  * Creates an MCP Client that connects to a single MCP server.
174
198
  * This is useful when you only need to interact with one MCP server,
@@ -190,6 +214,33 @@ declare function createMcpHost(options: McpHostOptions): GenkitMcpHost;
190
214
  * @returns A new instance of GenkitMcpClient.
191
215
  */
192
216
  declare function createMcpClient(options: McpClientOptions): GenkitMcpClient;
217
+ /**
218
+ * Defines an MCP Client that connects to a single MCP server.
219
+ * This is useful when you only need to interact with one MCP server,
220
+ * or if you want to manage client instances individually.
221
+ *
222
+ * ```ts
223
+ * const client = defineMcpClient(ai, {
224
+ * name: "mySingleMcpClient", // A name for this client instance
225
+ * command: "npx", // Example: Launching a local server
226
+ * args: ["-y", "@modelcontextprotocol/server-everything", "/path/to/allowed/dir"],
227
+ * });
228
+ *
229
+ * // To get tools from this client:
230
+ * // const tools = await client.getActiveTools(ai);
231
+ *
232
+ * // Or in a generate call you can use:
233
+ * ai.generate({
234
+ prompt: `<a prompt requiring tools>`,
235
+ tools: ['mySingleMcpClient:tool/*'],
236
+ });
237
+ * ```
238
+ *
239
+ * @param options Configuration for the MCP Client, defining how it connects
240
+ * to the MCP server and its behavior.
241
+ * @returns A new instance of GenkitMcpClient.
242
+ */
243
+ declare function defineMcpClient(ai: Genkit, options: McpClientOptionsWithCache): GenkitMcpClient;
193
244
  /**
194
245
  * Creates an MCP server based on the supplied Genkit instance. All tools and prompts
195
246
  * will be automatically converted to MCP compatibility.
@@ -207,4 +258,4 @@ declare function createMcpClient(options: McpClientOptions): GenkitMcpClient;
207
258
  */
208
259
  declare function createMcpServer(ai: Genkit, options: McpServerOptions): GenkitMcpServer;
209
260
 
210
- export { GenkitMcpServer, type McpServerOptions as M, createMcpClient as a, createMcpServer as b, createMcpHost as c };
261
+ export { GenkitMcpServer, type McpServerOptions as M, createMcpClient as a, defineMcpClient as b, createMcpHost as c, defineMcpHost as d, createMcpServer as e };
package/lib/server.js CHANGED
@@ -361,7 +361,7 @@ function toMcpResourceMessage(uri, content) {
361
361
  if (!url.startsWith("data:"))
362
362
  throw new import_genkit.GenkitError({
363
363
  status: "UNIMPLEMENTED",
364
- message: `[MCP Server] MCP prompt messages only support base64 data images.`
364
+ message: `[MCP Server] MCP resource messages only support base64 data images.`
365
365
  });
366
366
  const mimeType = contentType || url.substring(url.indexOf(":") + 1, url.indexOf(";"));
367
367
  const data = url.substring(url.indexOf(",") + 1);
@@ -371,7 +371,7 @@ function toMcpResourceMessage(uri, content) {
371
371
  } else {
372
372
  throw new import_genkit.GenkitError({
373
373
  status: "UNIMPLEMENTED",
374
- message: `[MCP Server] MCP prompt messages only support media and text parts.`
374
+ message: `[MCP Server] MCP resource messages only support media and text parts.`
375
375
  });
376
376
  }
377
377
  });
package/lib/server.js.map CHANGED
@@ -1 +1 @@
1
- {"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 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 ListResourceTemplatesResult,\n ListToolsRequest,\n ListToolsResult,\n Prompt,\n PromptMessage,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n ListResourceTemplatesRequest,\n ListResourcesRequest,\n ListResourcesResult,\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n Message,\n type Genkit,\n type MessageData,\n type Part,\n type PromptAction,\n type ResourceAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { toJsonSchema } from 'genkit/schema';\nimport { toToolDefinition, type ToolAction } from 'genkit/tool';\nimport type { McpServerOptions } from './index.js';\n\n/**\n * Represents an MCP (Model Context Protocol) server that exposes Genkit tools\n * and prompts. This class wraps a Genkit instance and makes its registered\n * actions (tools, prompts) available to MCP clients. It handles the translation\n * between Genkit's action definitions and MCP's expected formats.\n */\nexport class GenkitMcpServer {\n ai: Genkit;\n options: McpServerOptions;\n server?: Server;\n actionsResolved = false;\n toolActions: ToolAction[] = [];\n promptActions: PromptAction[] = [];\n resourceActions: ResourceAction[] = [];\n\n /**\n * Creates an instance of GenkitMcpServer.\n * @param ai The Genkit instance whose actions will be exposed.\n * @param options Configuration options for the MCP server, like its name and version.\n */\n constructor(ai: Genkit, options: McpServerOptions) {\n this.ai = ai;\n this.options = options;\n }\n\n /**\n * Initializes the MCP server instance and registers request handlers. It\n * dynamically imports MCP SDK components and sets up handlers for listing\n * tools, calling tools, listing prompts, and getting prompts. It also\n * resolves and stores all tool and prompt actions from the Genkit instance.\n *\n * This method is called by the constructor and ensures the server is ready\n * before any requests are handled. It's idempotent.\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 resources: {},\n },\n }\n );\n\n const {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\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 ListResourcesRequestSchema,\n this.listResources.bind(this)\n );\n this.server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n this.listResourceTemplates.bind(this)\n );\n this.server.setRequestHandler(\n ReadResourceRequestSchema,\n this.readResource.bind(this)\n );\n this.server.setRequestHandler(\n GetPromptRequestSchema,\n this.getPrompt.bind(this)\n );\n\n // TODO -- use listResolvableActions.\n const allActions = await this.ai.registry.listActions();\n const toolList: ToolAction[] = [];\n const promptList: PromptAction[] = [];\n const resourceList: ResourceAction[] = [];\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 } else if (k.startsWith('/resource/')) {\n resourceList.push(allActions[k] as ResourceAction);\n }\n }\n this.toolActions = toolList;\n this.promptActions = promptList;\n this.resourceActions = resourceList;\n this.actionsResolved = true;\n }\n\n /**\n * Handles MCP requests to list available tools.\n * It maps the resolved Genkit tool actions to the MCP Tool format.\n * @param req The MCP ListToolsRequest.\n * @returns A Promise resolving to an MCP ListToolsResult.\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 _meta: t.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to call a specific tool. It finds the corresponding\n * Genkit tool action and executes it with the provided arguments. The result\n * is then formatted as an MCP CallToolResult.\n * @param req The MCP CallToolRequest containing the tool name and arguments.\n * @returns A Promise resolving to an MCP CallToolResult.\n * @throws GenkitError if the requested tool is not found.\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 {\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n };\n }\n\n /**\n * Handles MCP requests to list available prompts.\n * It maps the resolved Genkit prompt actions to the MCP Prompt format,\n * including converting input schemas to MCP argument definitions.\n * @param req The MCP ListPromptsRequest.\n * @returns A Promise resolving to an MCP ListPromptsResult.\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 _meta: p.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResources(req: ListResourcesRequest): Promise<ListResourcesResult> {\n await this.setup();\n return {\n resources: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.uri)\n .map((r): Resource => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uri: r.__action.metadata?.resource.uri,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResourceTemplates(\n req: ListResourceTemplatesRequest\n ): Promise<ListResourceTemplatesResult> {\n await this.setup();\n return {\n resourceTemplates: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.template)\n .map((r): ResourceTemplate => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uriTemplate: r.__action.metadata?.resource.template,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async readResource(req: ReadResourceRequest): Promise<ReadResourceResult> {\n await this.setup();\n const resource = this.resourceActions.find((r) =>\n r.matches({ uri: req.params.uri })\n );\n if (!resource) {\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `Tried to call resource '${req.params.uri}' but it could not be found.`,\n });\n }\n const result = await resource({ uri: req.params.uri });\n return {\n contents: toMcpResourceMessage(req.params.uri, result.content),\n };\n }\n\n /**\n * Handles MCP requests to get (render) a specific prompt. It finds the\n * corresponding Genkit prompt action, executes it with the provided\n * arguments, and then formats the resulting messages into the MCP\n * PromptMessage format.\n * @param req The MCP GetPromptRequest containing the prompt name and\n * arguments.\n * @returns A Promise resolving to an MCP GetPromptResult.\n * @throws GenkitError if the requested prompt is not found.\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: `[MCP Server] 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 /**\n * Starts the MCP server with the specified transport or a default\n * StdioServerTransport. Ensures the server is set up before connecting the\n * transport.\n * @param transport Optional MCP transport instance. If not provided, a\n * StdioServerTransport will be created and used.\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.debug(\n `[MCP Server] MCP server '${this.options.name}' started successfully.`\n );\n }\n}\n\n/**\n * Converts a Genkit prompt's input schema to an array of MCP prompt arguments.\n * MCP prompts currently only support string arguments.\n * @param p The Genkit PromptAction.\n * @returns An array of MCP prompt arguments, or undefined if the schema is not defined.\n * @throws GenkitError if the input schema is not an object or if any property is not a string.\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: '[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit MessageData object to an MCP PromptMessage.\n * Handles mapping of roles and content types (text, image).\n * MCP only supports 'user' and 'assistant' (model) roles and base64 data images.\n * @param messageData The Genkit MessageData object.\n * @returns An MCP PromptMessage.\n * @throws GenkitError if the role is unsupported or if media is not a data URL.\n */\nfunction toMcpPromptMessage(messageData: MessageData): PromptMessage {\n if (messageData.role !== 'model' && messageData.role !== 'user') {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit Parts to an MCP resource content.\n * Handles mapping of roles and content types (text, image).\n */\nfunction toMcpResourceMessage(\n uri: string,\n content: Part[]\n): ReadResourceResult['contents'] {\n return content.map((p) => {\n if (p.media) {\n const { url, contentType } = p.media;\n if (!url.startsWith('data:'))\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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 { uri, mimeType, blob: data };\n } else if (p.text) {\n return { uri, text: p.text };\n } else {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP prompt messages only support media and text parts.`,\n });\n }\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"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 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 ListResourceTemplatesResult,\n ListToolsRequest,\n ListToolsResult,\n Prompt,\n PromptMessage,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n ListResourceTemplatesRequest,\n ListResourcesRequest,\n ListResourcesResult,\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n Message,\n type Genkit,\n type MessageData,\n type Part,\n type PromptAction,\n type ResourceAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { toJsonSchema } from 'genkit/schema';\nimport { toToolDefinition, type ToolAction } from 'genkit/tool';\nimport type { McpServerOptions } from './index.js';\n\n/**\n * Represents an MCP (Model Context Protocol) server that exposes Genkit tools\n * and prompts. This class wraps a Genkit instance and makes its registered\n * actions (tools, prompts) available to MCP clients. It handles the translation\n * between Genkit's action definitions and MCP's expected formats.\n */\nexport class GenkitMcpServer {\n ai: Genkit;\n options: McpServerOptions;\n server?: Server;\n actionsResolved = false;\n toolActions: ToolAction[] = [];\n promptActions: PromptAction[] = [];\n resourceActions: ResourceAction[] = [];\n\n /**\n * Creates an instance of GenkitMcpServer.\n * @param ai The Genkit instance whose actions will be exposed.\n * @param options Configuration options for the MCP server, like its name and version.\n */\n constructor(ai: Genkit, options: McpServerOptions) {\n this.ai = ai;\n this.options = options;\n }\n\n /**\n * Initializes the MCP server instance and registers request handlers. It\n * dynamically imports MCP SDK components and sets up handlers for listing\n * tools, calling tools, listing prompts, and getting prompts. It also\n * resolves and stores all tool and prompt actions from the Genkit instance.\n *\n * This method is called by the constructor and ensures the server is ready\n * before any requests are handled. It's idempotent.\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 resources: {},\n },\n }\n );\n\n const {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\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 ListResourcesRequestSchema,\n this.listResources.bind(this)\n );\n this.server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n this.listResourceTemplates.bind(this)\n );\n this.server.setRequestHandler(\n ReadResourceRequestSchema,\n this.readResource.bind(this)\n );\n this.server.setRequestHandler(\n GetPromptRequestSchema,\n this.getPrompt.bind(this)\n );\n\n // TODO -- use listResolvableActions.\n const allActions = await this.ai.registry.listActions();\n const toolList: ToolAction[] = [];\n const promptList: PromptAction[] = [];\n const resourceList: ResourceAction[] = [];\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 } else if (k.startsWith('/resource/')) {\n resourceList.push(allActions[k] as ResourceAction);\n }\n }\n this.toolActions = toolList;\n this.promptActions = promptList;\n this.resourceActions = resourceList;\n this.actionsResolved = true;\n }\n\n /**\n * Handles MCP requests to list available tools.\n * It maps the resolved Genkit tool actions to the MCP Tool format.\n * @param req The MCP ListToolsRequest.\n * @returns A Promise resolving to an MCP ListToolsResult.\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 _meta: t.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to call a specific tool. It finds the corresponding\n * Genkit tool action and executes it with the provided arguments. The result\n * is then formatted as an MCP CallToolResult.\n * @param req The MCP CallToolRequest containing the tool name and arguments.\n * @returns A Promise resolving to an MCP CallToolResult.\n * @throws GenkitError if the requested tool is not found.\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 {\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n };\n }\n\n /**\n * Handles MCP requests to list available prompts.\n * It maps the resolved Genkit prompt actions to the MCP Prompt format,\n * including converting input schemas to MCP argument definitions.\n * @param req The MCP ListPromptsRequest.\n * @returns A Promise resolving to an MCP ListPromptsResult.\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 _meta: p.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResources(req: ListResourcesRequest): Promise<ListResourcesResult> {\n await this.setup();\n return {\n resources: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.uri)\n .map((r): Resource => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uri: r.__action.metadata?.resource.uri,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResourceTemplates(\n req: ListResourceTemplatesRequest\n ): Promise<ListResourceTemplatesResult> {\n await this.setup();\n return {\n resourceTemplates: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.template)\n .map((r): ResourceTemplate => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uriTemplate: r.__action.metadata?.resource.template,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async readResource(req: ReadResourceRequest): Promise<ReadResourceResult> {\n await this.setup();\n const resource = this.resourceActions.find((r) =>\n r.matches({ uri: req.params.uri })\n );\n if (!resource) {\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `Tried to call resource '${req.params.uri}' but it could not be found.`,\n });\n }\n const result = await resource({ uri: req.params.uri });\n return {\n contents: toMcpResourceMessage(req.params.uri, result.content),\n };\n }\n\n /**\n * Handles MCP requests to get (render) a specific prompt. It finds the\n * corresponding Genkit prompt action, executes it with the provided\n * arguments, and then formats the resulting messages into the MCP\n * PromptMessage format.\n * @param req The MCP GetPromptRequest containing the prompt name and\n * arguments.\n * @returns A Promise resolving to an MCP GetPromptResult.\n * @throws GenkitError if the requested prompt is not found.\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: `[MCP Server] 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 /**\n * Starts the MCP server with the specified transport or a default\n * StdioServerTransport. Ensures the server is set up before connecting the\n * transport.\n * @param transport Optional MCP transport instance. If not provided, a\n * StdioServerTransport will be created and used.\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.debug(\n `[MCP Server] MCP server '${this.options.name}' started successfully.`\n );\n }\n}\n\n/**\n * Converts a Genkit prompt's input schema to an array of MCP prompt arguments.\n * MCP prompts currently only support string arguments.\n * @param p The Genkit PromptAction.\n * @returns An array of MCP prompt arguments, or undefined if the schema is not defined.\n * @throws GenkitError if the input schema is not an object or if any property is not a string.\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: '[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit MessageData object to an MCP PromptMessage.\n * Handles mapping of roles and content types (text, image).\n * MCP only supports 'user' and 'assistant' (model) roles and base64 data images.\n * @param messageData The Genkit MessageData object.\n * @returns An MCP PromptMessage.\n * @throws GenkitError if the role is unsupported or if media is not a data URL.\n */\nfunction toMcpPromptMessage(messageData: MessageData): PromptMessage {\n if (messageData.role !== 'model' && messageData.role !== 'user') {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit Parts to an MCP resource content.\n * Handles mapping of roles and content types (text, image).\n */\nfunction toMcpResourceMessage(\n uri: string,\n content: Part[]\n): ReadResourceResult['contents'] {\n return content.map((p) => {\n if (p.media) {\n const { url, contentType } = p.media;\n if (!url.startsWith('data:'))\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP resource 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 { uri, mimeType, blob: data };\n } else if (p.text) {\n return { uri, text: p.text };\n } else {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP resource messages only support media and text parts.`,\n });\n }\n });\n}\n"],"mappings":"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","names":[]}
package/lib/server.mjs CHANGED
@@ -331,7 +331,7 @@ function toMcpResourceMessage(uri, content) {
331
331
  if (!url.startsWith("data:"))
332
332
  throw new GenkitError({
333
333
  status: "UNIMPLEMENTED",
334
- message: `[MCP Server] MCP prompt messages only support base64 data images.`
334
+ message: `[MCP Server] MCP resource messages only support base64 data images.`
335
335
  });
336
336
  const mimeType = contentType || url.substring(url.indexOf(":") + 1, url.indexOf(";"));
337
337
  const data = url.substring(url.indexOf(",") + 1);
@@ -341,7 +341,7 @@ function toMcpResourceMessage(uri, content) {
341
341
  } else {
342
342
  throw new GenkitError({
343
343
  status: "UNIMPLEMENTED",
344
- message: `[MCP Server] MCP prompt messages only support media and text parts.`
344
+ message: `[MCP Server] MCP resource messages only support media and text parts.`
345
345
  });
346
346
  }
347
347
  });
@@ -1 +1 @@
1
- {"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 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 ListResourceTemplatesResult,\n ListToolsRequest,\n ListToolsResult,\n Prompt,\n PromptMessage,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n ListResourceTemplatesRequest,\n ListResourcesRequest,\n ListResourcesResult,\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n Message,\n type Genkit,\n type MessageData,\n type Part,\n type PromptAction,\n type ResourceAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { toJsonSchema } from 'genkit/schema';\nimport { toToolDefinition, type ToolAction } from 'genkit/tool';\nimport type { McpServerOptions } from './index.js';\n\n/**\n * Represents an MCP (Model Context Protocol) server that exposes Genkit tools\n * and prompts. This class wraps a Genkit instance and makes its registered\n * actions (tools, prompts) available to MCP clients. It handles the translation\n * between Genkit's action definitions and MCP's expected formats.\n */\nexport class GenkitMcpServer {\n ai: Genkit;\n options: McpServerOptions;\n server?: Server;\n actionsResolved = false;\n toolActions: ToolAction[] = [];\n promptActions: PromptAction[] = [];\n resourceActions: ResourceAction[] = [];\n\n /**\n * Creates an instance of GenkitMcpServer.\n * @param ai The Genkit instance whose actions will be exposed.\n * @param options Configuration options for the MCP server, like its name and version.\n */\n constructor(ai: Genkit, options: McpServerOptions) {\n this.ai = ai;\n this.options = options;\n }\n\n /**\n * Initializes the MCP server instance and registers request handlers. It\n * dynamically imports MCP SDK components and sets up handlers for listing\n * tools, calling tools, listing prompts, and getting prompts. It also\n * resolves and stores all tool and prompt actions from the Genkit instance.\n *\n * This method is called by the constructor and ensures the server is ready\n * before any requests are handled. It's idempotent.\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 resources: {},\n },\n }\n );\n\n const {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\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 ListResourcesRequestSchema,\n this.listResources.bind(this)\n );\n this.server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n this.listResourceTemplates.bind(this)\n );\n this.server.setRequestHandler(\n ReadResourceRequestSchema,\n this.readResource.bind(this)\n );\n this.server.setRequestHandler(\n GetPromptRequestSchema,\n this.getPrompt.bind(this)\n );\n\n // TODO -- use listResolvableActions.\n const allActions = await this.ai.registry.listActions();\n const toolList: ToolAction[] = [];\n const promptList: PromptAction[] = [];\n const resourceList: ResourceAction[] = [];\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 } else if (k.startsWith('/resource/')) {\n resourceList.push(allActions[k] as ResourceAction);\n }\n }\n this.toolActions = toolList;\n this.promptActions = promptList;\n this.resourceActions = resourceList;\n this.actionsResolved = true;\n }\n\n /**\n * Handles MCP requests to list available tools.\n * It maps the resolved Genkit tool actions to the MCP Tool format.\n * @param req The MCP ListToolsRequest.\n * @returns A Promise resolving to an MCP ListToolsResult.\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 _meta: t.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to call a specific tool. It finds the corresponding\n * Genkit tool action and executes it with the provided arguments. The result\n * is then formatted as an MCP CallToolResult.\n * @param req The MCP CallToolRequest containing the tool name and arguments.\n * @returns A Promise resolving to an MCP CallToolResult.\n * @throws GenkitError if the requested tool is not found.\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 {\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n };\n }\n\n /**\n * Handles MCP requests to list available prompts.\n * It maps the resolved Genkit prompt actions to the MCP Prompt format,\n * including converting input schemas to MCP argument definitions.\n * @param req The MCP ListPromptsRequest.\n * @returns A Promise resolving to an MCP ListPromptsResult.\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 _meta: p.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResources(req: ListResourcesRequest): Promise<ListResourcesResult> {\n await this.setup();\n return {\n resources: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.uri)\n .map((r): Resource => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uri: r.__action.metadata?.resource.uri,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResourceTemplates(\n req: ListResourceTemplatesRequest\n ): Promise<ListResourceTemplatesResult> {\n await this.setup();\n return {\n resourceTemplates: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.template)\n .map((r): ResourceTemplate => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uriTemplate: r.__action.metadata?.resource.template,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async readResource(req: ReadResourceRequest): Promise<ReadResourceResult> {\n await this.setup();\n const resource = this.resourceActions.find((r) =>\n r.matches({ uri: req.params.uri })\n );\n if (!resource) {\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `Tried to call resource '${req.params.uri}' but it could not be found.`,\n });\n }\n const result = await resource({ uri: req.params.uri });\n return {\n contents: toMcpResourceMessage(req.params.uri, result.content),\n };\n }\n\n /**\n * Handles MCP requests to get (render) a specific prompt. It finds the\n * corresponding Genkit prompt action, executes it with the provided\n * arguments, and then formats the resulting messages into the MCP\n * PromptMessage format.\n * @param req The MCP GetPromptRequest containing the prompt name and\n * arguments.\n * @returns A Promise resolving to an MCP GetPromptResult.\n * @throws GenkitError if the requested prompt is not found.\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: `[MCP Server] 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 /**\n * Starts the MCP server with the specified transport or a default\n * StdioServerTransport. Ensures the server is set up before connecting the\n * transport.\n * @param transport Optional MCP transport instance. If not provided, a\n * StdioServerTransport will be created and used.\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.debug(\n `[MCP Server] MCP server '${this.options.name}' started successfully.`\n );\n }\n}\n\n/**\n * Converts a Genkit prompt's input schema to an array of MCP prompt arguments.\n * MCP prompts currently only support string arguments.\n * @param p The Genkit PromptAction.\n * @returns An array of MCP prompt arguments, or undefined if the schema is not defined.\n * @throws GenkitError if the input schema is not an object or if any property is not a string.\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: '[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit MessageData object to an MCP PromptMessage.\n * Handles mapping of roles and content types (text, image).\n * MCP only supports 'user' and 'assistant' (model) roles and base64 data images.\n * @param messageData The Genkit MessageData object.\n * @returns An MCP PromptMessage.\n * @throws GenkitError if the role is unsupported or if media is not a data URL.\n */\nfunction toMcpPromptMessage(messageData: MessageData): PromptMessage {\n if (messageData.role !== 'model' && messageData.role !== 'user') {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit Parts to an MCP resource content.\n * Handles mapping of roles and content types (text, image).\n */\nfunction toMcpResourceMessage(\n uri: string,\n content: Part[]\n): ReadResourceResult['contents'] {\n return content.map((p) => {\n if (p.media) {\n const { url, contentType } = p.media;\n if (!url.startsWith('data:'))\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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 { uri, mimeType, blob: data };\n } else if (p.text) {\n return { uri, text: p.text };\n } else {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP prompt messages only support media and text parts.`,\n });\n }\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"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 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 ListResourceTemplatesResult,\n ListToolsRequest,\n ListToolsResult,\n Prompt,\n PromptMessage,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n ListResourceTemplatesRequest,\n ListResourcesRequest,\n ListResourcesResult,\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n Message,\n type Genkit,\n type MessageData,\n type Part,\n type PromptAction,\n type ResourceAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport { toJsonSchema } from 'genkit/schema';\nimport { toToolDefinition, type ToolAction } from 'genkit/tool';\nimport type { McpServerOptions } from './index.js';\n\n/**\n * Represents an MCP (Model Context Protocol) server that exposes Genkit tools\n * and prompts. This class wraps a Genkit instance and makes its registered\n * actions (tools, prompts) available to MCP clients. It handles the translation\n * between Genkit's action definitions and MCP's expected formats.\n */\nexport class GenkitMcpServer {\n ai: Genkit;\n options: McpServerOptions;\n server?: Server;\n actionsResolved = false;\n toolActions: ToolAction[] = [];\n promptActions: PromptAction[] = [];\n resourceActions: ResourceAction[] = [];\n\n /**\n * Creates an instance of GenkitMcpServer.\n * @param ai The Genkit instance whose actions will be exposed.\n * @param options Configuration options for the MCP server, like its name and version.\n */\n constructor(ai: Genkit, options: McpServerOptions) {\n this.ai = ai;\n this.options = options;\n }\n\n /**\n * Initializes the MCP server instance and registers request handlers. It\n * dynamically imports MCP SDK components and sets up handlers for listing\n * tools, calling tools, listing prompts, and getting prompts. It also\n * resolves and stores all tool and prompt actions from the Genkit instance.\n *\n * This method is called by the constructor and ensures the server is ready\n * before any requests are handled. It's idempotent.\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 resources: {},\n },\n }\n );\n\n const {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\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 ListResourcesRequestSchema,\n this.listResources.bind(this)\n );\n this.server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n this.listResourceTemplates.bind(this)\n );\n this.server.setRequestHandler(\n ReadResourceRequestSchema,\n this.readResource.bind(this)\n );\n this.server.setRequestHandler(\n GetPromptRequestSchema,\n this.getPrompt.bind(this)\n );\n\n // TODO -- use listResolvableActions.\n const allActions = await this.ai.registry.listActions();\n const toolList: ToolAction[] = [];\n const promptList: PromptAction[] = [];\n const resourceList: ResourceAction[] = [];\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 } else if (k.startsWith('/resource/')) {\n resourceList.push(allActions[k] as ResourceAction);\n }\n }\n this.toolActions = toolList;\n this.promptActions = promptList;\n this.resourceActions = resourceList;\n this.actionsResolved = true;\n }\n\n /**\n * Handles MCP requests to list available tools.\n * It maps the resolved Genkit tool actions to the MCP Tool format.\n * @param req The MCP ListToolsRequest.\n * @returns A Promise resolving to an MCP ListToolsResult.\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 _meta: t.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to call a specific tool. It finds the corresponding\n * Genkit tool action and executes it with the provided arguments. The result\n * is then formatted as an MCP CallToolResult.\n * @param req The MCP CallToolRequest containing the tool name and arguments.\n * @returns A Promise resolving to an MCP CallToolResult.\n * @throws GenkitError if the requested tool is not found.\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 {\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result),\n },\n ],\n };\n }\n\n /**\n * Handles MCP requests to list available prompts.\n * It maps the resolved Genkit prompt actions to the MCP Prompt format,\n * including converting input schemas to MCP argument definitions.\n * @param req The MCP ListPromptsRequest.\n * @returns A Promise resolving to an MCP ListPromptsResult.\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 _meta: p.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResources(req: ListResourcesRequest): Promise<ListResourcesResult> {\n await this.setup();\n return {\n resources: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.uri)\n .map((r): Resource => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uri: r.__action.metadata?.resource.uri,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async listResourceTemplates(\n req: ListResourceTemplatesRequest\n ): Promise<ListResourceTemplatesResult> {\n await this.setup();\n return {\n resourceTemplates: this.resourceActions\n .filter((r) => r.__action.metadata?.resource.template)\n .map((r): ResourceTemplate => {\n return {\n name: r.__action.name,\n description: r.__action.description,\n uriTemplate: r.__action.metadata?.resource.template,\n _meta: r.__action.metadata?.mcp?._meta,\n };\n }),\n };\n }\n\n /**\n * Handles MCP requests to list available resources.\n * It maps the resolved Genkit resource actions to the MCP Resource format.\n * @param req The MCP ListResourcesRequest.\n * @returns A Promise resolving to an MCP ListResourcesResult.\n */\n async readResource(req: ReadResourceRequest): Promise<ReadResourceResult> {\n await this.setup();\n const resource = this.resourceActions.find((r) =>\n r.matches({ uri: req.params.uri })\n );\n if (!resource) {\n throw new GenkitError({\n status: 'NOT_FOUND',\n message: `Tried to call resource '${req.params.uri}' but it could not be found.`,\n });\n }\n const result = await resource({ uri: req.params.uri });\n return {\n contents: toMcpResourceMessage(req.params.uri, result.content),\n };\n }\n\n /**\n * Handles MCP requests to get (render) a specific prompt. It finds the\n * corresponding Genkit prompt action, executes it with the provided\n * arguments, and then formats the resulting messages into the MCP\n * PromptMessage format.\n * @param req The MCP GetPromptRequest containing the prompt name and\n * arguments.\n * @returns A Promise resolving to an MCP GetPromptResult.\n * @throws GenkitError if the requested prompt is not found.\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: `[MCP Server] 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 /**\n * Starts the MCP server with the specified transport or a default\n * StdioServerTransport. Ensures the server is set up before connecting the\n * transport.\n * @param transport Optional MCP transport instance. If not provided, a\n * StdioServerTransport will be created and used.\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.debug(\n `[MCP Server] MCP server '${this.options.name}' started successfully.`\n );\n }\n}\n\n/**\n * Converts a Genkit prompt's input schema to an array of MCP prompt arguments.\n * MCP prompts currently only support string arguments.\n * @param p The Genkit PromptAction.\n * @returns An array of MCP prompt arguments, or undefined if the schema is not defined.\n * @throws GenkitError if the input schema is not an object or if any property is not a string.\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: '[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit MessageData object to an MCP PromptMessage.\n * Handles mapping of roles and content types (text, image).\n * MCP only supports 'user' and 'assistant' (model) roles and base64 data images.\n * @param messageData The Genkit MessageData object.\n * @returns An MCP PromptMessage.\n * @throws GenkitError if the role is unsupported or if media is not a data URL.\n */\nfunction toMcpPromptMessage(messageData: MessageData): PromptMessage {\n if (messageData.role !== 'model' && messageData.role !== 'user') {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] 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: `[MCP Server] 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\n/**\n * Converts a Genkit Parts to an MCP resource content.\n * Handles mapping of roles and content types (text, image).\n */\nfunction toMcpResourceMessage(\n uri: string,\n content: Part[]\n): ReadResourceResult['contents'] {\n return content.map((p) => {\n if (p.media) {\n const { url, contentType } = p.media;\n if (!url.startsWith('data:'))\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP resource 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 { uri, mimeType, blob: data };\n } else if (p.text) {\n return { uri, text: p.text };\n } else {\n throw new GenkitError({\n status: 'UNIMPLEMENTED',\n message: `[MCP Server] MCP resource messages only support media and text parts.`,\n });\n }\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "genai",
10
10
  "generative-ai"
11
11
  ],
12
- "version": "1.20.0",
12
+ "version": "1.22.0-rc.0",
13
13
  "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.",
14
14
  "main": "./lib/index.js",
15
15
  "types": "./lib/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "license": "Apache-2.0",
32
32
  "peerDependencies": {
33
33
  "@modelcontextprotocol/sdk": "^1.13.0",
34
- "genkit": "^1.20.0"
34
+ "genkit": "^1.22.0-rc.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "get-port": "^5.1.0",
@@ -24,6 +24,7 @@ import {
24
24
  } from '@modelcontextprotocol/sdk/types.js';
25
25
  import {
26
26
  GenkitError,
27
+ type DynamicActionProviderAction,
27
28
  type DynamicResourceAction,
28
29
  type ExecutablePrompt,
29
30
  type Genkit,
@@ -113,6 +114,10 @@ export type McpClientOptions = {
113
114
  sessionId?: string;
114
115
  };
115
116
 
117
+ export type McpClientOptionsWithCache = McpClientOptions & {
118
+ cacheTtlMillis?: number;
119
+ };
120
+
116
121
  /**
117
122
  * Represents a client connection to a single MCP (Model Context Protocol) server.
118
123
  * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)
@@ -123,6 +128,7 @@ export type McpClientOptions = {
123
128
  */
124
129
  export class GenkitMcpClient {
125
130
  _server?: McpServerRef;
131
+ private _dynamicActionProvider: DynamicActionProviderAction | undefined;
126
132
 
127
133
  sessionId?: string;
128
134
  readonly name: string;
@@ -152,6 +158,16 @@ export class GenkitMcpClient {
152
158
  this._initializeConnection();
153
159
  }
154
160
 
161
+ set dynamicActionProvider(dap: DynamicActionProviderAction) {
162
+ this._dynamicActionProvider = dap;
163
+ }
164
+
165
+ _invalidateDapCache(): void {
166
+ if (this._dynamicActionProvider) {
167
+ this._dynamicActionProvider.invalidateCache();
168
+ }
169
+ }
170
+
155
171
  get serverName(): string {
156
172
  return (
157
173
  this.suppliedServerName ??
@@ -163,6 +179,7 @@ export class GenkitMcpClient {
163
179
  async updateRoots(roots: Root[]) {
164
180
  this.roots = roots;
165
181
  await this._server?.client.sendRootsListChanged();
182
+ this._invalidateDapCache();
166
183
  }
167
184
 
168
185
  /**
@@ -188,6 +205,7 @@ export class GenkitMcpClient {
188
205
  if (this.roots) {
189
206
  await this.updateRoots(this.roots);
190
207
  }
208
+ this._invalidateDapCache();
191
209
  }
192
210
 
193
211
  /**
@@ -208,6 +226,7 @@ export class GenkitMcpClient {
208
226
  */
209
227
  private async _connect() {
210
228
  if (this._server) await this._server.transport.close();
229
+ this._invalidateDapCache();
211
230
  logger.debug(
212
231
  `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`
213
232
  );
@@ -249,6 +268,7 @@ export class GenkitMcpClient {
249
268
  transport,
250
269
  error,
251
270
  } as McpServerRef;
271
+ this._invalidateDapCache();
252
272
  }
253
273
 
254
274
  /**
@@ -262,6 +282,7 @@ export class GenkitMcpClient {
262
282
  );
263
283
  await this._server.client.close();
264
284
  this._server = undefined;
285
+ this._invalidateDapCache();
265
286
  }
266
287
  }
267
288
 
@@ -277,6 +298,7 @@ export class GenkitMcpClient {
277
298
  );
278
299
  await this._disconnect();
279
300
  this.disabled = true;
301
+ this._invalidateDapCache();
280
302
  }
281
303
  }
282
304
 
@@ -296,6 +318,7 @@ export class GenkitMcpClient {
296
318
  logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);
297
319
  await this._initializeConnection();
298
320
  this.disabled = !!this._server!.error;
321
+ this._invalidateDapCache();
299
322
  }
300
323
 
301
324
  /**
@@ -310,6 +333,7 @@ export class GenkitMcpClient {
310
333
  );
311
334
  await this._disconnect();
312
335
  await this._initializeConnection();
336
+ this._invalidateDapCache();
313
337
  }
314
338
  }
315
339