6090508bad
- Add curly braces to all if/else/for/while statements - Fix indentation and trailing spaces - Auto-fixed 372 linting errors using eslint --fix - Remaining issues are warnings only (non-null assertions, explicit any types)
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
/**
|
|
* MCP Bridge — converts MCP tools into Flynn's Tool interface.
|
|
*
|
|
* Each MCP tool is prefixed with "mcp:<serverName>:" 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<ToolResult> {
|
|
try {
|
|
const result = await client.callTool(
|
|
toolInfo.name,
|
|
(args ?? {}) as Record<string, unknown>,
|
|
);
|
|
|
|
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));
|
|
}
|