diff --git a/common/src/mcp/client.ts b/common/src/mcp/client.ts index 5a5608d57f..e9bf89d994 100644 --- a/common/src/mcp/client.ts +++ b/common/src/mcp/client.ts @@ -65,6 +65,7 @@ function hashConfig(config: MCPConfig): string { type: 'http', url: config.url, params: config.params, + headers: config.headers, }) } if (config.type === 'sse') { @@ -72,6 +73,7 @@ function hashConfig(config: MCPConfig): string { type: 'sse', url: config.url, params: config.params, + headers: config.headers, }) } config.type satisfies never @@ -231,3 +233,25 @@ export async function callMCPTool( } satisfies ToolResultOutput }) } + +/** + * Close an MCP client connection and clean up associated resources. + * + * Removes the client from the running clients registry and clears any cached + * tool listings. The underlying transport (stdio child process, HTTP/SSE + * connection) is also torn down via the MCP SDK's client.close() method. + * + * Safe to call on an already-closed or unknown client id (no-ops silently). + */ +export async function closeMCPClient(clientId: string): Promise { + const client = runningClients[clientId] + if (client) { + try { + await client.close() + } catch { + // Ignore errors during close — the transport may already be dead. + } + delete runningClients[clientId] + } + delete listToolsCache[clientId] +} diff --git a/sdk/src/run.ts b/sdk/src/run.ts index 4ed4f1321f..9b190deaf5 100644 --- a/sdk/src/run.ts +++ b/sdk/src/run.ts @@ -11,6 +11,7 @@ import { getMCPClient, listMCPTools, callMCPTool, + closeMCPClient, } from '@codebuff/common/mcp/client' import { COMPOSIO_META_TOOL_NAMES, @@ -809,8 +810,9 @@ async function handleToolCall({ // Handle MCP tool calls when mcpConfig is present if (action.mcpConfig) { + let mcpClientId: string | undefined try { - const mcpClientId = await getMCPClient(action.mcpConfig) + mcpClientId = await getMCPClient(action.mcpConfig) const result = await callMCPTool( mcpClientId, { @@ -822,6 +824,11 @@ async function handleToolCall({ ) return { output: result } } catch (error) { + // Clean up the dead client so the next call reconnects with a fresh + // transport rather than hitting the same broken connection. + if (mcpClientId) { + closeMCPClient(mcpClientId).catch(() => {}) + } return { output: [ {