diff options
| author | Dax Raad <[email protected]> | 2025-12-30 23:06:07 -0500 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-12-30 23:06:07 -0500 |
| commit | ed4ce67cdc24e4e9324a00cf176a45ccd267a584 (patch) | |
| tree | 79645363948d66ded086b602d9972f0f1973bf54 | |
| parent | 94dca309e93dc85b22eb3fc2b2ea9481580269d5 (diff) | |
| download | opencode-ed4ce67cdc24e4e9324a00cf176a45ccd267a584.tar.gz opencode-ed4ce67cdc24e4e9324a00cf176a45ccd267a584.zip | |
core: add configurable timeout for MCP tool calls to prevent hanging requests
| -rw-r--r-- | packages/opencode/src/config/config.ts | 6 | ||||
| -rw-r--r-- | packages/opencode/src/mcp/index.ts | 26 |
2 files changed, 25 insertions, 7 deletions
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index f7c55d6f0..86576667b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -844,6 +844,12 @@ export namespace Config { .optional() .describe("Tools that should only be available to primary agents."), continue_loop_on_deny: z.boolean().optional().describe("Continue the agent loop when a tool call is denied"), + mcp_timeout: z + .number() + .int() + .positive() + .optional() + .describe("Timeout in milliseconds for model context protocol (MCP) requests"), }) .optional(), }) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 8ee54a4bf..bb25d7d1c 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -4,7 +4,11 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" -import { type Tool as MCPToolDef, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js" +import { + CallToolResultSchema, + type Tool as MCPToolDef, + ToolListChangedNotificationSchema, +} from "@modelcontextprotocol/sdk/types.js" import { Config } from "../config/config" import { Log } from "../util/log" import { NamedError } from "@opencode-ai/util/error" @@ -93,7 +97,7 @@ export namespace MCP { } // Convert MCP tool definition to AI SDK Tool type - function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient): Tool { + async function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient): Promise<Tool> { const inputSchema = mcpTool.inputSchema // Spread first, then override type to ensure it's always "object" @@ -103,15 +107,23 @@ export namespace MCP { properties: (inputSchema.properties ?? {}) as JSONSchema7["properties"], additionalProperties: false, } + const config = await Config.get() return dynamicTool({ description: mcpTool.description ?? "", inputSchema: jsonSchema(schema), execute: async (args: unknown) => { - return client.callTool({ - name: mcpTool.name, - arguments: args as Record<string, unknown>, - }) + return client.callTool( + { + name: mcpTool.name, + arguments: args as Record<string, unknown>, + }, + CallToolResultSchema, + { + resetTimeoutOnProgress: true, + timeout: config.experimental?.mcp_timeout, + }, + ) }, }) } @@ -474,7 +486,7 @@ export namespace MCP { for (const mcpTool of toolsResult.tools) { const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_") const sanitizedToolName = mcpTool.name.replace(/[^a-zA-Z0-9_-]/g, "_") - result[sanitizedClientName + "_" + sanitizedToolName] = convertMcpTool(mcpTool, client) + result[sanitizedClientName + "_" + sanitizedToolName] = await convertMcpTool(mcpTool, client) } } return result |
