diff --git a/src/config/schema.ts b/src/config/schema.ts index a2f36b1..23379fb 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -9,6 +9,8 @@ import { z } from "zod"; const accessControlFields = { allowTools: z.array(z.string()).optional(), blockTools: z.array(z.string()).optional(), + /** Per-service tool call timeout in milliseconds. Overrides MCP2CLI_TOOL_TIMEOUT env var. */ + timeout: z.number().int().positive().optional(), }; /** diff --git a/src/daemon/server.ts b/src/daemon/server.ts index d2d7a49..dd654f7 100644 --- a/src/daemon/server.ts +++ b/src/daemon/server.ts @@ -107,8 +107,11 @@ export function createDaemonServer(opts: DaemonServerOptions) { callTool = body.tool; const conn = await pool.getConnection(body.service, getConfig()); - // MEM-02: AbortSignal timeout on tool calls (default 30s, configurable) - const timeout = parseInt(process.env.MCP2CLI_TOOL_TIMEOUT ?? "30000", 10); + // MEM-02: AbortSignal timeout on tool calls + // Priority: per-service config > MCP2CLI_TOOL_TIMEOUT env > 30s default + const serviceConfig = getConfig().services[body.service]; + const perServiceTimeout = serviceConfig && "timeout" in serviceConfig ? serviceConfig.timeout : undefined; + const timeout = perServiceTimeout ?? parseInt(process.env.MCP2CLI_TOOL_TIMEOUT ?? "30000", 10); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeout);