@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.
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { Root } from '@modelcontextprotocol/sdk/types.js';
18
18
  import {
19
+ type DynamicActionProviderAction,
19
20
  type DynamicResourceAction,
20
21
  type ExecutablePrompt,
21
22
  type Genkit,
@@ -57,6 +58,24 @@ export interface McpHostOptions {
57
58
  roots?: Root[];
58
59
  }
59
60
 
61
+ export type McpHostOptionsWithCache = Omit<McpHostOptions, 'name'> & {
62
+ /**
63
+ * A client name for this MCP host. This name is advertised to MCP Servers
64
+ * as the connecting client name.
65
+ */
66
+ name: string;
67
+
68
+ /**
69
+ * Cache TTL. The dynamic action provider has a cache for the available actions
70
+ * The default TTL is 3 seconds.
71
+ * The cache will automatically be invalidated if any connections change.
72
+ * Negative = no caching (expect noisy traces and slower resolution)
73
+ * Zero or undefined = use the default 3000 millis (3 seconds)
74
+ * Positive: The number of milliseconds to keep the cache.
75
+ */
76
+ cacheTTLMillis?: number;
77
+ };
78
+
60
79
  /** Internal representation of client state for logging. */
61
80
  interface ClientState {
62
81
  error?: {
@@ -82,6 +101,7 @@ export class GenkitMcpHost {
82
101
  reject: (err: Error) => void;
83
102
  }[] = [];
84
103
  private _ready = false;
104
+ private _dynamicActionProvider: DynamicActionProviderAction | undefined;
85
105
  private roots: Root[] | undefined;
86
106
  rawToolResponses?: boolean;
87
107
 
@@ -97,6 +117,16 @@ export class GenkitMcpHost {
97
117
  }
98
118
  }
99
119
 
120
+ set dynamicActionProvider(dap: DynamicActionProviderAction) {
121
+ this._dynamicActionProvider = dap;
122
+ }
123
+
124
+ _invalidateCache(): void {
125
+ if (this._dynamicActionProvider) {
126
+ this._dynamicActionProvider.invalidateCache();
127
+ }
128
+ }
129
+
100
130
  /**
101
131
  * Returns a Promise that resolves when the host has attempted to connect
102
132
  * to all configured clients, or rejects if a critical error occurs during
@@ -148,6 +178,7 @@ export class GenkitMcpHost {
148
178
  detail: `Details: ${e}`,
149
179
  });
150
180
  }
181
+ this._invalidateCache();
151
182
  }
152
183
 
153
184
  /**
@@ -175,6 +206,7 @@ export class GenkitMcpHost {
175
206
  });
176
207
  }
177
208
  delete this._clients[serverName];
209
+ this._invalidateCache();
178
210
  }
179
211
 
180
212
  /**
@@ -198,6 +230,7 @@ export class GenkitMcpHost {
198
230
  `[MCP Host] Disabling MCP server '${serverName}' in host '${this.name}'`
199
231
  );
200
232
  await client.disable();
233
+ this._invalidateCache();
201
234
  }
202
235
 
203
236
  /**
@@ -224,6 +257,7 @@ export class GenkitMcpHost {
224
257
  detail: `Details: ${e}`,
225
258
  });
226
259
  }
260
+ this._invalidateCache();
227
261
  }
228
262
 
229
263
  /**
@@ -251,6 +285,7 @@ export class GenkitMcpHost {
251
285
  detail: `Details: ${e}`,
252
286
  });
253
287
  }
288
+ this._invalidateCache();
254
289
  }
255
290
 
256
291
  /**
@@ -290,6 +325,8 @@ export class GenkitMcpHost {
290
325
  this._readyListeners.pop()?.reject(err);
291
326
  }
292
327
  });
328
+
329
+ this._invalidateCache();
293
330
  }
294
331
 
295
332
  /**
@@ -446,6 +483,7 @@ export class GenkitMcpHost {
446
483
  for (const client of Object.values(this._clients)) {
447
484
  await client._disconnect();
448
485
  }
486
+ this._invalidateCache();
449
487
  }
450
488
 
451
489
  /** Helper method to track and log client errors. */
@@ -18,11 +18,16 @@ import { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.
18
18
  import { StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';
19
19
  import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
20
20
  import { GenkitMcpClient, McpClientOptions } from './client.js';
21
- import { GenkitMcpHost, McpHostOptions } from './host.js';
21
+ import {
22
+ GenkitMcpHost,
23
+ McpHostOptions,
24
+ McpHostOptionsWithCache,
25
+ } from './host.js';
22
26
  export { GenkitMcpClient, GenkitMcpHost };
23
27
  export type {
24
28
  McpClientOptions,
25
29
  McpHostOptions,
30
+ McpHostOptionsWithCache,
26
31
  SSEClientTransportOptions,
27
32
  StdioServerParameters,
28
33
  Transport,
package/src/index.ts CHANGED
@@ -18,16 +18,23 @@ import type { Genkit } from 'genkit';
18
18
  import {
19
19
  GenkitMcpClient,
20
20
  McpClientOptions,
21
+ McpClientOptionsWithCache,
21
22
  McpServerConfig,
22
23
  McpStdioServerConfig,
23
24
  } from './client/client.js';
24
- import { GenkitMcpHost, McpHostOptions } from './client/index.js';
25
+ import {
26
+ GenkitMcpHost,
27
+ McpHostOptions,
28
+ McpHostOptionsWithCache,
29
+ } from './client/index.js';
25
30
  import { GenkitMcpServer } from './server.js';
26
31
  export {
27
32
  GenkitMcpClient,
28
33
  GenkitMcpHost,
29
34
  type McpClientOptions,
35
+ type McpClientOptionsWithCache,
30
36
  type McpHostOptions,
37
+ type McpHostOptionsWithCache,
31
38
  type McpServerConfig,
32
39
  type McpStdioServerConfig,
33
40
  };
@@ -67,6 +74,47 @@ export function createMcpHost(options: McpHostOptions) {
67
74
  return new GenkitMcpHost(options);
68
75
  }
69
76
 
77
+ /**
78
+ * Creates an MCP Client Host that connects to one or more MCP servers.
79
+ * Each server is defined in the `mcpClients` option, where the key is a
80
+ * client-side name for the server and the value is the server's configuration.
81
+ *
82
+ * By default, all servers in the config will be attempted to connect unless
83
+ * their configuration includes `{disabled: true}`.
84
+ *
85
+ * ```ts
86
+ * const clientHost = defineMcpHost(ai, {
87
+ * name: "my-mcp-client-host", // Name for the host itself
88
+ * mcpServers: {
89
+ * // Each key is a name for this client/server configuration
90
+ * // Each value is an McpServerConfig object
91
+ * gitToolServer: { command: "uvx", args: ["mcp-server-git"] },
92
+ * customApiServer: { url: "http://localhost:1234/mcp" }
93
+ * }
94
+ * });
95
+ * ```
96
+ *
97
+ * @param options Configuration for the MCP Client Host, including the definitions of MCP servers to connect to.
98
+ * @returns A new instance of GenkitMcpHost.
99
+ */
100
+ export function defineMcpHost(ai: Genkit, options: McpHostOptionsWithCache) {
101
+ const mcpHost = new GenkitMcpHost(options);
102
+ const dap = ai.defineDynamicActionProvider(
103
+ {
104
+ name: options.name,
105
+ cacheConfig: {
106
+ ttlMillis: options.cacheTTLMillis,
107
+ },
108
+ },
109
+ async () => ({
110
+ tool: await mcpHost.getActiveTools(ai),
111
+ resource: await mcpHost.getActiveResources(ai),
112
+ })
113
+ );
114
+ mcpHost.dynamicActionProvider = dap;
115
+ return mcpHost;
116
+ }
117
+
70
118
  /**
71
119
  * Creates an MCP Client that connects to a single MCP server.
72
120
  * This is useful when you only need to interact with one MCP server,
@@ -91,6 +139,55 @@ export function createMcpClient(options: McpClientOptions) {
91
139
  return new GenkitMcpClient(options);
92
140
  }
93
141
 
142
+ /**
143
+ * Defines an MCP Client that connects to a single MCP server.
144
+ * This is useful when you only need to interact with one MCP server,
145
+ * or if you want to manage client instances individually.
146
+ *
147
+ * ```ts
148
+ * const client = defineMcpClient(ai, {
149
+ * name: "mySingleMcpClient", // A name for this client instance
150
+ * command: "npx", // Example: Launching a local server
151
+ * args: ["-y", "@modelcontextprotocol/server-everything", "/path/to/allowed/dir"],
152
+ * });
153
+ *
154
+ * // To get tools from this client:
155
+ * // const tools = await client.getActiveTools(ai);
156
+ *
157
+ * // Or in a generate call you can use:
158
+ * ai.generate({
159
+ prompt: `<a prompt requiring tools>`,
160
+ tools: ['mySingleMcpClient:tool/*'],
161
+ });
162
+ * ```
163
+ *
164
+ * @param options Configuration for the MCP Client, defining how it connects
165
+ * to the MCP server and its behavior.
166
+ * @returns A new instance of GenkitMcpClient.
167
+ */
168
+ export function defineMcpClient(
169
+ ai: Genkit,
170
+ options: McpClientOptionsWithCache
171
+ ) {
172
+ const mcpClient = new GenkitMcpClient(options);
173
+ const dap = ai.defineDynamicActionProvider(
174
+ {
175
+ name: options.name,
176
+ cacheConfig: {
177
+ ttlMillis: options.cacheTtlMillis,
178
+ },
179
+ },
180
+ async () => {
181
+ return {
182
+ tool: await mcpClient.getActiveTools(ai),
183
+ resource: await mcpClient.getActiveResources(ai),
184
+ };
185
+ }
186
+ );
187
+ mcpClient.dynamicActionProvider = dap;
188
+ return mcpClient;
189
+ }
190
+
94
191
  /**
95
192
  * Creates an MCP server based on the supplied Genkit instance. All tools and prompts
96
193
  * will be automatically converted to MCP compatibility.
package/src/server.ts CHANGED
@@ -444,7 +444,7 @@ function toMcpResourceMessage(
444
444
  if (!url.startsWith('data:'))
445
445
  throw new GenkitError({
446
446
  status: 'UNIMPLEMENTED',
447
- message: `[MCP Server] MCP prompt messages only support base64 data images.`,
447
+ message: `[MCP Server] MCP resource messages only support base64 data images.`,
448
448
  });
449
449
  const mimeType =
450
450
  contentType || url.substring(url.indexOf(':')! + 1, url.indexOf(';'));
@@ -455,7 +455,7 @@ function toMcpResourceMessage(
455
455
  } else {
456
456
  throw new GenkitError({
457
457
  status: 'UNIMPLEMENTED',
458
- message: `[MCP Server] MCP prompt messages only support media and text parts.`,
458
+ message: `[MCP Server] MCP resource messages only support media and text parts.`,
459
459
  });
460
460
  }
461
461
  });