Summary
packages/opencode/src/mcp/index.ts calls client.callTool with resetTimeoutOnProgress: true but does not pass an onprogress callback. Per the official MCP TypeScript SDK (Protocol.request), a progressToken is added to the outgoing _meta field only when options.onprogress is truthy. As a result:
- OpenCode never tells the MCP server it wants progress
- The server (correctly per MCP spec) emits no progress notifications
resetTimeoutOnProgress: true has nothing to reset- The client's transport timeout fires on any tool call that legitimately runs longer than the default (~60s)
This silently bricks any long-running MCP tool.
Repro
I hit this on od_generate_design from open-design-mcp generating a full landing page (~15–30k tokens through Sonnet 4.6, ~3–5 minutes). The server's own 600s timeout never fires — OpenCode aborts with MCP error -32001: Request timed out after ~60s, server-side partial-recovery never runs because the response never reaches the client.
Detailed analysis with spec citations and SDK source links: nano-step/open-design-mcp#33
Fix
In packages/opencode/src/mcp/index.ts around line 169, add an onprogress callback to callTool:
returnclient.callTool({name: mcpTool.name,arguments: ... },CallToolResultSchema,{resetTimeoutOnProgress: true,onprogress: (_progress)=>{// No-op is fine — the SDK only needs SOMETHING here to add a// progressToken to _meta. Servers then emit progress against// that token, the SDK matches it, and resetTimeoutOnProgress// does the timeout reset.},
timeout,},)Even a no-op callback unlocks the keepalive — the SDK uses the callback's presence (not its return value) to decide whether to send progressToken. Forwarding progress to the user's UI / status bar is a nice-to-have on top.
Why this matters
Without this fix:
- Long-running MCP tools (
generate, analyze, migrate, batch ops) appear broken in OpenCode but work fine in other MCP clients - MCP server authors get bug reports for client-side issues
- Server-side timeout config (
OD_GENERATE_TIMEOUT_MS, etc.) is silently moot — the client times out first
Verification
Once fixed, any MCP server that emits notifications/progress on long calls (the standard pattern) will keep OpenCode's request alive. I can re-test against open-design-mcp@>=0.12.2 if helpful.
Summary
packages/opencode/src/mcp/index.tscallsclient.callToolwithresetTimeoutOnProgress: truebut does not pass anonprogresscallback. Per the official MCP TypeScript SDK (Protocol.request), aprogressTokenis added to the outgoing_metafield only whenoptions.onprogressis truthy. As a result:resetTimeoutOnProgress: truehas nothing to resetThis silently bricks any long-running MCP tool.
Repro
I hit this on
od_generate_designfromopen-design-mcpgenerating a full landing page (~15–30k tokens through Sonnet 4.6, ~3–5 minutes). The server's own 600s timeout never fires — OpenCode aborts withMCP error -32001: Request timed outafter ~60s, server-side partial-recovery never runs because the response never reaches the client.Detailed analysis with spec citations and SDK source links: nano-step/open-design-mcp#33
Fix
In
packages/opencode/src/mcp/index.tsaround line 169, add anonprogresscallback tocallTool:Even a no-op callback unlocks the keepalive — the SDK uses the callback's presence (not its return value) to decide whether to send
progressToken. Forwarding progress to the user's UI / status bar is a nice-to-have on top.Why this matters
Without this fix:
generate,analyze,migrate, batch ops) appear broken in OpenCode but work fine in other MCP clientsOD_GENERATE_TIMEOUT_MS, etc.) is silently moot — the client times out firstVerification
Once fixed, any MCP server that emits
notifications/progresson long calls (the standard pattern) will keep OpenCode's request alive. I can re-test againstopen-design-mcp@>=0.12.2if helpful.