/** * MCP Bridge — converts MCP tools into Flynn's Tool interface. * * Each MCP tool is prefixed with "mcp::" to avoid * namespace collisions with builtin tools. When the tool is executed, * the bridge routes the call back to the originating McpClient. */ import type { Tool, ToolResult } from '../tools/types.js'; import type { McpClient } from './client.js'; import type { McpToolInfo } from './types.js'; /** * Create the prefixed tool name used in Flynn's tool registry. * * Example: MCP server "filesystem" with tool "read_file" -> "mcp:filesystem:read_file" */ export function mcpToolName(serverName: string, toolName: string): string { return `mcp:${serverName}:${toolName}`; } /** * Parse a prefixed tool name back into server + tool components. * Returns null if the name doesn't match the mcp: prefix pattern. */ export function parseMcpToolName(prefixedName: string): { serverName: string; toolName: string } | null { const match = prefixedName.match(/^mcp:([^:]+):(.+)$/); if (!match) return null; return { serverName: match[1], toolName: match[2] }; } /** * Convert a single MCP tool into a Flynn Tool. * * The returned Tool's execute() calls back into the McpClient * to invoke the tool on the remote MCP server. */ export function bridgeMcpTool(client: McpClient, toolInfo: McpToolInfo): Tool { const prefixedName = mcpToolName(client.serverName, toolInfo.name); return { name: prefixedName, description: `[MCP:${client.serverName}] ${toolInfo.description}`, inputSchema: { type: 'object', properties: toolInfo.inputSchema.properties ?? {}, required: toolInfo.inputSchema.required, }, async execute(args: unknown): Promise { try { const result = await client.callTool( toolInfo.name, (args ?? {}) as Record, ); return { success: !result.isError, output: result.content, error: result.isError ? result.content : undefined, }; } catch (error) { return { success: false, output: '', error: error instanceof Error ? error.message : String(error), }; } }, }; } /** * Bridge all tools from an MCP client into Flynn Tool objects. */ export function bridgeAllTools(client: McpClient): Tool[] { return client.tools.map((t) => bridgeMcpTool(client, t)); }