{"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":[]}