@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/client/client.d.mts +9 -6
- package/lib/client/client.d.ts +9 -6
- package/lib/client/client.js +19 -7
- package/lib/client/client.js.map +1 -1
- package/lib/client/client.mjs +19 -7
- package/lib/client/client.mjs.map +1 -1
- package/lib/client/host.d.mts +11 -8
- package/lib/client/host.d.ts +11 -8
- package/lib/client/host.js +4 -1
- package/lib/client/host.js.map +1 -1
- package/lib/client/host.mjs +4 -1
- package/lib/client/host.mjs.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs.map +1 -1
- package/lib/server.d.mts +4 -4
- package/lib/server.d.ts +4 -4
- package/lib/util/tools.d.mts +5 -3
- package/lib/util/tools.d.ts +5 -3
- package/lib/util/tools.js +152 -23
- package/lib/util/tools.js.map +1 -1
- package/lib/util/tools.mjs +156 -24
- package/lib/util/tools.mjs.map +1 -1
- package/package.json +3 -3
- package/src/client/client.ts +38 -14
- package/src/client/host.ts +23 -9
- package/src/index.ts +16 -9
- package/src/util/tools.ts +204 -35
- package/tests/host_test.ts +151 -1
- package/tests/tools_test.ts +201 -0
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}'
|
|
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
|
-
|
|
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 || "",
|
package/lib/util/tools.js.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;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"]}
|
package/lib/util/tools.mjs
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import {
|
|
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}'
|
|
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
|
-
|
|
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 || "",
|
package/lib/util/tools.mjs.map
CHANGED
|
@@ -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":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"genai",
|
|
10
10
|
"generative-ai"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.
|
|
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.
|
|
34
|
-
"genkit": "^1.
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
34
|
+
"genkit": "^1.35.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"get-port": "^5.1.0",
|
package/src/client/client.ts
CHANGED
|
@@ -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 =
|
|
118
|
-
|
|
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(
|
|
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
|
-
|
|
352
|
-
|
|
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
|
|
382
|
+
return [] as unknown as (Multipart extends true
|
|
383
|
+
? MultipartToolAction
|
|
384
|
+
: ToolAction)[];
|
|
361
385
|
}
|
|
362
386
|
|
|
363
387
|
/**
|