Skip to content

fix: forward server timeout to MCP SDK Client requests#21

Open
provos wants to merge 2 commits intouniversal-tool-calling-protocol:mainfrom
provos:fix/forward-timeout-to-mcp-sdk-client
Open

fix: forward server timeout to MCP SDK Client requests#21
provos wants to merge 2 commits intouniversal-tool-calling-protocol:mainfrom
provos:fix/forward-timeout-to-mcp-sdk-client

Conversation

@provos
Copy link
Copy Markdown

@provos provos commented Feb 22, 2026

Problem

_withSession wraps MCP operations in Promise.race using serverConfig.timeout, but callTool() and listTools() call the MCP SDK Client without a per-request timeout option. The SDK's DEFAULT_REQUEST_TIMEOUT_MSEC (60s) therefore fires before the configured server timeout, making it impossible for callers to use timeouts longer than 60s.

This affects any use case where MCP tool calls may take longer than 60 seconds (e.g., interactive approval flows, long-running computations).

Fix

Extract _getTimeoutMs() helper and pass the configured timeout to client.callTool() and client.listTools() request options so the per-request timeout matches the server configuration.

Test

Tested with a slow MCP server (65s delay per call) and serverConfig.timeout = 120:

Scenario Before After
Direct SDK Client, no timeout, 65s delay FAILED at 60s FAILED at 60s
UTCP callTool, timeout=120s, 65s delay FAILED at 60s OK at 65s
UTCP callTool, timeout=1s, 3s delay FAILED at 1s FAILED at 1s

Existing unit tests pass (bun test packages/mcp/tests/).


Summary by cubic

Forward the configured server timeout to MCP SDK client requests so listTools and callTool respect timeouts longer than 60s. Long-running tools now align with serverConfig.timeout.

  • Bug Fixes
    • Pass serverConfig.timeout (ms) to client.listTools(...) and client.callTool(...).
    • Added _getTimeoutMs() helper and reused it in _withSession.
    • Default timeout remains 30s when not set.
    • Added a unit test verifying timeout forwarding for both methods.

Written for commit 75a3044. Summary will update on new commits.

callTool() and listTools() were called without a per-request timeout,
so the SDK's 60s default always fires before serverConfig.timeout.
Pass the configured timeout through to the SDK request options.
Copilot AI review requested due to automatic review settings February 22, 2026 01:23
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes MCP request timeout handling so UTCP’s configured serverConfig.timeout is also applied to the underlying @modelcontextprotocol/sdk client requests, preventing the SDK’s default 60s request timeout from firing first when longer server timeouts are configured.

Changes:

  • Added a _getTimeoutMs() helper to centralize converting MCP server timeout seconds → milliseconds (defaulting to 30s).
  • Updated _withSession() to use _getTimeoutMs() rather than inline timeout calculation.
  • Passed { timeout: requestTimeout } into client.listTools() and client.callTool() request options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +221 to 224
const requestTimeout = this._getTimeoutMs(serverConfig);
const mcpToolsResult = await this._withSession(serverName, serverConfig, mcpCallTemplate.auth,
(client) => client.listTools()
(client) => client.listTools(undefined, { timeout: requestTimeout })
);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior forwards a per-request timeout into the MCP SDK (listTools/callTool options). This is the core fix but isn’t covered by the existing MCP protocol tests. Please add a unit test that asserts the SDK client receives the expected timeout option (e.g., by monkeypatching _getOrCreateSession to return a fake client and asserting the passed options), so regressions don’t silently reintroduce the 60s default timeout issue.

Copilot uses AI. Check for mistakes.
Comment on lines +303 to 306
const requestTimeout = this._getTimeoutMs(serverConfig);
const result = await this._withSession(serverName, serverConfig, mcpCallTemplate.auth,
(client) => client.callTool({ name: actualToolName, arguments: toolArgs })
(client) => client.callTool({ name: actualToolName, arguments: toolArgs }, undefined, { timeout: requestTimeout })
);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as listTools, callTool is now relying on passing a per-request timeout into the MCP SDK. Please add a unit test that verifies the timeout option is forwarded for tool execution as well (ideally without a long-running real server, by using a fake McpClient and asserting the arguments).

Copilot uses AI. Check for mistakes.
@h3xxit h3xxit self-requested a review March 20, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants