@genkit-ai/mcp 1.33.0 → 1.35.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/util/tools.js CHANGED
@@ -27,6 +27,9 @@ var import_logging = require("genkit/logging");
27
27
  const toText = (c) => c.map((p) => p.type === "text" ? p.text : "").join("");
28
28
  function processResult(result) {
29
29
  if (result.isError) return { error: toText(result.content) };
30
+ if (result.structuredContent !== void 0) {
31
+ return result.structuredContent;
32
+ }
30
33
  if (result.content.every((c) => c.type === "text" && !!c.text)) {
31
34
  const text = toText(result.content);
32
35
  if (text.trim().startsWith("{") || text.trim().startsWith("[")) {
@@ -41,34 +44,160 @@ function processResult(result) {
41
44
  if (result.content.length === 1) return result.content[0];
42
45
  return result;
43
46
  }
47
+ function processMultipartResult(result) {
48
+ if (result.isError) {
49
+ return {
50
+ output: { error: toText(result.content) },
51
+ metadata: result._meta
52
+ };
53
+ }
54
+ const content = [];
55
+ let textOutput = "";
56
+ for (const c of result.content) {
57
+ if (c.type === "text") {
58
+ if (c.text) {
59
+ textOutput += c.text;
60
+ }
61
+ } else if (c.type === "image" || c.type === "audio") {
62
+ if (c.data) {
63
+ content.push({
64
+ media: {
65
+ url: `data:${c.mimeType};base64,${c.data}`,
66
+ contentType: c.mimeType
67
+ }
68
+ });
69
+ }
70
+ } else if (c.type === "resource_link") {
71
+ if (c.uri) {
72
+ content.push({
73
+ resource: {
74
+ uri: c.uri
75
+ }
76
+ });
77
+ }
78
+ } else if (c.type === "resource") {
79
+ if (c.resource) {
80
+ if ("text" in c.resource && c.resource.text) {
81
+ textOutput += (textOutput ? "\n\n" : "") + `Resource (${c.resource.uri}):
82
+ ${c.resource.text}`;
83
+ } else if ("blob" in c.resource && c.resource.blob) {
84
+ content.push({
85
+ media: {
86
+ url: `data:${c.resource.mimeType};base64,${c.resource.blob}`,
87
+ contentType: c.resource.mimeType
88
+ }
89
+ });
90
+ }
91
+ }
92
+ }
93
+ }
94
+ let output = result.structuredContent;
95
+ if (output === void 0 && textOutput) {
96
+ if (textOutput.trim().startsWith("{") || textOutput.trim().startsWith("[")) {
97
+ try {
98
+ output = JSON.parse(textOutput);
99
+ } catch (e) {
100
+ output = textOutput;
101
+ }
102
+ } else {
103
+ output = textOutput;
104
+ }
105
+ }
106
+ return {
107
+ ...output !== void 0 ? { output } : {},
108
+ ...content.length > 0 ? { content } : {},
109
+ metadata: result._meta
110
+ };
111
+ }
44
112
  function registerTool(ai, client, tool, params) {
45
113
  import_logging.logger.debug(
46
- `[MCP] Registering tool '${params.name}/${tool.name}'' from server '${params.serverName}'`
47
- );
48
- ai.defineTool(
49
- {
50
- name: `${params.serverName}/${tool.name}`,
51
- description: tool.description || "",
52
- inputJsonSchema: tool.inputSchema,
53
- outputSchema: import_genkit.z.any(),
54
- metadata: { mcp: { _meta: tool._meta || {} } }
55
- },
56
- async (args) => {
57
- import_logging.logger.debug(
58
- `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
59
- JSON.stringify(args)
60
- );
61
- const result = await client.callTool({
62
- name: tool.name,
63
- arguments: args
64
- });
65
- if (params.rawToolResponses) return result;
66
- return processResult(result);
67
- }
114
+ `[MCP] Registering tool '${params.name}/${tool.name}' from server '${params.serverName}'`
68
115
  );
116
+ if (params.multipart && params.rawToolResponses) {
117
+ import_logging.logger.warn(
118
+ `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`
119
+ );
120
+ }
121
+ if (params.multipart) {
122
+ ai.defineTool(
123
+ {
124
+ name: `${params.serverName}/${tool.name}`,
125
+ description: tool.description || "",
126
+ inputJsonSchema: tool.inputSchema,
127
+ outputSchema: import_genkit.z.any(),
128
+ metadata: { mcp: { _meta: tool._meta || {} } },
129
+ multipart: true
130
+ },
131
+ async (args, { context }) => {
132
+ import_logging.logger.debug(
133
+ `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
134
+ JSON.stringify(args)
135
+ );
136
+ const result = await client.callTool({
137
+ name: tool.name,
138
+ arguments: args,
139
+ _meta: context?.mcp?._meta
140
+ });
141
+ if (params.rawToolResponses) return { output: result };
142
+ return processMultipartResult(result);
143
+ }
144
+ );
145
+ } else {
146
+ ai.defineTool(
147
+ {
148
+ name: `${params.serverName}/${tool.name}`,
149
+ description: tool.description || "",
150
+ inputJsonSchema: tool.inputSchema,
151
+ outputSchema: import_genkit.z.any(),
152
+ metadata: { mcp: { _meta: tool._meta || {} } }
153
+ },
154
+ async (args, { context }) => {
155
+ import_logging.logger.debug(
156
+ `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
157
+ JSON.stringify(args)
158
+ );
159
+ const result = await client.callTool({
160
+ name: tool.name,
161
+ arguments: args,
162
+ _meta: context?.mcp?._meta
163
+ });
164
+ if (params.rawToolResponses) return result;
165
+ return processResult(result);
166
+ }
167
+ );
168
+ }
69
169
  }
70
170
  function createDynamicTool(ai, client, tool, params) {
71
- return ai.dynamicTool(
171
+ if (params.multipart && params.rawToolResponses) {
172
+ import_logging.logger.warn(
173
+ `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`
174
+ );
175
+ }
176
+ if (params.multipart) {
177
+ return (0, import_genkit.tool)(
178
+ {
179
+ name: `${params.serverName}/${tool.name}`,
180
+ description: tool.description || "",
181
+ inputJsonSchema: tool.inputSchema,
182
+ outputSchema: import_genkit.z.any(),
183
+ metadata: { mcp: { _meta: tool._meta || {} } },
184
+ multipart: true
185
+ },
186
+ async (args, { context }) => {
187
+ import_logging.logger.debug(
188
+ `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`
189
+ );
190
+ const result = await client.callTool({
191
+ name: tool.name,
192
+ arguments: args,
193
+ _meta: context?.mcp?._meta
194
+ });
195
+ if (params.rawToolResponses) return { output: result };
196
+ return processMultipartResult(result);
197
+ }
198
+ );
199
+ }
200
+ return (0, import_genkit.tool)(
72
201
  {
73
202
  name: `${params.serverName}/${tool.name}`,
74
203
  description: tool.description || "",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/tools.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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type {\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport { JSONSchema7, z, type Genkit, type ToolAction } from 'genkit';\nimport { logger } from 'genkit/logging';\n\nconst toText = (c: CallToolResult['content']) =>\n c.map((p) => (p.type === 'text' ? p.text : '')).join('');\n\nfunction processResult(result: CallToolResult) {\n if (result.isError) return { error: toText(result.content) };\n if (result.content.every((c) => c.type === 'text' && !!c.text)) {\n const text = toText(result.content);\n if (text.trim().startsWith('{') || text.trim().startsWith('[')) {\n try {\n return JSON.parse(text);\n } catch (e) {\n return text;\n }\n }\n return text;\n }\n if (result.content.length === 1) return result.content[0];\n return result;\n}\n\n/**\n * Registers a single MCP tool as a Genkit tool.\n * It defines a new Genkit tool action that, when called, will\n * interact with the MCP client to execute the corresponding MCP tool.\n *\n * @param ai The Genkit instance to define the tool on.\n * @param client The MCP client instance used to interact with the MCP server.\n * @param tool The MCP Tool object to register.\n * @param params Contains the Genkit client name, MCP server name for namespacing,\n * and a flag for raw tool responses.\n */\nfunction registerTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: { serverName: string; name: string; rawToolResponses?: boolean }\n) {\n logger.debug(\n `[MCP] Registering tool '${params.name}/${tool.name}'' from server '${params.serverName}'`\n );\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n}\n\n/**\n * Creates a Genkit dynamic tool action for a given MCP tool.\n * This is similar to `registerTool` but returns the `ToolAction` directly\n * instead of defining it on the Genkit instance.\n *\n * @param ai The Genkit instance, used for creating the dynamic tool.\n * @param client The MCP client instance.\n * @param tool The MCP Tool object.\n * @param params Configuration parameters including namespacing and raw response flag.\n * @returns A Genkit `ToolAction` representing the MCP tool.\n */\nfunction createDynamicTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: { serverName: string; name: string; rawToolResponses?: boolean }\n): ToolAction {\n return ai.dynamicTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n}\n\n/**\n * Lookup all tools available in the server and register each as a Genkit tool.\n */\nexport async function registerAllTools(\n ai: Genkit,\n client: Client,\n params: { name: string; serverName: string; rawToolResponses?: boolean }\n): Promise<void> {\n let cursor: string | undefined;\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n tools.forEach((t) => registerTool(ai, client, t, params));\n cursor = nextCursor;\n if (!cursor) break;\n }\n}\n\n/**\n * Lookup all tools available in the server and fetches as a Genkit dynamic tool.\n */\nexport async function fetchDynamicTools(\n ai: Genkit,\n client: Client,\n params: { name: string; serverName: string; rawToolResponses?: boolean }\n): Promise<ToolAction[]> {\n let cursor: string | undefined;\n let allTools: ToolAction[] = [];\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n allTools.push(\n ...tools.map((t) => createDynamicTool(ai, client, t, params))\n );\n cursor = nextCursor;\n if (!cursor) break;\n }\n return allTools;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBA,oBAA6D;AAC7D,qBAAuB;AAEvB,MAAM,SAAS,CAAC,MACd,EAAE,IAAI,CAAC,MAAO,EAAE,SAAS,SAAS,EAAE,OAAO,EAAG,EAAE,KAAK,EAAE;AAEzD,SAAS,cAAc,QAAwB;AAC7C,MAAI,OAAO,QAAS,QAAO,EAAE,OAAO,OAAO,OAAO,OAAO,EAAE;AAC3D,MAAI,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;AAC9D,UAAM,OAAO,OAAO,OAAO,OAAO;AAClC,QAAI,KAAK,KAAK,EAAE,WAAW,GAAG,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG,GAAG;AAC9D,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,SAAS,GAAG;AACV,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,WAAW,EAAG,QAAO,OAAO,QAAQ,CAAC;AACxD,SAAO;AACT;AAaA,SAAS,aACP,IACA,QACA,MACA,QACA;AACA,wBAAO;AAAA,IACL,2BAA2B,OAAO,IAAI,IAAI,KAAK,IAAI,mBAAmB,OAAO,UAAU;AAAA,EACzF;AACA,KAAG;AAAA,IACD;AAAA,MACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,MACvC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK;AAAA,MACtB,cAAc,gBAAE,IAAI;AAAA,MACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,IAC/C;AAAA,IACA,OAAO,SAAS;AACd,4BAAO;AAAA,QACL,2BAA2B,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACzD,KAAK,UAAU,IAAI;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,OAAO,SAAS;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,WAAW;AAAA,MACb,CAAC;AACD,UAAI,OAAO,iBAAkB,QAAO;AACpC,aAAO,cAAc,MAAwB;AAAA,IAC/C;AAAA,EACF;AACF;AAaA,SAAS,kBACP,IACA,QACA,MACA,QACY;AACZ,SAAO,GAAG;AAAA,IACR;AAAA,MACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,MACvC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK;AAAA,MACtB,cAAc,gBAAE,IAAI;AAAA,MACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,IAC/C;AAAA,IACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,4BAAO;AAAA,QACL,uBAAuB,OAAO,UAAU,IAAI,KAAK,IAAI,cAAc,OAAO,IAAI;AAAA,MAChF;AACA,YAAM,SAAS,MAAM,OAAO,SAAS;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,WAAW;AAAA,QACX,OAAO,SAAS,KAAK;AAAA,MACvB,CAAC;AACD,UAAI,OAAO,iBAAkB,QAAO;AACpC,aAAO,cAAc,MAAwB;AAAA,IAC/C;AAAA,EACF;AACF;AAKA,eAAsB,iBACpB,IACA,QACA,QACe;AACf,MAAI;AACJ,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,UAAM,QAAQ,CAAC,MAAM,aAAa,IAAI,QAAQ,GAAG,MAAM,CAAC;AACxD,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACF;AAKA,eAAsB,kBACpB,IACA,QACA,QACuB;AACvB,MAAI;AACJ,MAAI,WAAyB,CAAC;AAC9B,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,aAAS;AAAA,MACP,GAAG,MAAM,IAAI,CAAC,MAAM,kBAAkB,IAAI,QAAQ,GAAG,MAAM,CAAC;AAAA,IAC9D;AACA,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/util/tools.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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type {\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n JSONSchema7,\n tool as genkitTool,\n z,\n type Genkit,\n type MultipartToolAction,\n type Part,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\n\nconst toText = (c: CallToolResult['content']) =>\n c.map((p) => (p.type === 'text' ? p.text : '')).join('');\n\nfunction processResult(result: CallToolResult) {\n if (result.isError) return { error: toText(result.content) };\n if (result.structuredContent !== undefined) {\n return result.structuredContent;\n }\n if (result.content.every((c) => c.type === 'text' && !!c.text)) {\n const text = toText(result.content);\n if (text.trim().startsWith('{') || text.trim().startsWith('[')) {\n try {\n return JSON.parse(text);\n } catch (e) {\n return text;\n }\n }\n return text;\n }\n if (result.content.length === 1) return result.content[0];\n return result;\n}\n\nfunction processMultipartResult(result: CallToolResult) {\n if (result.isError) {\n return {\n output: { error: toText(result.content) },\n metadata: result._meta,\n };\n }\n\n const content: Part[] = [];\n let textOutput = '';\n\n for (const c of result.content) {\n if (c.type === 'text') {\n if (c.text) {\n textOutput += c.text;\n }\n } else if (c.type === 'image' || c.type === 'audio') {\n if (c.data) {\n content.push({\n media: {\n url: `data:${c.mimeType};base64,${c.data}`,\n contentType: c.mimeType,\n },\n });\n }\n } else if (c.type === 'resource_link') {\n if (c.uri) {\n content.push({\n resource: {\n uri: c.uri,\n },\n });\n }\n } else if (c.type === 'resource') {\n if (c.resource) {\n if ('text' in c.resource && c.resource.text) {\n textOutput +=\n (textOutput ? '\\n\\n' : '') +\n `Resource (${c.resource.uri}):\\n${c.resource.text}`;\n } else if ('blob' in c.resource && c.resource.blob) {\n content.push({\n media: {\n url: `data:${c.resource.mimeType};base64,${c.resource.blob}`,\n contentType: c.resource.mimeType,\n },\n });\n }\n }\n }\n }\n\n let output: unknown = result.structuredContent;\n\n if (output === undefined && textOutput) {\n if (\n textOutput.trim().startsWith('{') ||\n textOutput.trim().startsWith('[')\n ) {\n try {\n output = JSON.parse(textOutput);\n } catch (e) {\n output = textOutput;\n }\n } else {\n output = textOutput;\n }\n }\n\n return {\n ...(output !== undefined ? { output } : {}),\n ...(content.length > 0 ? { content } : {}),\n metadata: result._meta,\n };\n}\n\n/**\n * Registers a single MCP tool as a Genkit tool.\n * It defines a new Genkit tool action that, when called, will\n * interact with the MCP client to execute the corresponding MCP tool.\n *\n * @param ai The Genkit instance to define the tool on.\n * @param client The MCP client instance used to interact with the MCP server.\n * @param tool The MCP Tool object to register.\n * @param params Contains the Genkit client name, MCP server name for namespacing,\n * and a flag for raw tool responses.\n */\nfunction registerTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: {\n serverName: string;\n name: string;\n rawToolResponses?: boolean;\n multipart?: boolean;\n }\n) {\n logger.debug(\n `[MCP] Registering tool '${params.name}/${tool.name}' from server '${params.serverName}'`\n );\n if (params.multipart && params.rawToolResponses) {\n logger.warn(\n `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`\n );\n }\n if (params.multipart) {\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n multipart: true as const,\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return { output: result };\n return processMultipartResult(result as CallToolResult);\n }\n );\n } else {\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n }\n}\n\n/**\n * Creates a Genkit dynamic tool action for a given MCP tool.\n * This is similar to `registerTool` but returns the `ToolAction` directly\n * instead of defining it on the Genkit instance.\n *\n * @param ai The Genkit instance, used for creating the dynamic tool.\n * @param client The MCP client instance.\n * @param tool The MCP Tool object.\n * @param params Configuration parameters including namespacing and raw response flag.\n * @returns A Genkit `ToolAction` representing the MCP tool.\n */\nfunction createDynamicTool<Multipart extends boolean = false>(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: {\n serverName: string;\n name: string;\n rawToolResponses?: boolean;\n multipart?: Multipart;\n }\n): Multipart extends true ? MultipartToolAction : ToolAction {\n if (params.multipart && params.rawToolResponses) {\n logger.warn(\n `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`\n );\n }\n if (params.multipart) {\n return genkitTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n multipart: true as const,\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return { output: result };\n return processMultipartResult(result as CallToolResult);\n }\n ) as unknown as Multipart extends true ? MultipartToolAction : ToolAction;\n }\n\n return genkitTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result as CallToolResult;\n return processResult(result as CallToolResult);\n }\n ) as unknown as Multipart extends true ? MultipartToolAction : ToolAction;\n}\n\n/**\n * Lookup all tools available in the server and register each as a Genkit tool.\n */\nexport async function registerAllTools(\n ai: Genkit,\n client: Client,\n params: {\n name: string;\n serverName: string;\n rawToolResponses?: boolean;\n multipart?: boolean;\n }\n): Promise<void> {\n let cursor: string | undefined;\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n tools.forEach((t) => registerTool(ai, client, t, params));\n cursor = nextCursor;\n if (!cursor) break;\n }\n}\n\n/**\n * Lookup all tools available in the server and fetches as a Genkit dynamic tool.\n */\nexport async function fetchDynamicTools<Multipart extends boolean = false>(\n ai: Genkit,\n client: Client,\n params: {\n name: string;\n serverName: string;\n rawToolResponses?: boolean;\n multipart?: Multipart;\n }\n): Promise<(Multipart extends true ? MultipartToolAction : ToolAction)[]> {\n let cursor: string | undefined;\n let allTools: (Multipart extends true ? MultipartToolAction : ToolAction)[] =\n [];\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n allTools.push(\n ...tools.map((t) => createDynamicTool(ai, client, t, params))\n );\n cursor = nextCursor;\n if (!cursor) break;\n }\n return allTools;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBA,oBAQO;AACP,qBAAuB;AAEvB,MAAM,SAAS,CAAC,MACd,EAAE,IAAI,CAAC,MAAO,EAAE,SAAS,SAAS,EAAE,OAAO,EAAG,EAAE,KAAK,EAAE;AAEzD,SAAS,cAAc,QAAwB;AAC7C,MAAI,OAAO,QAAS,QAAO,EAAE,OAAO,OAAO,OAAO,OAAO,EAAE;AAC3D,MAAI,OAAO,sBAAsB,QAAW;AAC1C,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;AAC9D,UAAM,OAAO,OAAO,OAAO,OAAO;AAClC,QAAI,KAAK,KAAK,EAAE,WAAW,GAAG,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG,GAAG;AAC9D,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,SAAS,GAAG;AACV,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,WAAW,EAAG,QAAO,OAAO,QAAQ,CAAC;AACxD,SAAO;AACT;AAEA,SAAS,uBAAuB,QAAwB;AACtD,MAAI,OAAO,SAAS;AAClB,WAAO;AAAA,MACL,QAAQ,EAAE,OAAO,OAAO,OAAO,OAAO,EAAE;AAAA,MACxC,UAAU,OAAO;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,UAAkB,CAAC;AACzB,MAAI,aAAa;AAEjB,aAAW,KAAK,OAAO,SAAS;AAC9B,QAAI,EAAE,SAAS,QAAQ;AACrB,UAAI,EAAE,MAAM;AACV,sBAAc,EAAE;AAAA,MAClB;AAAA,IACF,WAAW,EAAE,SAAS,WAAW,EAAE,SAAS,SAAS;AACnD,UAAI,EAAE,MAAM;AACV,gBAAQ,KAAK;AAAA,UACX,OAAO;AAAA,YACL,KAAK,QAAQ,EAAE,QAAQ,WAAW,EAAE,IAAI;AAAA,YACxC,aAAa,EAAE;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,EAAE,SAAS,iBAAiB;AACrC,UAAI,EAAE,KAAK;AACT,gBAAQ,KAAK;AAAA,UACX,UAAU;AAAA,YACR,KAAK,EAAE;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,EAAE,SAAS,YAAY;AAChC,UAAI,EAAE,UAAU;AACd,YAAI,UAAU,EAAE,YAAY,EAAE,SAAS,MAAM;AAC3C,yBACG,aAAa,SAAS,MACvB,aAAa,EAAE,SAAS,GAAG;AAAA,EAAO,EAAE,SAAS,IAAI;AAAA,QACrD,WAAW,UAAU,EAAE,YAAY,EAAE,SAAS,MAAM;AAClD,kBAAQ,KAAK;AAAA,YACX,OAAO;AAAA,cACL,KAAK,QAAQ,EAAE,SAAS,QAAQ,WAAW,EAAE,SAAS,IAAI;AAAA,cAC1D,aAAa,EAAE,SAAS;AAAA,YAC1B;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAkB,OAAO;AAE7B,MAAI,WAAW,UAAa,YAAY;AACtC,QACE,WAAW,KAAK,EAAE,WAAW,GAAG,KAChC,WAAW,KAAK,EAAE,WAAW,GAAG,GAChC;AACA,UAAI;AACF,iBAAS,KAAK,MAAM,UAAU;AAAA,MAChC,SAAS,GAAG;AACV,iBAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IACzC,GAAI,QAAQ,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxC,UAAU,OAAO;AAAA,EACnB;AACF;AAaA,SAAS,aACP,IACA,QACA,MACA,QAMA;AACA,wBAAO;AAAA,IACL,2BAA2B,OAAO,IAAI,IAAI,KAAK,IAAI,kBAAkB,OAAO,UAAU;AAAA,EACxF;AACA,MAAI,OAAO,aAAa,OAAO,kBAAkB;AAC/C,0BAAO;AAAA,MACL,eAAe,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,WAAW;AACpB,OAAG;AAAA,MACD;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,gBAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,QAC7C,WAAW;AAAA,MACb;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,8BAAO;AAAA,UACL,2BAA2B,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,UACzD,KAAK,UAAU,IAAI;AAAA,QACrB;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO,EAAE,QAAQ,OAAO;AACrD,eAAO,uBAAuB,MAAwB;AAAA,MACxD;AAAA,IACF;AAAA,EACF,OAAO;AACL,OAAG;AAAA,MACD;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,gBAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,MAC/C;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,8BAAO;AAAA,UACL,2BAA2B,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,UACzD,KAAK,UAAU,IAAI;AAAA,QACrB;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO;AACpC,eAAO,cAAc,MAAwB;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;AAaA,SAAS,kBACP,IACA,QACA,MACA,QAM2D;AAC3D,MAAI,OAAO,aAAa,OAAO,kBAAkB;AAC/C,0BAAO;AAAA,MACL,eAAe,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,WAAW;AACpB,eAAO,cAAAA;AAAA,MACL;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,gBAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,QAC7C,WAAW;AAAA,MACb;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,8BAAO;AAAA,UACL,uBAAuB,OAAO,UAAU,IAAI,KAAK,IAAI,cAAc,OAAO,IAAI;AAAA,QAChF;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO,EAAE,QAAQ,OAAO;AACrD,eAAO,uBAAuB,MAAwB;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAEA,aAAO,cAAAA;AAAA,IACL;AAAA,MACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,MACvC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK;AAAA,MACtB,cAAc,gBAAE,IAAI;AAAA,MACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,IAC/C;AAAA,IACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,4BAAO;AAAA,QACL,uBAAuB,OAAO,UAAU,IAAI,KAAK,IAAI,cAAc,OAAO,IAAI;AAAA,MAChF;AACA,YAAM,SAAS,MAAM,OAAO,SAAS;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,WAAW;AAAA,QACX,OAAO,SAAS,KAAK;AAAA,MACvB,CAAC;AACD,UAAI,OAAO,iBAAkB,QAAO;AACpC,aAAO,cAAc,MAAwB;AAAA,IAC/C;AAAA,EACF;AACF;AAKA,eAAsB,iBACpB,IACA,QACA,QAMe;AACf,MAAI;AACJ,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,UAAM,QAAQ,CAAC,MAAM,aAAa,IAAI,QAAQ,GAAG,MAAM,CAAC;AACxD,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACF;AAKA,eAAsB,kBACpB,IACA,QACA,QAMwE;AACxE,MAAI;AACJ,MAAI,WACF,CAAC;AACH,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,aAAS;AAAA,MACP,GAAG,MAAM,IAAI,CAAC,MAAM,kBAAkB,IAAI,QAAQ,GAAG,MAAM,CAAC;AAAA,IAC9D;AACA,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACA,SAAO;AACT;","names":["genkitTool"]}
@@ -1,8 +1,14 @@
1
- import { z } from "genkit";
1
+ import {
2
+ tool as genkitTool,
3
+ z
4
+ } from "genkit";
2
5
  import { logger } from "genkit/logging";
3
6
  const toText = (c) => c.map((p) => p.type === "text" ? p.text : "").join("");
4
7
  function processResult(result) {
5
8
  if (result.isError) return { error: toText(result.content) };
9
+ if (result.structuredContent !== void 0) {
10
+ return result.structuredContent;
11
+ }
6
12
  if (result.content.every((c) => c.type === "text" && !!c.text)) {
7
13
  const text = toText(result.content);
8
14
  if (text.trim().startsWith("{") || text.trim().startsWith("[")) {
@@ -17,34 +23,160 @@ function processResult(result) {
17
23
  if (result.content.length === 1) return result.content[0];
18
24
  return result;
19
25
  }
26
+ function processMultipartResult(result) {
27
+ if (result.isError) {
28
+ return {
29
+ output: { error: toText(result.content) },
30
+ metadata: result._meta
31
+ };
32
+ }
33
+ const content = [];
34
+ let textOutput = "";
35
+ for (const c of result.content) {
36
+ if (c.type === "text") {
37
+ if (c.text) {
38
+ textOutput += c.text;
39
+ }
40
+ } else if (c.type === "image" || c.type === "audio") {
41
+ if (c.data) {
42
+ content.push({
43
+ media: {
44
+ url: `data:${c.mimeType};base64,${c.data}`,
45
+ contentType: c.mimeType
46
+ }
47
+ });
48
+ }
49
+ } else if (c.type === "resource_link") {
50
+ if (c.uri) {
51
+ content.push({
52
+ resource: {
53
+ uri: c.uri
54
+ }
55
+ });
56
+ }
57
+ } else if (c.type === "resource") {
58
+ if (c.resource) {
59
+ if ("text" in c.resource && c.resource.text) {
60
+ textOutput += (textOutput ? "\n\n" : "") + `Resource (${c.resource.uri}):
61
+ ${c.resource.text}`;
62
+ } else if ("blob" in c.resource && c.resource.blob) {
63
+ content.push({
64
+ media: {
65
+ url: `data:${c.resource.mimeType};base64,${c.resource.blob}`,
66
+ contentType: c.resource.mimeType
67
+ }
68
+ });
69
+ }
70
+ }
71
+ }
72
+ }
73
+ let output = result.structuredContent;
74
+ if (output === void 0 && textOutput) {
75
+ if (textOutput.trim().startsWith("{") || textOutput.trim().startsWith("[")) {
76
+ try {
77
+ output = JSON.parse(textOutput);
78
+ } catch (e) {
79
+ output = textOutput;
80
+ }
81
+ } else {
82
+ output = textOutput;
83
+ }
84
+ }
85
+ return {
86
+ ...output !== void 0 ? { output } : {},
87
+ ...content.length > 0 ? { content } : {},
88
+ metadata: result._meta
89
+ };
90
+ }
20
91
  function registerTool(ai, client, tool, params) {
21
92
  logger.debug(
22
- `[MCP] Registering tool '${params.name}/${tool.name}'' from server '${params.serverName}'`
23
- );
24
- ai.defineTool(
25
- {
26
- name: `${params.serverName}/${tool.name}`,
27
- description: tool.description || "",
28
- inputJsonSchema: tool.inputSchema,
29
- outputSchema: z.any(),
30
- metadata: { mcp: { _meta: tool._meta || {} } }
31
- },
32
- async (args) => {
33
- logger.debug(
34
- `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
35
- JSON.stringify(args)
36
- );
37
- const result = await client.callTool({
38
- name: tool.name,
39
- arguments: args
40
- });
41
- if (params.rawToolResponses) return result;
42
- return processResult(result);
43
- }
93
+ `[MCP] Registering tool '${params.name}/${tool.name}' from server '${params.serverName}'`
44
94
  );
95
+ if (params.multipart && params.rawToolResponses) {
96
+ logger.warn(
97
+ `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`
98
+ );
99
+ }
100
+ if (params.multipart) {
101
+ ai.defineTool(
102
+ {
103
+ name: `${params.serverName}/${tool.name}`,
104
+ description: tool.description || "",
105
+ inputJsonSchema: tool.inputSchema,
106
+ outputSchema: z.any(),
107
+ metadata: { mcp: { _meta: tool._meta || {} } },
108
+ multipart: true
109
+ },
110
+ async (args, { context }) => {
111
+ logger.debug(
112
+ `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
113
+ JSON.stringify(args)
114
+ );
115
+ const result = await client.callTool({
116
+ name: tool.name,
117
+ arguments: args,
118
+ _meta: context?.mcp?._meta
119
+ });
120
+ if (params.rawToolResponses) return { output: result };
121
+ return processMultipartResult(result);
122
+ }
123
+ );
124
+ } else {
125
+ ai.defineTool(
126
+ {
127
+ name: `${params.serverName}/${tool.name}`,
128
+ description: tool.description || "",
129
+ inputJsonSchema: tool.inputSchema,
130
+ outputSchema: z.any(),
131
+ metadata: { mcp: { _meta: tool._meta || {} } }
132
+ },
133
+ async (args, { context }) => {
134
+ logger.debug(
135
+ `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,
136
+ JSON.stringify(args)
137
+ );
138
+ const result = await client.callTool({
139
+ name: tool.name,
140
+ arguments: args,
141
+ _meta: context?.mcp?._meta
142
+ });
143
+ if (params.rawToolResponses) return result;
144
+ return processResult(result);
145
+ }
146
+ );
147
+ }
45
148
  }
46
149
  function createDynamicTool(ai, client, tool, params) {
47
- return ai.dynamicTool(
150
+ if (params.multipart && params.rawToolResponses) {
151
+ logger.warn(
152
+ `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`
153
+ );
154
+ }
155
+ if (params.multipart) {
156
+ return genkitTool(
157
+ {
158
+ name: `${params.serverName}/${tool.name}`,
159
+ description: tool.description || "",
160
+ inputJsonSchema: tool.inputSchema,
161
+ outputSchema: z.any(),
162
+ metadata: { mcp: { _meta: tool._meta || {} } },
163
+ multipart: true
164
+ },
165
+ async (args, { context }) => {
166
+ logger.debug(
167
+ `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`
168
+ );
169
+ const result = await client.callTool({
170
+ name: tool.name,
171
+ arguments: args,
172
+ _meta: context?.mcp?._meta
173
+ });
174
+ if (params.rawToolResponses) return { output: result };
175
+ return processMultipartResult(result);
176
+ }
177
+ );
178
+ }
179
+ return genkitTool(
48
180
  {
49
181
  name: `${params.serverName}/${tool.name}`,
50
182
  description: tool.description || "",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/tools.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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type {\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport { JSONSchema7, z, type Genkit, type ToolAction } from 'genkit';\nimport { logger } from 'genkit/logging';\n\nconst toText = (c: CallToolResult['content']) =>\n c.map((p) => (p.type === 'text' ? p.text : '')).join('');\n\nfunction processResult(result: CallToolResult) {\n if (result.isError) return { error: toText(result.content) };\n if (result.content.every((c) => c.type === 'text' && !!c.text)) {\n const text = toText(result.content);\n if (text.trim().startsWith('{') || text.trim().startsWith('[')) {\n try {\n return JSON.parse(text);\n } catch (e) {\n return text;\n }\n }\n return text;\n }\n if (result.content.length === 1) return result.content[0];\n return result;\n}\n\n/**\n * Registers a single MCP tool as a Genkit tool.\n * It defines a new Genkit tool action that, when called, will\n * interact with the MCP client to execute the corresponding MCP tool.\n *\n * @param ai The Genkit instance to define the tool on.\n * @param client The MCP client instance used to interact with the MCP server.\n * @param tool The MCP Tool object to register.\n * @param params Contains the Genkit client name, MCP server name for namespacing,\n * and a flag for raw tool responses.\n */\nfunction registerTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: { serverName: string; name: string; rawToolResponses?: boolean }\n) {\n logger.debug(\n `[MCP] Registering tool '${params.name}/${tool.name}'' from server '${params.serverName}'`\n );\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n}\n\n/**\n * Creates a Genkit dynamic tool action for a given MCP tool.\n * This is similar to `registerTool` but returns the `ToolAction` directly\n * instead of defining it on the Genkit instance.\n *\n * @param ai The Genkit instance, used for creating the dynamic tool.\n * @param client The MCP client instance.\n * @param tool The MCP Tool object.\n * @param params Configuration parameters including namespacing and raw response flag.\n * @returns A Genkit `ToolAction` representing the MCP tool.\n */\nfunction createDynamicTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: { serverName: string; name: string; rawToolResponses?: boolean }\n): ToolAction {\n return ai.dynamicTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n}\n\n/**\n * Lookup all tools available in the server and register each as a Genkit tool.\n */\nexport async function registerAllTools(\n ai: Genkit,\n client: Client,\n params: { name: string; serverName: string; rawToolResponses?: boolean }\n): Promise<void> {\n let cursor: string | undefined;\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n tools.forEach((t) => registerTool(ai, client, t, params));\n cursor = nextCursor;\n if (!cursor) break;\n }\n}\n\n/**\n * Lookup all tools available in the server and fetches as a Genkit dynamic tool.\n */\nexport async function fetchDynamicTools(\n ai: Genkit,\n client: Client,\n params: { name: string; serverName: string; rawToolResponses?: boolean }\n): Promise<ToolAction[]> {\n let cursor: string | undefined;\n let allTools: ToolAction[] = [];\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n allTools.push(\n ...tools.map((t) => createDynamicTool(ai, client, t, params))\n );\n cursor = nextCursor;\n if (!cursor) break;\n }\n return allTools;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/util/tools.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 { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type {\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js' with { 'resolution-mode': 'import' };\nimport {\n JSONSchema7,\n tool as genkitTool,\n z,\n type Genkit,\n type MultipartToolAction,\n type Part,\n type ToolAction,\n} from 'genkit';\nimport { logger } from 'genkit/logging';\n\nconst toText = (c: CallToolResult['content']) =>\n c.map((p) => (p.type === 'text' ? p.text : '')).join('');\n\nfunction processResult(result: CallToolResult) {\n if (result.isError) return { error: toText(result.content) };\n if (result.structuredContent !== undefined) {\n return result.structuredContent;\n }\n if (result.content.every((c) => c.type === 'text' && !!c.text)) {\n const text = toText(result.content);\n if (text.trim().startsWith('{') || text.trim().startsWith('[')) {\n try {\n return JSON.parse(text);\n } catch (e) {\n return text;\n }\n }\n return text;\n }\n if (result.content.length === 1) return result.content[0];\n return result;\n}\n\nfunction processMultipartResult(result: CallToolResult) {\n if (result.isError) {\n return {\n output: { error: toText(result.content) },\n metadata: result._meta,\n };\n }\n\n const content: Part[] = [];\n let textOutput = '';\n\n for (const c of result.content) {\n if (c.type === 'text') {\n if (c.text) {\n textOutput += c.text;\n }\n } else if (c.type === 'image' || c.type === 'audio') {\n if (c.data) {\n content.push({\n media: {\n url: `data:${c.mimeType};base64,${c.data}`,\n contentType: c.mimeType,\n },\n });\n }\n } else if (c.type === 'resource_link') {\n if (c.uri) {\n content.push({\n resource: {\n uri: c.uri,\n },\n });\n }\n } else if (c.type === 'resource') {\n if (c.resource) {\n if ('text' in c.resource && c.resource.text) {\n textOutput +=\n (textOutput ? '\\n\\n' : '') +\n `Resource (${c.resource.uri}):\\n${c.resource.text}`;\n } else if ('blob' in c.resource && c.resource.blob) {\n content.push({\n media: {\n url: `data:${c.resource.mimeType};base64,${c.resource.blob}`,\n contentType: c.resource.mimeType,\n },\n });\n }\n }\n }\n }\n\n let output: unknown = result.structuredContent;\n\n if (output === undefined && textOutput) {\n if (\n textOutput.trim().startsWith('{') ||\n textOutput.trim().startsWith('[')\n ) {\n try {\n output = JSON.parse(textOutput);\n } catch (e) {\n output = textOutput;\n }\n } else {\n output = textOutput;\n }\n }\n\n return {\n ...(output !== undefined ? { output } : {}),\n ...(content.length > 0 ? { content } : {}),\n metadata: result._meta,\n };\n}\n\n/**\n * Registers a single MCP tool as a Genkit tool.\n * It defines a new Genkit tool action that, when called, will\n * interact with the MCP client to execute the corresponding MCP tool.\n *\n * @param ai The Genkit instance to define the tool on.\n * @param client The MCP client instance used to interact with the MCP server.\n * @param tool The MCP Tool object to register.\n * @param params Contains the Genkit client name, MCP server name for namespacing,\n * and a flag for raw tool responses.\n */\nfunction registerTool(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: {\n serverName: string;\n name: string;\n rawToolResponses?: boolean;\n multipart?: boolean;\n }\n) {\n logger.debug(\n `[MCP] Registering tool '${params.name}/${tool.name}' from server '${params.serverName}'`\n );\n if (params.multipart && params.rawToolResponses) {\n logger.warn(\n `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`\n );\n }\n if (params.multipart) {\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n multipart: true as const,\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return { output: result };\n return processMultipartResult(result as CallToolResult);\n }\n );\n } else {\n ai.defineTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] Calling MCP tool '${params.serverName}/${tool.name}' with arguments`,\n JSON.stringify(args)\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result;\n return processResult(result as CallToolResult);\n }\n );\n }\n}\n\n/**\n * Creates a Genkit dynamic tool action for a given MCP tool.\n * This is similar to `registerTool` but returns the `ToolAction` directly\n * instead of defining it on the Genkit instance.\n *\n * @param ai The Genkit instance, used for creating the dynamic tool.\n * @param client The MCP client instance.\n * @param tool The MCP Tool object.\n * @param params Configuration parameters including namespacing and raw response flag.\n * @returns A Genkit `ToolAction` representing the MCP tool.\n */\nfunction createDynamicTool<Multipart extends boolean = false>(\n ai: Genkit,\n client: Client,\n tool: Tool,\n params: {\n serverName: string;\n name: string;\n rawToolResponses?: boolean;\n multipart?: Multipart;\n }\n): Multipart extends true ? MultipartToolAction : ToolAction {\n if (params.multipart && params.rawToolResponses) {\n logger.warn(\n `[MCP] Tool '${params.serverName}/${tool.name}' is configured with both multipart and rawToolResponses. Genkit will return the raw MCP CallToolResult in the output field, and media parts will not be natively parsed.`\n );\n }\n if (params.multipart) {\n return genkitTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n multipart: true as const,\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return { output: result };\n return processMultipartResult(result as CallToolResult);\n }\n ) as unknown as Multipart extends true ? MultipartToolAction : ToolAction;\n }\n\n return genkitTool(\n {\n name: `${params.serverName}/${tool.name}`,\n description: tool.description || '',\n inputJsonSchema: tool.inputSchema as JSONSchema7,\n outputSchema: z.any(),\n metadata: { mcp: { _meta: tool._meta || {} } },\n },\n async (args, { context }) => {\n logger.debug(\n `[MCP] calling tool '${params.serverName}/${tool.name}' in host '${params.name}'`\n );\n const result = await client.callTool({\n name: tool.name,\n arguments: args,\n _meta: context?.mcp?._meta,\n });\n if (params.rawToolResponses) return result as CallToolResult;\n return processResult(result as CallToolResult);\n }\n ) as unknown as Multipart extends true ? MultipartToolAction : ToolAction;\n}\n\n/**\n * Lookup all tools available in the server and register each as a Genkit tool.\n */\nexport async function registerAllTools(\n ai: Genkit,\n client: Client,\n params: {\n name: string;\n serverName: string;\n rawToolResponses?: boolean;\n multipart?: boolean;\n }\n): Promise<void> {\n let cursor: string | undefined;\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n tools.forEach((t) => registerTool(ai, client, t, params));\n cursor = nextCursor;\n if (!cursor) break;\n }\n}\n\n/**\n * Lookup all tools available in the server and fetches as a Genkit dynamic tool.\n */\nexport async function fetchDynamicTools<Multipart extends boolean = false>(\n ai: Genkit,\n client: Client,\n params: {\n name: string;\n serverName: string;\n rawToolResponses?: boolean;\n multipart?: Multipart;\n }\n): Promise<(Multipart extends true ? MultipartToolAction : ToolAction)[]> {\n let cursor: string | undefined;\n let allTools: (Multipart extends true ? MultipartToolAction : ToolAction)[] =\n [];\n while (true) {\n const { nextCursor, tools } = await client.listTools({ cursor });\n allTools.push(\n ...tools.map((t) => createDynamicTool(ai, client, t, params))\n );\n cursor = nextCursor;\n if (!cursor) break;\n }\n return allTools;\n}\n"],"mappings":"AAqBA;AAAA,EAEE,QAAQ;AAAA,EACR;AAAA,OAKK;AACP,SAAS,cAAc;AAEvB,MAAM,SAAS,CAAC,MACd,EAAE,IAAI,CAAC,MAAO,EAAE,SAAS,SAAS,EAAE,OAAO,EAAG,EAAE,KAAK,EAAE;AAEzD,SAAS,cAAc,QAAwB;AAC7C,MAAI,OAAO,QAAS,QAAO,EAAE,OAAO,OAAO,OAAO,OAAO,EAAE;AAC3D,MAAI,OAAO,sBAAsB,QAAW;AAC1C,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG;AAC9D,UAAM,OAAO,OAAO,OAAO,OAAO;AAClC,QAAI,KAAK,KAAK,EAAE,WAAW,GAAG,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG,GAAG;AAC9D,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,SAAS,GAAG;AACV,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,WAAW,EAAG,QAAO,OAAO,QAAQ,CAAC;AACxD,SAAO;AACT;AAEA,SAAS,uBAAuB,QAAwB;AACtD,MAAI,OAAO,SAAS;AAClB,WAAO;AAAA,MACL,QAAQ,EAAE,OAAO,OAAO,OAAO,OAAO,EAAE;AAAA,MACxC,UAAU,OAAO;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,UAAkB,CAAC;AACzB,MAAI,aAAa;AAEjB,aAAW,KAAK,OAAO,SAAS;AAC9B,QAAI,EAAE,SAAS,QAAQ;AACrB,UAAI,EAAE,MAAM;AACV,sBAAc,EAAE;AAAA,MAClB;AAAA,IACF,WAAW,EAAE,SAAS,WAAW,EAAE,SAAS,SAAS;AACnD,UAAI,EAAE,MAAM;AACV,gBAAQ,KAAK;AAAA,UACX,OAAO;AAAA,YACL,KAAK,QAAQ,EAAE,QAAQ,WAAW,EAAE,IAAI;AAAA,YACxC,aAAa,EAAE;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,EAAE,SAAS,iBAAiB;AACrC,UAAI,EAAE,KAAK;AACT,gBAAQ,KAAK;AAAA,UACX,UAAU;AAAA,YACR,KAAK,EAAE;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,EAAE,SAAS,YAAY;AAChC,UAAI,EAAE,UAAU;AACd,YAAI,UAAU,EAAE,YAAY,EAAE,SAAS,MAAM;AAC3C,yBACG,aAAa,SAAS,MACvB,aAAa,EAAE,SAAS,GAAG;AAAA,EAAO,EAAE,SAAS,IAAI;AAAA,QACrD,WAAW,UAAU,EAAE,YAAY,EAAE,SAAS,MAAM;AAClD,kBAAQ,KAAK;AAAA,YACX,OAAO;AAAA,cACL,KAAK,QAAQ,EAAE,SAAS,QAAQ,WAAW,EAAE,SAAS,IAAI;AAAA,cAC1D,aAAa,EAAE,SAAS;AAAA,YAC1B;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAkB,OAAO;AAE7B,MAAI,WAAW,UAAa,YAAY;AACtC,QACE,WAAW,KAAK,EAAE,WAAW,GAAG,KAChC,WAAW,KAAK,EAAE,WAAW,GAAG,GAChC;AACA,UAAI;AACF,iBAAS,KAAK,MAAM,UAAU;AAAA,MAChC,SAAS,GAAG;AACV,iBAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IACzC,GAAI,QAAQ,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxC,UAAU,OAAO;AAAA,EACnB;AACF;AAaA,SAAS,aACP,IACA,QACA,MACA,QAMA;AACA,SAAO;AAAA,IACL,2BAA2B,OAAO,IAAI,IAAI,KAAK,IAAI,kBAAkB,OAAO,UAAU;AAAA,EACxF;AACA,MAAI,OAAO,aAAa,OAAO,kBAAkB;AAC/C,WAAO;AAAA,MACL,eAAe,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,WAAW;AACpB,OAAG;AAAA,MACD;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,EAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,QAC7C,WAAW;AAAA,MACb;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,eAAO;AAAA,UACL,2BAA2B,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,UACzD,KAAK,UAAU,IAAI;AAAA,QACrB;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO,EAAE,QAAQ,OAAO;AACrD,eAAO,uBAAuB,MAAwB;AAAA,MACxD;AAAA,IACF;AAAA,EACF,OAAO;AACL,OAAG;AAAA,MACD;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,EAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,MAC/C;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,eAAO;AAAA,UACL,2BAA2B,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,UACzD,KAAK,UAAU,IAAI;AAAA,QACrB;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO;AACpC,eAAO,cAAc,MAAwB;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;AAaA,SAAS,kBACP,IACA,QACA,MACA,QAM2D;AAC3D,MAAI,OAAO,aAAa,OAAO,kBAAkB;AAC/C,WAAO;AAAA,MACL,eAAe,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,WAAW;AACpB,WAAO;AAAA,MACL;AAAA,QACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,QACvC,aAAa,KAAK,eAAe;AAAA,QACjC,iBAAiB,KAAK;AAAA,QACtB,cAAc,EAAE,IAAI;AAAA,QACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,QAC7C,WAAW;AAAA,MACb;AAAA,MACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,eAAO;AAAA,UACL,uBAAuB,OAAO,UAAU,IAAI,KAAK,IAAI,cAAc,OAAO,IAAI;AAAA,QAChF;AACA,cAAM,SAAS,MAAM,OAAO,SAAS;AAAA,UACnC,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,OAAO,SAAS,KAAK;AAAA,QACvB,CAAC;AACD,YAAI,OAAO,iBAAkB,QAAO,EAAE,QAAQ,OAAO;AACrD,eAAO,uBAAuB,MAAwB;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM,GAAG,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,MACvC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK;AAAA,MACtB,cAAc,EAAE,IAAI;AAAA,MACpB,UAAU,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,IAC/C;AAAA,IACA,OAAO,MAAM,EAAE,QAAQ,MAAM;AAC3B,aAAO;AAAA,QACL,uBAAuB,OAAO,UAAU,IAAI,KAAK,IAAI,cAAc,OAAO,IAAI;AAAA,MAChF;AACA,YAAM,SAAS,MAAM,OAAO,SAAS;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,WAAW;AAAA,QACX,OAAO,SAAS,KAAK;AAAA,MACvB,CAAC;AACD,UAAI,OAAO,iBAAkB,QAAO;AACpC,aAAO,cAAc,MAAwB;AAAA,IAC/C;AAAA,EACF;AACF;AAKA,eAAsB,iBACpB,IACA,QACA,QAMe;AACf,MAAI;AACJ,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,UAAM,QAAQ,CAAC,MAAM,aAAa,IAAI,QAAQ,GAAG,MAAM,CAAC;AACxD,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACF;AAKA,eAAsB,kBACpB,IACA,QACA,QAMwE;AACxE,MAAI;AACJ,MAAI,WACF,CAAC;AACH,SAAO,MAAM;AACX,UAAM,EAAE,YAAY,MAAM,IAAI,MAAM,OAAO,UAAU,EAAE,OAAO,CAAC;AAC/D,aAAS;AAAA,MACP,GAAG,MAAM,IAAI,CAAC,MAAM,kBAAkB,IAAI,QAAQ,GAAG,MAAM,CAAC;AAAA,IAC9D;AACA,aAAS;AACT,QAAI,CAAC,OAAQ;AAAA,EACf;AACA,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "genai",
10
10
  "generative-ai"
11
11
  ],
12
- "version": "1.33.0",
12
+ "version": "1.35.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",
@@ -30,8 +30,8 @@
30
30
  "author": "genkit",
31
31
  "license": "Apache-2.0",
32
32
  "peerDependencies": {
33
- "@modelcontextprotocol/sdk": "^1.13.0",
34
- "genkit": "^1.33.0"
33
+ "@modelcontextprotocol/sdk": "^1.29.0",
34
+ "genkit": "^1.35.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "get-port": "^5.1.0",
@@ -28,6 +28,7 @@ import {
28
28
  type DynamicResourceAction,
29
29
  type ExecutablePrompt,
30
30
  type Genkit,
31
+ type MultipartToolAction,
31
32
  type PromptGenerateOptions,
32
33
  type ToolAction,
33
34
  } from 'genkit';
@@ -91,7 +92,7 @@ export type McpServerConfig = (
91
92
  * Configuration options for an individual `GenkitMcpClient` instance.
92
93
  * This defines how the client connects to a single MCP server and how it behaves.
93
94
  */
94
- export type McpClientOptions = {
95
+ export type McpClientOptions<M extends boolean = false> = {
95
96
  /** Client name to advertise to the server. */
96
97
  name: string;
97
98
  /** Name for the server, defaults to the server's advertised name. */
@@ -108,15 +109,18 @@ export type McpClientOptions = {
108
109
  * simplified for better compatibility with Genkit's typical data structures.
109
110
  */
110
111
  rawToolResponses?: boolean;
112
+ /** If true, tools will be registered as multipart tool.v2 actions. */
113
+ multipart?: M;
111
114
  /** The server configuration to connect. */
112
115
  mcpServer: McpServerConfig;
113
116
  /** Manually supply a session id for HTTP streaming clients if desired. */
114
117
  sessionId?: string;
115
118
  };
116
119
 
117
- export type McpClientOptionsWithCache = McpClientOptions & {
118
- cacheTtlMillis?: number;
119
- };
120
+ export type McpClientOptionsWithCache<M extends boolean = false> =
121
+ McpClientOptions<M> & {
122
+ cacheTtlMillis?: number;
123
+ };
120
124
 
121
125
  /**
122
126
  * Represents a client connection to a single MCP (Model Context Protocol) server.
@@ -126,7 +130,7 @@ export type McpClientOptionsWithCache = McpClientOptions & {
126
130
  * An instance of `GenkitMcpClient` is typically managed by a `GenkitMcpHost`
127
131
  * when dealing with multiple MCP server connections.
128
132
  */
129
- export class GenkitMcpClient {
133
+ export class GenkitMcpClient<Multipart extends boolean = false> {
130
134
  _server?: McpServerRef;
131
135
  private _dynamicActionProvider: DynamicActionProviderAction | undefined;
132
136
 
@@ -136,6 +140,7 @@ export class GenkitMcpClient {
136
140
  private version: string;
137
141
  private serverConfig: McpServerConfig;
138
142
  private rawToolResponses?: boolean;
143
+ private multipart?: boolean;
139
144
  private disabled: boolean;
140
145
  private roots?: Root[];
141
146
 
@@ -145,12 +150,13 @@ export class GenkitMcpClient {
145
150
  }[] = [];
146
151
  private _ready = false;
147
152
 
148
- constructor(options: McpClientOptions) {
153
+ constructor(options: McpClientOptions<Multipart>) {
149
154
  this.name = options.name;
150
155
  this.suppliedServerName = options.serverName;
151
156
  this.version = options.version || '1.0.0';
152
157
  this.serverConfig = options.mcpServer;
153
158
  this.rawToolResponses = !!options.rawToolResponses;
159
+ this.multipart = !!options.multipart;
154
160
  this.disabled = !!options.mcpServer.disabled;
155
161
  this.roots = options.mcpServer.roots;
156
162
  this.sessionId = options.sessionId;
@@ -341,23 +347,41 @@ export class GenkitMcpClient {
341
347
  * Fetches all tools available through this client, if the server
342
348
  * configuration is not disabled.
343
349
  */
344
- async getActiveTools(ai: Genkit): Promise<ToolAction[]> {
350
+ async getActiveTools(
351
+ ai: Genkit
352
+ ): Promise<(Multipart extends true ? MultipartToolAction : ToolAction)[]> {
345
353
  await this.ready();
346
- let tools: ToolAction[] = [];
347
354
 
348
355
  if (this._server) {
349
356
  const capabilities = this._server.client.getServerCapabilities();
350
- if (capabilities?.tools)
351
- tools.push(
352
- ...(await fetchDynamicTools(ai, this._server.client, {
357
+ if (capabilities?.tools) {
358
+ if (this.multipart) {
359
+ const tools = await fetchDynamicTools(ai, this._server.client, {
353
360
  rawToolResponses: this.rawToolResponses,
361
+ multipart: true,
354
362
  serverName: this.serverName,
355
363
  name: this.name,
356
- }))
357
- );
364
+ });
365
+ return tools as unknown as (Multipart extends true
366
+ ? MultipartToolAction
367
+ : ToolAction)[];
368
+ } else {
369
+ const tools = await fetchDynamicTools(ai, this._server.client, {
370
+ rawToolResponses: this.rawToolResponses,
371
+ multipart: false,
372
+ serverName: this.serverName,
373
+ name: this.name,
374
+ });
375
+ return tools as unknown as (Multipart extends true
376
+ ? MultipartToolAction
377
+ : ToolAction)[];
378
+ }
379
+ }
358
380
  }
359
381
 
360
- return tools;
382
+ return [] as unknown as (Multipart extends true
383
+ ? MultipartToolAction
384
+ : ToolAction)[];
361
385
  }
362
386
 
363
387
  /**