@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.
@@ -3,7 +3,7 @@ import { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js
3
3
  import { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4
4
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
5
5
  import { Root } from '@modelcontextprotocol/sdk/types.js';
6
- import { Genkit, ToolAction, DynamicResourceAction, PromptGenerateOptions, ExecutablePrompt } from 'genkit';
6
+ import { DynamicActionProviderAction, Genkit, ToolAction, DynamicResourceAction, PromptGenerateOptions, ExecutablePrompt } from 'genkit';
7
7
 
8
8
  /**
9
9
  * Copyright 2025 Google LLC
@@ -80,6 +80,9 @@ type McpClientOptions = {
80
80
  /** Manually supply a session id for HTTP streaming clients if desired. */
81
81
  sessionId?: string;
82
82
  };
83
+ type McpClientOptionsWithCache = McpClientOptions & {
84
+ cacheTtlMillis?: number;
85
+ };
83
86
  /**
84
87
  * Represents a client connection to a single MCP (Model Context Protocol) server.
85
88
  * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)
@@ -90,6 +93,7 @@ type McpClientOptions = {
90
93
  */
91
94
  declare class GenkitMcpClient {
92
95
  _server?: McpServerRef;
96
+ private _dynamicActionProvider;
93
97
  sessionId?: string;
94
98
  readonly name: string;
95
99
  readonly suppliedServerName?: string;
@@ -101,6 +105,8 @@ declare class GenkitMcpClient {
101
105
  private _readyListeners;
102
106
  private _ready;
103
107
  constructor(options: McpClientOptions);
108
+ set dynamicActionProvider(dap: DynamicActionProviderAction);
109
+ _invalidateDapCache(): void;
104
110
  get serverName(): string;
105
111
  updateRoots(roots: Root[]): Promise<void>;
106
112
  /**
@@ -174,4 +180,4 @@ declare class GenkitMcpClient {
174
180
  get mcpClient(): Client | undefined;
175
181
  }
176
182
 
177
- export { GenkitMcpClient, type McpClientOptions, type McpServerConfig, type McpServerControls, type McpStdioServerConfig, type McpStreamableHttpConfig, type McpTransportServerConfig };
183
+ export { GenkitMcpClient, type McpClientOptions, type McpClientOptionsWithCache, type McpServerConfig, type McpServerControls, type McpStdioServerConfig, type McpStreamableHttpConfig, type McpTransportServerConfig };
@@ -3,7 +3,7 @@ import { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js
3
3
  import { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4
4
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
5
5
  import { Root } from '@modelcontextprotocol/sdk/types.js';
6
- import { Genkit, ToolAction, DynamicResourceAction, PromptGenerateOptions, ExecutablePrompt } from 'genkit';
6
+ import { DynamicActionProviderAction, Genkit, ToolAction, DynamicResourceAction, PromptGenerateOptions, ExecutablePrompt } from 'genkit';
7
7
 
8
8
  /**
9
9
  * Copyright 2025 Google LLC
@@ -80,6 +80,9 @@ type McpClientOptions = {
80
80
  /** Manually supply a session id for HTTP streaming clients if desired. */
81
81
  sessionId?: string;
82
82
  };
83
+ type McpClientOptionsWithCache = McpClientOptions & {
84
+ cacheTtlMillis?: number;
85
+ };
83
86
  /**
84
87
  * Represents a client connection to a single MCP (Model Context Protocol) server.
85
88
  * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)
@@ -90,6 +93,7 @@ type McpClientOptions = {
90
93
  */
91
94
  declare class GenkitMcpClient {
92
95
  _server?: McpServerRef;
96
+ private _dynamicActionProvider;
93
97
  sessionId?: string;
94
98
  readonly name: string;
95
99
  readonly suppliedServerName?: string;
@@ -101,6 +105,8 @@ declare class GenkitMcpClient {
101
105
  private _readyListeners;
102
106
  private _ready;
103
107
  constructor(options: McpClientOptions);
108
+ set dynamicActionProvider(dap: DynamicActionProviderAction);
109
+ _invalidateDapCache(): void;
104
110
  get serverName(): string;
105
111
  updateRoots(roots: Root[]): Promise<void>;
106
112
  /**
@@ -174,4 +180,4 @@ declare class GenkitMcpClient {
174
180
  get mcpClient(): Client | undefined;
175
181
  }
176
182
 
177
- export { GenkitMcpClient, type McpClientOptions, type McpServerConfig, type McpServerControls, type McpStdioServerConfig, type McpStreamableHttpConfig, type McpTransportServerConfig };
183
+ export { GenkitMcpClient, type McpClientOptions, type McpClientOptionsWithCache, type McpServerConfig, type McpServerControls, type McpStdioServerConfig, type McpStreamableHttpConfig, type McpTransportServerConfig };
@@ -29,6 +29,7 @@ var import_util = require("../util");
29
29
  var import_resource = require("../util/resource");
30
30
  class GenkitMcpClient {
31
31
  _server;
32
+ _dynamicActionProvider;
32
33
  sessionId;
33
34
  name;
34
35
  suppliedServerName;
@@ -50,12 +51,21 @@ class GenkitMcpClient {
50
51
  this.sessionId = options.sessionId;
51
52
  this._initializeConnection();
52
53
  }
54
+ set dynamicActionProvider(dap) {
55
+ this._dynamicActionProvider = dap;
56
+ }
57
+ _invalidateDapCache() {
58
+ if (this._dynamicActionProvider) {
59
+ this._dynamicActionProvider.invalidateCache();
60
+ }
61
+ }
53
62
  get serverName() {
54
63
  return this.suppliedServerName ?? this._server?.client.getServerVersion()?.name ?? "unknown-server";
55
64
  }
56
65
  async updateRoots(roots) {
57
66
  this.roots = roots;
58
67
  await this._server?.client.sendRootsListChanged();
68
+ this._invalidateDapCache();
59
69
  }
60
70
  /**
61
71
  * Sets up a connection based on a provided map of server configurations.
@@ -80,6 +90,7 @@ class GenkitMcpClient {
80
90
  if (this.roots) {
81
91
  await this.updateRoots(this.roots);
82
92
  }
93
+ this._invalidateDapCache();
83
94
  }
84
95
  /**
85
96
  * Returns a Promise that resolves when the client has attempted to connect
@@ -98,6 +109,7 @@ class GenkitMcpClient {
98
109
  */
99
110
  async _connect() {
100
111
  if (this._server) await this._server.transport.close();
112
+ this._invalidateDapCache();
101
113
  import_logging.logger.debug(
102
114
  `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`
103
115
  );
@@ -134,6 +146,7 @@ class GenkitMcpClient {
134
146
  transport,
135
147
  error
136
148
  };
149
+ this._invalidateDapCache();
137
150
  }
138
151
  /**
139
152
  * Disconnects the MCP server and removes its registration
@@ -146,6 +159,7 @@ class GenkitMcpClient {
146
159
  );
147
160
  await this._server.client.close();
148
161
  this._server = void 0;
162
+ this._invalidateDapCache();
149
163
  }
150
164
  }
151
165
  /**
@@ -160,6 +174,7 @@ class GenkitMcpClient {
160
174
  );
161
175
  await this._disconnect();
162
176
  this.disabled = true;
177
+ this._invalidateDapCache();
163
178
  }
164
179
  }
165
180
  /**
@@ -177,6 +192,7 @@ class GenkitMcpClient {
177
192
  import_logging.logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);
178
193
  await this._initializeConnection();
179
194
  this.disabled = !!this._server.error;
195
+ this._invalidateDapCache();
180
196
  }
181
197
  /**
182
198
  * Closes and then restarts the transport connection for the specified server.
@@ -190,6 +206,7 @@ class GenkitMcpClient {
190
206
  );
191
207
  await this._disconnect();
192
208
  await this._initializeConnection();
209
+ this._invalidateDapCache();
193
210
  }
194
211
  }
195
212
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/client.ts"],"sourcesContent":["/**\n * Copyright 2025 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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {\n ListRootsRequestSchema,\n Root,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n type DynamicResourceAction,\n type ExecutablePrompt,\n type Genkit,\n type PromptGenerateOptions,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport {\n fetchAllPrompts,\n fetchDynamicTools,\n getExecutablePrompt,\n transportFrom,\n} from '../util';\nimport { fetchDynamicResources } from '../util/resource';\n\ninterface McpServerRef {\n client: Client;\n transport: Transport;\n error?: string;\n}\n\nexport interface McpServerControls {\n /** when true, the server will be stopped and its registered components will\n * not appear in lists/plugins/etc */\n disabled?: boolean;\n\n // MCP roots configuration. See: https://modelcontextprotocol.io/docs/concepts/roots\n roots?: Root[];\n}\n\nexport type McpStdioServerConfig = StdioServerParameters & {\n url?: never;\n transport?: never;\n};\n\nexport type McpStreamableHttpConfig = {\n url: string;\n command?: never;\n transport?: never;\n} & Omit<StreamableHTTPClientTransportOptions, 'sessionId'>;\n\nexport type McpTransportServerConfig = {\n transport: Transport;\n command?: never;\n url?: never;\n};\n\n/**\n * Configuration for an individual MCP server. The interface should be familiar\n * and compatible with existing tool configurations e.g. Cursor or Claude\n * Desktop.\n *\n * In addition to stdio servers, remote servers are supported via URL and\n * custom/arbitary transports are supported as well.\n */\nexport type McpServerConfig = (\n | McpStdioServerConfig\n | McpStreamableHttpConfig\n | McpTransportServerConfig\n) &\n McpServerControls;\n\n/**\n * Configuration options for an individual `GenkitMcpClient` instance.\n * This defines how the client connects to a single MCP server and how it behaves.\n */\nexport type McpClientOptions = {\n /** Client name to advertise to the server. */\n name: string;\n /** Name for the server, defaults to the server's advertised name. */\n serverName?: string;\n\n /**\n * An optional version number for this client. This is primarily for logging\n * and identification purposes. Defaults to '1.0.0'.\n */\n version?: string;\n /**\n * If true, tool responses from the MCP server will be returned in their raw\n * MCP format. Otherwise (default), they are processed and potentially\n * simplified for better compatibility with Genkit's typical data structures.\n */\n rawToolResponses?: boolean;\n /** The server configuration to connect. */\n mcpServer: McpServerConfig;\n /** Manually supply a session id for HTTP streaming clients if desired. */\n sessionId?: string;\n};\n\n/**\n * Represents a client connection to a single MCP (Model Context Protocol) server.\n * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)\n * and provides methods to fetch tools from the connected server.\n *\n * An instance of `GenkitMcpClient` is typically managed by a `GenkitMcpHost`\n * when dealing with multiple MCP server connections.\n */\nexport class GenkitMcpClient {\n _server?: McpServerRef;\n\n sessionId?: string;\n readonly name: string;\n readonly suppliedServerName?: string;\n private version: string;\n private serverConfig: McpServerConfig;\n private rawToolResponses?: boolean;\n private disabled: boolean;\n private roots?: Root[];\n\n private _readyListeners: {\n resolve: () => void;\n reject: (err: Error) => void;\n }[] = [];\n private _ready = false;\n\n constructor(options: McpClientOptions) {\n this.name = options.name;\n this.suppliedServerName = options.serverName;\n this.version = options.version || '1.0.0';\n this.serverConfig = options.mcpServer;\n this.rawToolResponses = !!options.rawToolResponses;\n this.disabled = !!options.mcpServer.disabled;\n this.roots = options.mcpServer.roots;\n this.sessionId = options.sessionId;\n\n this._initializeConnection();\n }\n\n get serverName(): string {\n return (\n this.suppliedServerName ??\n this._server?.client.getServerVersion()?.name ??\n 'unknown-server'\n );\n }\n\n async updateRoots(roots: Root[]) {\n this.roots = roots;\n await this._server?.client.sendRootsListChanged();\n }\n\n /**\n * Sets up a connection based on a provided map of server configurations.\n * - Reconnects existing servers if their configuration appears to have\n * changed (implicitly handled by `connectServer`).\n * - Sets the client's ready state once all connection attempts are complete.\n * @param mcpServers A record mapping server names to their configurations.\n */\n private async _initializeConnection() {\n this._ready = false;\n try {\n await this._connect();\n this._ready = true;\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.resolve();\n }\n } catch (err) {\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.reject(err as Error);\n }\n }\n if (this.roots) {\n await this.updateRoots(this.roots);\n }\n }\n\n /**\n * Returns a Promise that resolves when the client has attempted to connect\n * to all configured servers, or rejects if a critical error occurs during\n * the initial connection phase.\n */\n async ready() {\n if (this._ready) return;\n return new Promise<void>((resolve, reject) => {\n this._readyListeners.push({ resolve, reject });\n });\n }\n\n /**\n * Connects to a single MCP server defined by the provided configuration.\n * @param config The configuration object for the server.\n */\n private async _connect() {\n if (this._server) await this._server.transport.close();\n logger.debug(\n `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`\n );\n\n const { transport, type: transportType } = await transportFrom(\n this.serverConfig,\n this.sessionId\n );\n if (!transport) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `[MCP Client] Could not determine valid transport config from supplied options.`,\n });\n }\n\n let error: string | undefined;\n\n const client = new Client(\n { name: this.name, version: this.version },\n { capabilities: { roots: { listChanged: true } } }\n );\n client.setRequestHandler(ListRootsRequestSchema, () => {\n logger.debug(`[MCP Client] fetching roots for ${this.name}`);\n return { roots: this.roots || [] };\n });\n\n try {\n await client.connect(transport);\n } catch (e) {\n logger.warn(\n `[MCP Client] Error connecting server via ${transportType} transport: ${e}`\n );\n this.disabled = true;\n error = (e as Error).toString();\n }\n\n this._server = {\n client,\n transport,\n error,\n } as McpServerRef;\n }\n\n /**\n * Disconnects the MCP server and removes its registration\n * from this client instance.\n */\n async _disconnect() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Disconnecting MCP server in client '${this.name}'.`\n );\n await this._server.client.close();\n this._server = undefined;\n }\n }\n\n /**\n * Disables a server. Closes the underlying transport and server's configuration. Does nothing if the server is\n * already disabled.\n */\n async disable() {\n if (!this.isEnabled()) return;\n if (this._server) {\n logger.debug(\n `[MCP Client] Disabling MCP server in client '${this.name}'`\n );\n await this._disconnect();\n this.disabled = true;\n }\n }\n\n /**\n * Whether this client-server connection is enabled or not.\n */\n isEnabled() {\n return !this.disabled;\n }\n\n /**\n * Enables a server connection, including previously disabled ones. Does nothing if the\n * server is not disabled.\n */\n async enable() {\n if (this.isEnabled()) return;\n logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);\n await this._initializeConnection();\n this.disabled = !!this._server!.error;\n }\n\n /**\n * Closes and then restarts the transport connection for the specified server.\n * Useful for attempting to recover from connection issues without full\n * reconfiguration.\n */\n async restart() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Restarting connection to MCP server in client '${this.name}'`\n );\n await this._disconnect();\n await this._initializeConnection();\n }\n }\n\n /**\n * Fetches all tools available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveTools(ai: Genkit): Promise<ToolAction[]> {\n await this.ready();\n let tools: ToolAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.tools)\n tools.push(\n ...(await fetchDynamicTools(ai, this._server.client, {\n rawToolResponses: this.rawToolResponses,\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return tools;\n }\n\n /**\n * Fetches all resources available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveResources(ai: Genkit): Promise<DynamicResourceAction[]> {\n await this.ready();\n let resources: DynamicResourceAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.resources)\n resources.push(\n ...(await fetchDynamicResources(ai, this._server.client, {\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return resources;\n }\n\n /**\n * Fetches all active prompts available through this client, if the server\n * configuration supports prompts.\n * @param ai The Genkit instance.\n * @param options Optional prompt generation options.\n * @returns A promise that resolves to an array of ExecutablePrompt.\n */\n async getActivePrompts(\n ai: Genkit,\n options?: PromptGenerateOptions\n ): Promise<ExecutablePrompt[]> {\n if (this._server?.client.getServerCapabilities()?.prompts) {\n return fetchAllPrompts(this._server.client, {\n ai,\n serverName: this.serverName,\n name: this.name,\n options,\n });\n }\n return [];\n }\n\n /**\n * Get the specified prompt as an `ExecutablePrompt` available through this\n * client. If no such prompt is found, return undefined.\n */\n async getPrompt(\n ai: Genkit,\n promptName: string,\n opts?: PromptGenerateOptions\n ): Promise<ExecutablePrompt | undefined> {\n await this.ready();\n\n if (this._server) {\n const capabilities = await this._server.client.getServerCapabilities();\n if (capabilities?.prompts) {\n return await getExecutablePrompt(this._server.client, {\n ai,\n serverName: this.name,\n promptName,\n name: this.name,\n options: opts,\n });\n }\n logger.debug(`[MCP Client] No prompts are found in this MCP server.`);\n }\n return;\n }\n\n /** Returns the underlying MCP SDK client if one has been initialized. */\n get mcpClient(): Client | undefined {\n return this._server?.client;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/client/client.ts"],"sourcesContent":["/**\n * Copyright 2025 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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {\n ListRootsRequestSchema,\n Root,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n type DynamicActionProviderAction,\n type DynamicResourceAction,\n type ExecutablePrompt,\n type Genkit,\n type PromptGenerateOptions,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport {\n fetchAllPrompts,\n fetchDynamicTools,\n getExecutablePrompt,\n transportFrom,\n} from '../util';\nimport { fetchDynamicResources } from '../util/resource';\n\ninterface McpServerRef {\n client: Client;\n transport: Transport;\n error?: string;\n}\n\nexport interface McpServerControls {\n /** when true, the server will be stopped and its registered components will\n * not appear in lists/plugins/etc */\n disabled?: boolean;\n\n // MCP roots configuration. See: https://modelcontextprotocol.io/docs/concepts/roots\n roots?: Root[];\n}\n\nexport type McpStdioServerConfig = StdioServerParameters & {\n url?: never;\n transport?: never;\n};\n\nexport type McpStreamableHttpConfig = {\n url: string;\n command?: never;\n transport?: never;\n} & Omit<StreamableHTTPClientTransportOptions, 'sessionId'>;\n\nexport type McpTransportServerConfig = {\n transport: Transport;\n command?: never;\n url?: never;\n};\n\n/**\n * Configuration for an individual MCP server. The interface should be familiar\n * and compatible with existing tool configurations e.g. Cursor or Claude\n * Desktop.\n *\n * In addition to stdio servers, remote servers are supported via URL and\n * custom/arbitary transports are supported as well.\n */\nexport type McpServerConfig = (\n | McpStdioServerConfig\n | McpStreamableHttpConfig\n | McpTransportServerConfig\n) &\n McpServerControls;\n\n/**\n * Configuration options for an individual `GenkitMcpClient` instance.\n * This defines how the client connects to a single MCP server and how it behaves.\n */\nexport type McpClientOptions = {\n /** Client name to advertise to the server. */\n name: string;\n /** Name for the server, defaults to the server's advertised name. */\n serverName?: string;\n\n /**\n * An optional version number for this client. This is primarily for logging\n * and identification purposes. Defaults to '1.0.0'.\n */\n version?: string;\n /**\n * If true, tool responses from the MCP server will be returned in their raw\n * MCP format. Otherwise (default), they are processed and potentially\n * simplified for better compatibility with Genkit's typical data structures.\n */\n rawToolResponses?: boolean;\n /** The server configuration to connect. */\n mcpServer: McpServerConfig;\n /** Manually supply a session id for HTTP streaming clients if desired. */\n sessionId?: string;\n};\n\nexport type McpClientOptionsWithCache = McpClientOptions & {\n cacheTtlMillis?: number;\n};\n\n/**\n * Represents a client connection to a single MCP (Model Context Protocol) server.\n * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)\n * and provides methods to fetch tools from the connected server.\n *\n * An instance of `GenkitMcpClient` is typically managed by a `GenkitMcpHost`\n * when dealing with multiple MCP server connections.\n */\nexport class GenkitMcpClient {\n _server?: McpServerRef;\n private _dynamicActionProvider: DynamicActionProviderAction | undefined;\n\n sessionId?: string;\n readonly name: string;\n readonly suppliedServerName?: string;\n private version: string;\n private serverConfig: McpServerConfig;\n private rawToolResponses?: boolean;\n private disabled: boolean;\n private roots?: Root[];\n\n private _readyListeners: {\n resolve: () => void;\n reject: (err: Error) => void;\n }[] = [];\n private _ready = false;\n\n constructor(options: McpClientOptions) {\n this.name = options.name;\n this.suppliedServerName = options.serverName;\n this.version = options.version || '1.0.0';\n this.serverConfig = options.mcpServer;\n this.rawToolResponses = !!options.rawToolResponses;\n this.disabled = !!options.mcpServer.disabled;\n this.roots = options.mcpServer.roots;\n this.sessionId = options.sessionId;\n\n this._initializeConnection();\n }\n\n set dynamicActionProvider(dap: DynamicActionProviderAction) {\n this._dynamicActionProvider = dap;\n }\n\n _invalidateDapCache(): void {\n if (this._dynamicActionProvider) {\n this._dynamicActionProvider.invalidateCache();\n }\n }\n\n get serverName(): string {\n return (\n this.suppliedServerName ??\n this._server?.client.getServerVersion()?.name ??\n 'unknown-server'\n );\n }\n\n async updateRoots(roots: Root[]) {\n this.roots = roots;\n await this._server?.client.sendRootsListChanged();\n this._invalidateDapCache();\n }\n\n /**\n * Sets up a connection based on a provided map of server configurations.\n * - Reconnects existing servers if their configuration appears to have\n * changed (implicitly handled by `connectServer`).\n * - Sets the client's ready state once all connection attempts are complete.\n * @param mcpServers A record mapping server names to their configurations.\n */\n private async _initializeConnection() {\n this._ready = false;\n try {\n await this._connect();\n this._ready = true;\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.resolve();\n }\n } catch (err) {\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.reject(err as Error);\n }\n }\n if (this.roots) {\n await this.updateRoots(this.roots);\n }\n this._invalidateDapCache();\n }\n\n /**\n * Returns a Promise that resolves when the client has attempted to connect\n * to all configured servers, or rejects if a critical error occurs during\n * the initial connection phase.\n */\n async ready() {\n if (this._ready) return;\n return new Promise<void>((resolve, reject) => {\n this._readyListeners.push({ resolve, reject });\n });\n }\n\n /**\n * Connects to a single MCP server defined by the provided configuration.\n * @param config The configuration object for the server.\n */\n private async _connect() {\n if (this._server) await this._server.transport.close();\n this._invalidateDapCache();\n logger.debug(\n `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`\n );\n\n const { transport, type: transportType } = await transportFrom(\n this.serverConfig,\n this.sessionId\n );\n if (!transport) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `[MCP Client] Could not determine valid transport config from supplied options.`,\n });\n }\n\n let error: string | undefined;\n\n const client = new Client(\n { name: this.name, version: this.version },\n { capabilities: { roots: { listChanged: true } } }\n );\n client.setRequestHandler(ListRootsRequestSchema, () => {\n logger.debug(`[MCP Client] fetching roots for ${this.name}`);\n return { roots: this.roots || [] };\n });\n\n try {\n await client.connect(transport);\n } catch (e) {\n logger.warn(\n `[MCP Client] Error connecting server via ${transportType} transport: ${e}`\n );\n this.disabled = true;\n error = (e as Error).toString();\n }\n\n this._server = {\n client,\n transport,\n error,\n } as McpServerRef;\n this._invalidateDapCache();\n }\n\n /**\n * Disconnects the MCP server and removes its registration\n * from this client instance.\n */\n async _disconnect() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Disconnecting MCP server in client '${this.name}'.`\n );\n await this._server.client.close();\n this._server = undefined;\n this._invalidateDapCache();\n }\n }\n\n /**\n * Disables a server. Closes the underlying transport and server's configuration. Does nothing if the server is\n * already disabled.\n */\n async disable() {\n if (!this.isEnabled()) return;\n if (this._server) {\n logger.debug(\n `[MCP Client] Disabling MCP server in client '${this.name}'`\n );\n await this._disconnect();\n this.disabled = true;\n this._invalidateDapCache();\n }\n }\n\n /**\n * Whether this client-server connection is enabled or not.\n */\n isEnabled() {\n return !this.disabled;\n }\n\n /**\n * Enables a server connection, including previously disabled ones. Does nothing if the\n * server is not disabled.\n */\n async enable() {\n if (this.isEnabled()) return;\n logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);\n await this._initializeConnection();\n this.disabled = !!this._server!.error;\n this._invalidateDapCache();\n }\n\n /**\n * Closes and then restarts the transport connection for the specified server.\n * Useful for attempting to recover from connection issues without full\n * reconfiguration.\n */\n async restart() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Restarting connection to MCP server in client '${this.name}'`\n );\n await this._disconnect();\n await this._initializeConnection();\n this._invalidateDapCache();\n }\n }\n\n /**\n * Fetches all tools available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveTools(ai: Genkit): Promise<ToolAction[]> {\n await this.ready();\n let tools: ToolAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.tools)\n tools.push(\n ...(await fetchDynamicTools(ai, this._server.client, {\n rawToolResponses: this.rawToolResponses,\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return tools;\n }\n\n /**\n * Fetches all resources available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveResources(ai: Genkit): Promise<DynamicResourceAction[]> {\n await this.ready();\n let resources: DynamicResourceAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.resources)\n resources.push(\n ...(await fetchDynamicResources(ai, this._server.client, {\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return resources;\n }\n\n /**\n * Fetches all active prompts available through this client, if the server\n * configuration supports prompts.\n * @param ai The Genkit instance.\n * @param options Optional prompt generation options.\n * @returns A promise that resolves to an array of ExecutablePrompt.\n */\n async getActivePrompts(\n ai: Genkit,\n options?: PromptGenerateOptions\n ): Promise<ExecutablePrompt[]> {\n if (this._server?.client.getServerCapabilities()?.prompts) {\n return fetchAllPrompts(this._server.client, {\n ai,\n serverName: this.serverName,\n name: this.name,\n options,\n });\n }\n return [];\n }\n\n /**\n * Get the specified prompt as an `ExecutablePrompt` available through this\n * client. If no such prompt is found, return undefined.\n */\n async getPrompt(\n ai: Genkit,\n promptName: string,\n opts?: PromptGenerateOptions\n ): Promise<ExecutablePrompt | undefined> {\n await this.ready();\n\n if (this._server) {\n const capabilities = await this._server.client.getServerCapabilities();\n if (capabilities?.prompts) {\n return await getExecutablePrompt(this._server.client, {\n ai,\n serverName: this.name,\n promptName,\n name: this.name,\n options: opts,\n });\n }\n logger.debug(`[MCP Client] No prompts are found in this MCP server.`);\n }\n return;\n }\n\n /** Returns the underlying MCP SDK client if one has been initialized. */\n get mcpClient(): Client | undefined {\n return this._server?.client;\n }\n}\n"],"mappings":"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","names":[]}
@@ -15,6 +15,7 @@ import {
15
15
  import { fetchDynamicResources } from "../util/resource";
16
16
  class GenkitMcpClient {
17
17
  _server;
18
+ _dynamicActionProvider;
18
19
  sessionId;
19
20
  name;
20
21
  suppliedServerName;
@@ -36,12 +37,21 @@ class GenkitMcpClient {
36
37
  this.sessionId = options.sessionId;
37
38
  this._initializeConnection();
38
39
  }
40
+ set dynamicActionProvider(dap) {
41
+ this._dynamicActionProvider = dap;
42
+ }
43
+ _invalidateDapCache() {
44
+ if (this._dynamicActionProvider) {
45
+ this._dynamicActionProvider.invalidateCache();
46
+ }
47
+ }
39
48
  get serverName() {
40
49
  return this.suppliedServerName ?? this._server?.client.getServerVersion()?.name ?? "unknown-server";
41
50
  }
42
51
  async updateRoots(roots) {
43
52
  this.roots = roots;
44
53
  await this._server?.client.sendRootsListChanged();
54
+ this._invalidateDapCache();
45
55
  }
46
56
  /**
47
57
  * Sets up a connection based on a provided map of server configurations.
@@ -66,6 +76,7 @@ class GenkitMcpClient {
66
76
  if (this.roots) {
67
77
  await this.updateRoots(this.roots);
68
78
  }
79
+ this._invalidateDapCache();
69
80
  }
70
81
  /**
71
82
  * Returns a Promise that resolves when the client has attempted to connect
@@ -84,6 +95,7 @@ class GenkitMcpClient {
84
95
  */
85
96
  async _connect() {
86
97
  if (this._server) await this._server.transport.close();
98
+ this._invalidateDapCache();
87
99
  logger.debug(
88
100
  `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`
89
101
  );
@@ -120,6 +132,7 @@ class GenkitMcpClient {
120
132
  transport,
121
133
  error
122
134
  };
135
+ this._invalidateDapCache();
123
136
  }
124
137
  /**
125
138
  * Disconnects the MCP server and removes its registration
@@ -132,6 +145,7 @@ class GenkitMcpClient {
132
145
  );
133
146
  await this._server.client.close();
134
147
  this._server = void 0;
148
+ this._invalidateDapCache();
135
149
  }
136
150
  }
137
151
  /**
@@ -146,6 +160,7 @@ class GenkitMcpClient {
146
160
  );
147
161
  await this._disconnect();
148
162
  this.disabled = true;
163
+ this._invalidateDapCache();
149
164
  }
150
165
  }
151
166
  /**
@@ -163,6 +178,7 @@ class GenkitMcpClient {
163
178
  logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);
164
179
  await this._initializeConnection();
165
180
  this.disabled = !!this._server.error;
181
+ this._invalidateDapCache();
166
182
  }
167
183
  /**
168
184
  * Closes and then restarts the transport connection for the specified server.
@@ -176,6 +192,7 @@ class GenkitMcpClient {
176
192
  );
177
193
  await this._disconnect();
178
194
  await this._initializeConnection();
195
+ this._invalidateDapCache();
179
196
  }
180
197
  }
181
198
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/client.ts"],"sourcesContent":["/**\n * Copyright 2025 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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {\n ListRootsRequestSchema,\n Root,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n type DynamicResourceAction,\n type ExecutablePrompt,\n type Genkit,\n type PromptGenerateOptions,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport {\n fetchAllPrompts,\n fetchDynamicTools,\n getExecutablePrompt,\n transportFrom,\n} from '../util';\nimport { fetchDynamicResources } from '../util/resource';\n\ninterface McpServerRef {\n client: Client;\n transport: Transport;\n error?: string;\n}\n\nexport interface McpServerControls {\n /** when true, the server will be stopped and its registered components will\n * not appear in lists/plugins/etc */\n disabled?: boolean;\n\n // MCP roots configuration. See: https://modelcontextprotocol.io/docs/concepts/roots\n roots?: Root[];\n}\n\nexport type McpStdioServerConfig = StdioServerParameters & {\n url?: never;\n transport?: never;\n};\n\nexport type McpStreamableHttpConfig = {\n url: string;\n command?: never;\n transport?: never;\n} & Omit<StreamableHTTPClientTransportOptions, 'sessionId'>;\n\nexport type McpTransportServerConfig = {\n transport: Transport;\n command?: never;\n url?: never;\n};\n\n/**\n * Configuration for an individual MCP server. The interface should be familiar\n * and compatible with existing tool configurations e.g. Cursor or Claude\n * Desktop.\n *\n * In addition to stdio servers, remote servers are supported via URL and\n * custom/arbitary transports are supported as well.\n */\nexport type McpServerConfig = (\n | McpStdioServerConfig\n | McpStreamableHttpConfig\n | McpTransportServerConfig\n) &\n McpServerControls;\n\n/**\n * Configuration options for an individual `GenkitMcpClient` instance.\n * This defines how the client connects to a single MCP server and how it behaves.\n */\nexport type McpClientOptions = {\n /** Client name to advertise to the server. */\n name: string;\n /** Name for the server, defaults to the server's advertised name. */\n serverName?: string;\n\n /**\n * An optional version number for this client. This is primarily for logging\n * and identification purposes. Defaults to '1.0.0'.\n */\n version?: string;\n /**\n * If true, tool responses from the MCP server will be returned in their raw\n * MCP format. Otherwise (default), they are processed and potentially\n * simplified for better compatibility with Genkit's typical data structures.\n */\n rawToolResponses?: boolean;\n /** The server configuration to connect. */\n mcpServer: McpServerConfig;\n /** Manually supply a session id for HTTP streaming clients if desired. */\n sessionId?: string;\n};\n\n/**\n * Represents a client connection to a single MCP (Model Context Protocol) server.\n * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)\n * and provides methods to fetch tools from the connected server.\n *\n * An instance of `GenkitMcpClient` is typically managed by a `GenkitMcpHost`\n * when dealing with multiple MCP server connections.\n */\nexport class GenkitMcpClient {\n _server?: McpServerRef;\n\n sessionId?: string;\n readonly name: string;\n readonly suppliedServerName?: string;\n private version: string;\n private serverConfig: McpServerConfig;\n private rawToolResponses?: boolean;\n private disabled: boolean;\n private roots?: Root[];\n\n private _readyListeners: {\n resolve: () => void;\n reject: (err: Error) => void;\n }[] = [];\n private _ready = false;\n\n constructor(options: McpClientOptions) {\n this.name = options.name;\n this.suppliedServerName = options.serverName;\n this.version = options.version || '1.0.0';\n this.serverConfig = options.mcpServer;\n this.rawToolResponses = !!options.rawToolResponses;\n this.disabled = !!options.mcpServer.disabled;\n this.roots = options.mcpServer.roots;\n this.sessionId = options.sessionId;\n\n this._initializeConnection();\n }\n\n get serverName(): string {\n return (\n this.suppliedServerName ??\n this._server?.client.getServerVersion()?.name ??\n 'unknown-server'\n );\n }\n\n async updateRoots(roots: Root[]) {\n this.roots = roots;\n await this._server?.client.sendRootsListChanged();\n }\n\n /**\n * Sets up a connection based on a provided map of server configurations.\n * - Reconnects existing servers if their configuration appears to have\n * changed (implicitly handled by `connectServer`).\n * - Sets the client's ready state once all connection attempts are complete.\n * @param mcpServers A record mapping server names to their configurations.\n */\n private async _initializeConnection() {\n this._ready = false;\n try {\n await this._connect();\n this._ready = true;\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.resolve();\n }\n } catch (err) {\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.reject(err as Error);\n }\n }\n if (this.roots) {\n await this.updateRoots(this.roots);\n }\n }\n\n /**\n * Returns a Promise that resolves when the client has attempted to connect\n * to all configured servers, or rejects if a critical error occurs during\n * the initial connection phase.\n */\n async ready() {\n if (this._ready) return;\n return new Promise<void>((resolve, reject) => {\n this._readyListeners.push({ resolve, reject });\n });\n }\n\n /**\n * Connects to a single MCP server defined by the provided configuration.\n * @param config The configuration object for the server.\n */\n private async _connect() {\n if (this._server) await this._server.transport.close();\n logger.debug(\n `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`\n );\n\n const { transport, type: transportType } = await transportFrom(\n this.serverConfig,\n this.sessionId\n );\n if (!transport) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `[MCP Client] Could not determine valid transport config from supplied options.`,\n });\n }\n\n let error: string | undefined;\n\n const client = new Client(\n { name: this.name, version: this.version },\n { capabilities: { roots: { listChanged: true } } }\n );\n client.setRequestHandler(ListRootsRequestSchema, () => {\n logger.debug(`[MCP Client] fetching roots for ${this.name}`);\n return { roots: this.roots || [] };\n });\n\n try {\n await client.connect(transport);\n } catch (e) {\n logger.warn(\n `[MCP Client] Error connecting server via ${transportType} transport: ${e}`\n );\n this.disabled = true;\n error = (e as Error).toString();\n }\n\n this._server = {\n client,\n transport,\n error,\n } as McpServerRef;\n }\n\n /**\n * Disconnects the MCP server and removes its registration\n * from this client instance.\n */\n async _disconnect() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Disconnecting MCP server in client '${this.name}'.`\n );\n await this._server.client.close();\n this._server = undefined;\n }\n }\n\n /**\n * Disables a server. Closes the underlying transport and server's configuration. Does nothing if the server is\n * already disabled.\n */\n async disable() {\n if (!this.isEnabled()) return;\n if (this._server) {\n logger.debug(\n `[MCP Client] Disabling MCP server in client '${this.name}'`\n );\n await this._disconnect();\n this.disabled = true;\n }\n }\n\n /**\n * Whether this client-server connection is enabled or not.\n */\n isEnabled() {\n return !this.disabled;\n }\n\n /**\n * Enables a server connection, including previously disabled ones. Does nothing if the\n * server is not disabled.\n */\n async enable() {\n if (this.isEnabled()) return;\n logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);\n await this._initializeConnection();\n this.disabled = !!this._server!.error;\n }\n\n /**\n * Closes and then restarts the transport connection for the specified server.\n * Useful for attempting to recover from connection issues without full\n * reconfiguration.\n */\n async restart() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Restarting connection to MCP server in client '${this.name}'`\n );\n await this._disconnect();\n await this._initializeConnection();\n }\n }\n\n /**\n * Fetches all tools available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveTools(ai: Genkit): Promise<ToolAction[]> {\n await this.ready();\n let tools: ToolAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.tools)\n tools.push(\n ...(await fetchDynamicTools(ai, this._server.client, {\n rawToolResponses: this.rawToolResponses,\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return tools;\n }\n\n /**\n * Fetches all resources available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveResources(ai: Genkit): Promise<DynamicResourceAction[]> {\n await this.ready();\n let resources: DynamicResourceAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.resources)\n resources.push(\n ...(await fetchDynamicResources(ai, this._server.client, {\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return resources;\n }\n\n /**\n * Fetches all active prompts available through this client, if the server\n * configuration supports prompts.\n * @param ai The Genkit instance.\n * @param options Optional prompt generation options.\n * @returns A promise that resolves to an array of ExecutablePrompt.\n */\n async getActivePrompts(\n ai: Genkit,\n options?: PromptGenerateOptions\n ): Promise<ExecutablePrompt[]> {\n if (this._server?.client.getServerCapabilities()?.prompts) {\n return fetchAllPrompts(this._server.client, {\n ai,\n serverName: this.serverName,\n name: this.name,\n options,\n });\n }\n return [];\n }\n\n /**\n * Get the specified prompt as an `ExecutablePrompt` available through this\n * client. If no such prompt is found, return undefined.\n */\n async getPrompt(\n ai: Genkit,\n promptName: string,\n opts?: PromptGenerateOptions\n ): Promise<ExecutablePrompt | undefined> {\n await this.ready();\n\n if (this._server) {\n const capabilities = await this._server.client.getServerCapabilities();\n if (capabilities?.prompts) {\n return await getExecutablePrompt(this._server.client, {\n ai,\n serverName: this.name,\n promptName,\n name: this.name,\n options: opts,\n });\n }\n logger.debug(`[MCP Client] No prompts are found in this MCP server.`);\n }\n return;\n }\n\n /** Returns the underlying MCP SDK client if one has been initialized. */\n get mcpClient(): Client | undefined {\n return this._server?.client;\n }\n}\n"],"mappings":"AAgBA,SAAS,cAAc;AAIvB;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAMK;AACP,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AAoF/B,MAAM,gBAAgB;AAAA,EAC3B;AAAA,EAEA;AAAA,EACS;AAAA,EACA;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,kBAGF,CAAC;AAAA,EACC,SAAS;AAAA,EAEjB,YAAY,SAA2B;AACrC,SAAK,OAAO,QAAQ;AACpB,SAAK,qBAAqB,QAAQ;AAClC,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,eAAe,QAAQ;AAC5B,SAAK,mBAAmB,CAAC,CAAC,QAAQ;AAClC,SAAK,WAAW,CAAC,CAAC,QAAQ,UAAU;AACpC,SAAK,QAAQ,QAAQ,UAAU;AAC/B,SAAK,YAAY,QAAQ;AAEzB,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEA,IAAI,aAAqB;AACvB,WACE,KAAK,sBACL,KAAK,SAAS,OAAO,iBAAiB,GAAG,QACzC;AAAA,EAEJ;AAAA,EAEA,MAAM,YAAY,OAAe;AAC/B,SAAK,QAAQ;AACb,UAAM,KAAK,SAAS,OAAO,qBAAqB;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,wBAAwB;AACpC,SAAK,SAAS;AACd,QAAI;AACF,YAAM,KAAK,SAAS;AACpB,WAAK,SAAS;AACd,aAAO,KAAK,gBAAgB,QAAQ;AAClC,aAAK,gBAAgB,IAAI,GAAG,QAAQ;AAAA,MACtC;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,KAAK,gBAAgB,QAAQ;AAClC,aAAK,gBAAgB,IAAI,GAAG,OAAO,GAAY;AAAA,MACjD;AAAA,IACF;AACA,QAAI,KAAK,OAAO;AACd,YAAM,KAAK,YAAY,KAAK,KAAK;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACZ,QAAI,KAAK,OAAQ;AACjB,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,WAAK,gBAAgB,KAAK,EAAE,SAAS,OAAO,CAAC;AAAA,IAC/C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,WAAW;AACvB,QAAI,KAAK,QAAS,OAAM,KAAK,QAAQ,UAAU,MAAM;AACrD,WAAO;AAAA,MACL,uCAAuC,KAAK,UAAU,gBAAgB,KAAK,IAAI;AAAA,IACjF;AAEA,UAAM,EAAE,WAAW,MAAM,cAAc,IAAI,MAAM;AAAA,MAC/C,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AACA,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,YAAY;AAAA,QACpB,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,QAAI;AAEJ,UAAM,SAAS,IAAI;AAAA,MACjB,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,QAAQ;AAAA,MACzC,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,KAAK,EAAE,EAAE;AAAA,IACnD;AACA,WAAO,kBAAkB,wBAAwB,MAAM;AACrD,aAAO,MAAM,mCAAmC,KAAK,IAAI,EAAE;AAC3D,aAAO,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE;AAAA,IACnC,CAAC;AAED,QAAI;AACF,YAAM,OAAO,QAAQ,SAAS;AAAA,IAChC,SAAS,GAAG;AACV,aAAO;AAAA,QACL,4CAA4C,aAAa,eAAe,CAAC;AAAA,MAC3E;AACA,WAAK,WAAW;AAChB,cAAS,EAAY,SAAS;AAAA,IAChC;AAEA,SAAK,UAAU;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc;AAClB,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,QACL,oDAAoD,KAAK,IAAI;AAAA,MAC/D;AACA,YAAM,KAAK,QAAQ,OAAO,MAAM;AAChC,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU;AACd,QAAI,CAAC,KAAK,UAAU,EAAG;AACvB,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,QACL,gDAAgD,KAAK,IAAI;AAAA,MAC3D;AACA,YAAM,KAAK,YAAY;AACvB,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY;AACV,WAAO,CAAC,KAAK;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAAS;AACb,QAAI,KAAK,UAAU,EAAG;AACtB,WAAO,MAAM,iDAAiD,KAAK,IAAI,GAAG;AAC1E,UAAM,KAAK,sBAAsB;AACjC,SAAK,WAAW,CAAC,CAAC,KAAK,QAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAU;AACd,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,QACL,+DAA+D,KAAK,IAAI;AAAA,MAC1E;AACA,YAAM,KAAK,YAAY;AACvB,YAAM,KAAK,sBAAsB;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,IAAmC;AACtD,UAAM,KAAK,MAAM;AACjB,QAAI,QAAsB,CAAC;AAE3B,QAAI,KAAK,SAAS;AAChB,YAAM,eAAe,KAAK,QAAQ,OAAO,sBAAsB;AAC/D,UAAI,cAAc;AAChB,cAAM;AAAA,UACJ,GAAI,MAAM,kBAAkB,IAAI,KAAK,QAAQ,QAAQ;AAAA,YACnD,kBAAkB,KAAK;AAAA,YACvB,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,UACb,CAAC;AAAA,QACH;AAAA,IACJ;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAmB,IAA8C;AACrE,UAAM,KAAK,MAAM;AACjB,QAAI,YAAqC,CAAC;AAE1C,QAAI,KAAK,SAAS;AAChB,YAAM,eAAe,KAAK,QAAQ,OAAO,sBAAsB;AAC/D,UAAI,cAAc;AAChB,kBAAU;AAAA,UACR,GAAI,MAAM,sBAAsB,IAAI,KAAK,QAAQ,QAAQ;AAAA,YACvD,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,UACb,CAAC;AAAA,QACH;AAAA,IACJ;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,IACA,SAC6B;AAC7B,QAAI,KAAK,SAAS,OAAO,sBAAsB,GAAG,SAAS;AACzD,aAAO,gBAAgB,KAAK,QAAQ,QAAQ;AAAA,QAC1C;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,MAAM,KAAK;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UACJ,IACA,YACA,MACuC;AACvC,UAAM,KAAK,MAAM;AAEjB,QAAI,KAAK,SAAS;AAChB,YAAM,eAAe,MAAM,KAAK,QAAQ,OAAO,sBAAsB;AACrE,UAAI,cAAc,SAAS;AACzB,eAAO,MAAM,oBAAoB,KAAK,QAAQ,QAAQ;AAAA,UACpD;AAAA,UACA,YAAY,KAAK;AAAA,UACjB;AAAA,UACA,MAAM,KAAK;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,aAAO,MAAM,uDAAuD;AAAA,IACtE;AACA;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,YAAgC;AAClC,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/client/client.ts"],"sourcesContent":["/**\n * Copyright 2025 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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {\n ListRootsRequestSchema,\n Root,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n GenkitError,\n type DynamicActionProviderAction,\n type DynamicResourceAction,\n type ExecutablePrompt,\n type Genkit,\n type PromptGenerateOptions,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\nimport {\n fetchAllPrompts,\n fetchDynamicTools,\n getExecutablePrompt,\n transportFrom,\n} from '../util';\nimport { fetchDynamicResources } from '../util/resource';\n\ninterface McpServerRef {\n client: Client;\n transport: Transport;\n error?: string;\n}\n\nexport interface McpServerControls {\n /** when true, the server will be stopped and its registered components will\n * not appear in lists/plugins/etc */\n disabled?: boolean;\n\n // MCP roots configuration. See: https://modelcontextprotocol.io/docs/concepts/roots\n roots?: Root[];\n}\n\nexport type McpStdioServerConfig = StdioServerParameters & {\n url?: never;\n transport?: never;\n};\n\nexport type McpStreamableHttpConfig = {\n url: string;\n command?: never;\n transport?: never;\n} & Omit<StreamableHTTPClientTransportOptions, 'sessionId'>;\n\nexport type McpTransportServerConfig = {\n transport: Transport;\n command?: never;\n url?: never;\n};\n\n/**\n * Configuration for an individual MCP server. The interface should be familiar\n * and compatible with existing tool configurations e.g. Cursor or Claude\n * Desktop.\n *\n * In addition to stdio servers, remote servers are supported via URL and\n * custom/arbitary transports are supported as well.\n */\nexport type McpServerConfig = (\n | McpStdioServerConfig\n | McpStreamableHttpConfig\n | McpTransportServerConfig\n) &\n McpServerControls;\n\n/**\n * Configuration options for an individual `GenkitMcpClient` instance.\n * This defines how the client connects to a single MCP server and how it behaves.\n */\nexport type McpClientOptions = {\n /** Client name to advertise to the server. */\n name: string;\n /** Name for the server, defaults to the server's advertised name. */\n serverName?: string;\n\n /**\n * An optional version number for this client. This is primarily for logging\n * and identification purposes. Defaults to '1.0.0'.\n */\n version?: string;\n /**\n * If true, tool responses from the MCP server will be returned in their raw\n * MCP format. Otherwise (default), they are processed and potentially\n * simplified for better compatibility with Genkit's typical data structures.\n */\n rawToolResponses?: boolean;\n /** The server configuration to connect. */\n mcpServer: McpServerConfig;\n /** Manually supply a session id for HTTP streaming clients if desired. */\n sessionId?: string;\n};\n\nexport type McpClientOptionsWithCache = McpClientOptions & {\n cacheTtlMillis?: number;\n};\n\n/**\n * Represents a client connection to a single MCP (Model Context Protocol) server.\n * It handles the lifecycle of the connection (connect, disconnect, disable, re-enable, reconnect)\n * and provides methods to fetch tools from the connected server.\n *\n * An instance of `GenkitMcpClient` is typically managed by a `GenkitMcpHost`\n * when dealing with multiple MCP server connections.\n */\nexport class GenkitMcpClient {\n _server?: McpServerRef;\n private _dynamicActionProvider: DynamicActionProviderAction | undefined;\n\n sessionId?: string;\n readonly name: string;\n readonly suppliedServerName?: string;\n private version: string;\n private serverConfig: McpServerConfig;\n private rawToolResponses?: boolean;\n private disabled: boolean;\n private roots?: Root[];\n\n private _readyListeners: {\n resolve: () => void;\n reject: (err: Error) => void;\n }[] = [];\n private _ready = false;\n\n constructor(options: McpClientOptions) {\n this.name = options.name;\n this.suppliedServerName = options.serverName;\n this.version = options.version || '1.0.0';\n this.serverConfig = options.mcpServer;\n this.rawToolResponses = !!options.rawToolResponses;\n this.disabled = !!options.mcpServer.disabled;\n this.roots = options.mcpServer.roots;\n this.sessionId = options.sessionId;\n\n this._initializeConnection();\n }\n\n set dynamicActionProvider(dap: DynamicActionProviderAction) {\n this._dynamicActionProvider = dap;\n }\n\n _invalidateDapCache(): void {\n if (this._dynamicActionProvider) {\n this._dynamicActionProvider.invalidateCache();\n }\n }\n\n get serverName(): string {\n return (\n this.suppliedServerName ??\n this._server?.client.getServerVersion()?.name ??\n 'unknown-server'\n );\n }\n\n async updateRoots(roots: Root[]) {\n this.roots = roots;\n await this._server?.client.sendRootsListChanged();\n this._invalidateDapCache();\n }\n\n /**\n * Sets up a connection based on a provided map of server configurations.\n * - Reconnects existing servers if their configuration appears to have\n * changed (implicitly handled by `connectServer`).\n * - Sets the client's ready state once all connection attempts are complete.\n * @param mcpServers A record mapping server names to their configurations.\n */\n private async _initializeConnection() {\n this._ready = false;\n try {\n await this._connect();\n this._ready = true;\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.resolve();\n }\n } catch (err) {\n while (this._readyListeners.length) {\n this._readyListeners.pop()?.reject(err as Error);\n }\n }\n if (this.roots) {\n await this.updateRoots(this.roots);\n }\n this._invalidateDapCache();\n }\n\n /**\n * Returns a Promise that resolves when the client has attempted to connect\n * to all configured servers, or rejects if a critical error occurs during\n * the initial connection phase.\n */\n async ready() {\n if (this._ready) return;\n return new Promise<void>((resolve, reject) => {\n this._readyListeners.push({ resolve, reject });\n });\n }\n\n /**\n * Connects to a single MCP server defined by the provided configuration.\n * @param config The configuration object for the server.\n */\n private async _connect() {\n if (this._server) await this._server.transport.close();\n this._invalidateDapCache();\n logger.debug(\n `[MCP Client] Connecting MCP server '${this.serverName}' in client '${this.name}'.`\n );\n\n const { transport, type: transportType } = await transportFrom(\n this.serverConfig,\n this.sessionId\n );\n if (!transport) {\n throw new GenkitError({\n status: 'INVALID_ARGUMENT',\n message: `[MCP Client] Could not determine valid transport config from supplied options.`,\n });\n }\n\n let error: string | undefined;\n\n const client = new Client(\n { name: this.name, version: this.version },\n { capabilities: { roots: { listChanged: true } } }\n );\n client.setRequestHandler(ListRootsRequestSchema, () => {\n logger.debug(`[MCP Client] fetching roots for ${this.name}`);\n return { roots: this.roots || [] };\n });\n\n try {\n await client.connect(transport);\n } catch (e) {\n logger.warn(\n `[MCP Client] Error connecting server via ${transportType} transport: ${e}`\n );\n this.disabled = true;\n error = (e as Error).toString();\n }\n\n this._server = {\n client,\n transport,\n error,\n } as McpServerRef;\n this._invalidateDapCache();\n }\n\n /**\n * Disconnects the MCP server and removes its registration\n * from this client instance.\n */\n async _disconnect() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Disconnecting MCP server in client '${this.name}'.`\n );\n await this._server.client.close();\n this._server = undefined;\n this._invalidateDapCache();\n }\n }\n\n /**\n * Disables a server. Closes the underlying transport and server's configuration. Does nothing if the server is\n * already disabled.\n */\n async disable() {\n if (!this.isEnabled()) return;\n if (this._server) {\n logger.debug(\n `[MCP Client] Disabling MCP server in client '${this.name}'`\n );\n await this._disconnect();\n this.disabled = true;\n this._invalidateDapCache();\n }\n }\n\n /**\n * Whether this client-server connection is enabled or not.\n */\n isEnabled() {\n return !this.disabled;\n }\n\n /**\n * Enables a server connection, including previously disabled ones. Does nothing if the\n * server is not disabled.\n */\n async enable() {\n if (this.isEnabled()) return;\n logger.debug(`[MCP Client] Reenabling MCP server in client '${this.name}'`);\n await this._initializeConnection();\n this.disabled = !!this._server!.error;\n this._invalidateDapCache();\n }\n\n /**\n * Closes and then restarts the transport connection for the specified server.\n * Useful for attempting to recover from connection issues without full\n * reconfiguration.\n */\n async restart() {\n if (this._server) {\n logger.debug(\n `[MCP Client] Restarting connection to MCP server in client '${this.name}'`\n );\n await this._disconnect();\n await this._initializeConnection();\n this._invalidateDapCache();\n }\n }\n\n /**\n * Fetches all tools available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveTools(ai: Genkit): Promise<ToolAction[]> {\n await this.ready();\n let tools: ToolAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.tools)\n tools.push(\n ...(await fetchDynamicTools(ai, this._server.client, {\n rawToolResponses: this.rawToolResponses,\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return tools;\n }\n\n /**\n * Fetches all resources available through this client, if the server\n * configuration is not disabled.\n */\n async getActiveResources(ai: Genkit): Promise<DynamicResourceAction[]> {\n await this.ready();\n let resources: DynamicResourceAction[] = [];\n\n if (this._server) {\n const capabilities = this._server.client.getServerCapabilities();\n if (capabilities?.resources)\n resources.push(\n ...(await fetchDynamicResources(ai, this._server.client, {\n serverName: this.serverName,\n name: this.name,\n }))\n );\n }\n\n return resources;\n }\n\n /**\n * Fetches all active prompts available through this client, if the server\n * configuration supports prompts.\n * @param ai The Genkit instance.\n * @param options Optional prompt generation options.\n * @returns A promise that resolves to an array of ExecutablePrompt.\n */\n async getActivePrompts(\n ai: Genkit,\n options?: PromptGenerateOptions\n ): Promise<ExecutablePrompt[]> {\n if (this._server?.client.getServerCapabilities()?.prompts) {\n return fetchAllPrompts(this._server.client, {\n ai,\n serverName: this.serverName,\n name: this.name,\n options,\n });\n }\n return [];\n }\n\n /**\n * Get the specified prompt as an `ExecutablePrompt` available through this\n * client. If no such prompt is found, return undefined.\n */\n async getPrompt(\n ai: Genkit,\n promptName: string,\n opts?: PromptGenerateOptions\n ): Promise<ExecutablePrompt | undefined> {\n await this.ready();\n\n if (this._server) {\n const capabilities = await this._server.client.getServerCapabilities();\n if (capabilities?.prompts) {\n return await getExecutablePrompt(this._server.client, {\n ai,\n serverName: this.name,\n promptName,\n name: this.name,\n options: opts,\n });\n }\n logger.debug(`[MCP Client] No prompts are found in this MCP server.`);\n }\n return;\n }\n\n /** Returns the underlying MCP SDK client if one has been initialized. */\n get mcpClient(): Client | undefined {\n return this._server?.client;\n }\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import '@modelcontextprotocol/sdk/client/stdio.js';
3
3
  import '@modelcontextprotocol/sdk/shared/transport.js';
4
4
  import { McpServerConfig, GenkitMcpClient } from './client.mjs';
5
5
  import { Root } from '@modelcontextprotocol/sdk/types.js';
6
- import { Genkit, ToolAction, DynamicResourceAction, ExecutablePrompt, PromptGenerateOptions } from 'genkit';
6
+ import { DynamicActionProviderAction, Genkit, ToolAction, DynamicResourceAction, ExecutablePrompt, PromptGenerateOptions } from 'genkit';
7
7
  import '@modelcontextprotocol/sdk/client/index.js';
8
8
  import '@modelcontextprotocol/sdk/client/streamableHttp.js';
9
9
 
@@ -52,6 +52,22 @@ interface McpHostOptions {
52
52
  */
53
53
  roots?: Root[];
54
54
  }
55
+ type McpHostOptionsWithCache = Omit<McpHostOptions, 'name'> & {
56
+ /**
57
+ * A client name for this MCP host. This name is advertised to MCP Servers
58
+ * as the connecting client name.
59
+ */
60
+ name: string;
61
+ /**
62
+ * Cache TTL. The dynamic action provider has a cache for the available actions
63
+ * The default TTL is 3 seconds.
64
+ * The cache will automatically be invalidated if any connections change.
65
+ * Negative = no caching (expect noisy traces and slower resolution)
66
+ * Zero or undefined = use the default 3000 millis (3 seconds)
67
+ * Positive: The number of milliseconds to keep the cache.
68
+ */
69
+ cacheTTLMillis?: number;
70
+ };
55
71
  /**
56
72
  * Manages connections to multiple MCP (Model Context Protocol) servers.
57
73
  * Each server connection is individually configured and managed by an instance of `GenkitMcpClient`.
@@ -66,9 +82,12 @@ declare class GenkitMcpHost {
66
82
  private _clientStates;
67
83
  private _readyListeners;
68
84
  private _ready;
85
+ private _dynamicActionProvider;
69
86
  private roots;
70
87
  rawToolResponses?: boolean;
71
88
  constructor(options: McpHostOptions);
89
+ set dynamicActionProvider(dap: DynamicActionProviderAction);
90
+ _invalidateCache(): void;
72
91
  /**
73
92
  * Returns a Promise that resolves when the host has attempted to connect
74
93
  * to all configured clients, or rejects if a critical error occurs during
@@ -199,4 +218,4 @@ declare class GenkitMcpHost {
199
218
  getClient(name: string): GenkitMcpClient;
200
219
  }
201
220
 
202
- export { GenkitMcpHost, type McpHostOptions };
221
+ export { GenkitMcpHost, type McpHostOptions, type McpHostOptionsWithCache };
@@ -3,7 +3,7 @@ import '@modelcontextprotocol/sdk/client/stdio.js';
3
3
  import '@modelcontextprotocol/sdk/shared/transport.js';
4
4
  import { McpServerConfig, GenkitMcpClient } from './client.js';
5
5
  import { Root } from '@modelcontextprotocol/sdk/types.js';
6
- import { Genkit, ToolAction, DynamicResourceAction, ExecutablePrompt, PromptGenerateOptions } from 'genkit';
6
+ import { DynamicActionProviderAction, Genkit, ToolAction, DynamicResourceAction, ExecutablePrompt, PromptGenerateOptions } from 'genkit';
7
7
  import '@modelcontextprotocol/sdk/client/index.js';
8
8
  import '@modelcontextprotocol/sdk/client/streamableHttp.js';
9
9
 
@@ -52,6 +52,22 @@ interface McpHostOptions {
52
52
  */
53
53
  roots?: Root[];
54
54
  }
55
+ type McpHostOptionsWithCache = Omit<McpHostOptions, 'name'> & {
56
+ /**
57
+ * A client name for this MCP host. This name is advertised to MCP Servers
58
+ * as the connecting client name.
59
+ */
60
+ name: string;
61
+ /**
62
+ * Cache TTL. The dynamic action provider has a cache for the available actions
63
+ * The default TTL is 3 seconds.
64
+ * The cache will automatically be invalidated if any connections change.
65
+ * Negative = no caching (expect noisy traces and slower resolution)
66
+ * Zero or undefined = use the default 3000 millis (3 seconds)
67
+ * Positive: The number of milliseconds to keep the cache.
68
+ */
69
+ cacheTTLMillis?: number;
70
+ };
55
71
  /**
56
72
  * Manages connections to multiple MCP (Model Context Protocol) servers.
57
73
  * Each server connection is individually configured and managed by an instance of `GenkitMcpClient`.
@@ -66,9 +82,12 @@ declare class GenkitMcpHost {
66
82
  private _clientStates;
67
83
  private _readyListeners;
68
84
  private _ready;
85
+ private _dynamicActionProvider;
69
86
  private roots;
70
87
  rawToolResponses?: boolean;
71
88
  constructor(options: McpHostOptions);
89
+ set dynamicActionProvider(dap: DynamicActionProviderAction);
90
+ _invalidateCache(): void;
72
91
  /**
73
92
  * Returns a Promise that resolves when the host has attempted to connect
74
93
  * to all configured clients, or rejects if a critical error occurs during
@@ -199,4 +218,4 @@ declare class GenkitMcpHost {
199
218
  getClient(name: string): GenkitMcpClient;
200
219
  }
201
220
 
202
- export { GenkitMcpHost, type McpHostOptions };
221
+ export { GenkitMcpHost, type McpHostOptions, type McpHostOptionsWithCache };
@@ -29,6 +29,7 @@ class GenkitMcpHost {
29
29
  _clientStates = {};
30
30
  _readyListeners = [];
31
31
  _ready = false;
32
+ _dynamicActionProvider;
32
33
  roots;
33
34
  rawToolResponses;
34
35
  constructor(options) {
@@ -41,6 +42,14 @@ class GenkitMcpHost {
41
42
  this._ready = true;
42
43
  }
43
44
  }
45
+ set dynamicActionProvider(dap) {
46
+ this._dynamicActionProvider = dap;
47
+ }
48
+ _invalidateCache() {
49
+ if (this._dynamicActionProvider) {
50
+ this._dynamicActionProvider.invalidateCache();
51
+ }
52
+ }
44
53
  /**
45
54
  * Returns a Promise that resolves when the host has attempted to connect
46
55
  * to all configured clients, or rejects if a critical error occurs during
@@ -90,6 +99,7 @@ class GenkitMcpHost {
90
99
  detail: `Details: ${e}`
91
100
  });
92
101
  }
102
+ this._invalidateCache();
93
103
  }
94
104
  /**
95
105
  * Disconnects the specified MCP server and removes its registration
@@ -115,6 +125,7 @@ class GenkitMcpHost {
115
125
  });
116
126
  }
117
127
  delete this._clients[serverName];
128
+ this._invalidateCache();
118
129
  }
119
130
  /**
120
131
  * Temporarily disables a server connection. Closes the underlying transport
@@ -136,6 +147,7 @@ class GenkitMcpHost {
136
147
  `[MCP Host] Disabling MCP server '${serverName}' in host '${this.name}'`
137
148
  );
138
149
  await client.disable();
150
+ this._invalidateCache();
139
151
  }
140
152
  /**
141
153
  * Enables a server connection, including previously disabled ones. Attempts to reconnect
@@ -160,6 +172,7 @@ class GenkitMcpHost {
160
172
  detail: `Details: ${e}`
161
173
  });
162
174
  }
175
+ this._invalidateCache();
163
176
  }
164
177
  /**
165
178
  * Closes and then restarts the transport connection for the specified server.
@@ -185,6 +198,7 @@ class GenkitMcpHost {
185
198
  detail: `Details: ${e}`
186
199
  });
187
200
  }
201
+ this._invalidateCache();
188
202
  }
189
203
  /**
190
204
  * Updates the connections based on a provided map of server configurations.
@@ -217,6 +231,7 @@ class GenkitMcpHost {
217
231
  this._readyListeners.pop()?.reject(err);
218
232
  }
219
233
  });
234
+ this._invalidateCache();
220
235
  }
221
236
  /**
222
237
  * Retrieves all tools from all connected and enabled MCP clients managed by
@@ -360,6 +375,7 @@ class GenkitMcpHost {
360
375
  for (const client of Object.values(this._clients)) {
361
376
  await client._disconnect();
362
377
  }
378
+ this._invalidateCache();
363
379
  }
364
380
  /** Helper method to track and log client errors. */
365
381
  setError(serverName, error) {