Skip to content

Make MCP client initialization timeout configurable via ToolOptions#212

Open
matiazo wants to merge 2 commits intomicrosoft:mainfrom
matiazo:feature/configurable-mcp-timeout
Open

Make MCP client initialization timeout configurable via ToolOptions#212
matiazo wants to merge 2 commits intomicrosoft:mainfrom
matiazo:feature/configurable-mcp-timeout

Conversation

@matiazo
Copy link

@matiazo matiazo commented Mar 10, 2026

Summary

  • Add nullable McpClientInitializationTimeoutSeconds property to ToolOptions so consumers can override the MCP client initialization timeout
  • When not set (null), the MCP SDK default (60s) is used — no behavioral change for existing consumers
  • When set, both McpClientOptions.InitializationTimeout and HttpClient.Timeout are configured to match

Motivation

Consumers connecting to the MCP platform may experience OperationCanceledException when downstream dependencies are slow. The MCP SDK's default 60-second initialization timeout is not configurable through ToolOptions, leaving no way for consumers to adjust it for their agents.

Usage

// Default behavior — no change needed, SDK default timeout applies
var options = new ToolOptions();
await configService.GetMcpClientToolsAsync(turnContext, serverConfig, token, options);

// Custom timeout for slow environments
var options = new ToolOptions
{
    McpClientInitializationTimeoutSeconds = 180 // 3 minutes
};
await configService.GetMcpClientToolsAsync(turnContext, serverConfig, token, options);

Changes

  • src/Tooling/Core/Models/ToolOptions.cs — Add int? McpClientInitializationTimeoutSeconds property
  • src/Tooling/Core/Services/McpToolServerConfigurationService.cs — Apply timeout only when explicitly set

Test plan

  • Verified default behavior (null) uses MCP SDK default timeout
  • Verified explicit value is applied to both McpClientOptions.InitializationTimeout and HttpClient.Timeout
  • Built and tested end-to-end with sample-agent against MCP platform

Generated with Claude Code

Add nullable McpClientInitializationTimeoutSeconds property to ToolOptions
so consumers can override the MCP client initialization timeout. When null
(default), the MCP SDK default is used. When set, both McpClientOptions
and HttpClient.Timeout are configured to match.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Copy link
Contributor

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 adds an opt-in configuration surface to the Tooling layer so SDK consumers can override the MCP client initialization timeout (handshake/connection setup) via ToolOptions, helping environments where MCP initialization regularly exceeds the SDK default.

Changes:

  • Added ToolOptions.McpClientInitializationTimeoutSeconds (nullable) to allow overriding MCP initialization timeout.
  • When set, applies the timeout to both HttpClient.Timeout and McpClientOptions.InitializationTimeout during MCP client creation.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/Tooling/Core/Models/ToolOptions.cs Introduces a new nullable option to configure MCP client initialization timeout (in seconds).
src/Tooling/Core/Services/McpToolServerConfigurationService.cs Applies the configured timeout when creating the HTTP client and MCP client options for MCP client initialization.

Comment on lines +506 to +510
// Apply custom timeout only when explicitly configured
if (toolOptions.McpClientInitializationTimeoutSeconds.HasValue)
{
httpClient.Timeout = TimeSpan.FromSeconds(toolOptions.McpClientInitializationTimeoutSeconds.Value);
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

McpClientInitializationTimeoutSeconds is used directly to set HttpClient.Timeout and McpClientOptions.InitializationTimeout. If a consumer sets this to 0, a negative value, or a very large value, TimeSpan.FromSeconds(...) / HttpClient.Timeout can throw (or effectively make requests immediately time out). Consider validating the value (e.g., > 0 and within TimeSpan/HttpClient supported bounds) and throwing an ArgumentOutOfRangeException with a clear message.

Copilot uses AI. Check for mistakes.
Comment on lines +516 to +523
var clientOptions = toolOptions.McpClientInitializationTimeoutSeconds.HasValue
? new McpClientOptions
{
InitializationTimeout = TimeSpan.FromSeconds(toolOptions.McpClientInitializationTimeoutSeconds.Value),
}
: new McpClientOptions();

return await McpClientFactory.CreateAsync(clientTransport, clientOptions, loggerFactory: this._loggerFactory);
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

When McpClientInitializationTimeoutSeconds is null, this now still constructs and passes new McpClientOptions() and uses a different CreateAsync overload than before (previously the options argument was omitted/left null). If the MCP SDK treats “null options” differently from “default options instance”, this could be a behavioral change despite the PR’s stated intent. To preserve existing behavior, consider only creating/passing McpClientOptions when the timeout is explicitly set, and otherwise call the original overload (or pass null for options).

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +23
/// Gets or sets the timeout in seconds for MCP client initialization.
/// This includes the time for the MCP protocol handshake (initialize/initialized exchange)
/// and the underlying HTTP connection. Increase this value if the MCP server performs
/// slow operations during initialization (e.g., token exchanges in test environments).
/// When null, the MCP SDK default timeout is used.
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The new McpClientInitializationTimeoutSeconds property doesn’t document or enforce an allowed range. Since invalid values (<= 0, extremely large) can cause runtime exceptions when converted to a TimeSpan or applied to HttpClient.Timeout, it would help to document the expected bounds here (and ideally validate it at the call site).

Copilot uses AI. Check for mistakes.
Comment on lines +506 to +523
// Apply custom timeout only when explicitly configured
if (toolOptions.McpClientInitializationTimeoutSeconds.HasValue)
{
httpClient.Timeout = TimeSpan.FromSeconds(toolOptions.McpClientInitializationTimeoutSeconds.Value);
}

var clientTransport = new SseClientTransport(options, httpClient);

try
{
return await McpClientFactory.CreateAsync(clientTransport, loggerFactory: this._loggerFactory);
var clientOptions = toolOptions.McpClientInitializationTimeoutSeconds.HasValue
? new McpClientOptions
{
InitializationTimeout = TimeSpan.FromSeconds(toolOptions.McpClientInitializationTimeoutSeconds.Value),
}
: new McpClientOptions();

return await McpClientFactory.CreateAsync(clientTransport, clientOptions, loggerFactory: this._loggerFactory);
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

This change adds a new timeout behavior but there are no unit tests asserting the configured timeout is applied (and that the default/null path preserves existing behavior). Given there are existing tests around ToolOptions usage for MCP tooling services, please add coverage for the new option to prevent regressions.

Copilot uses AI. Check for mistakes.
- Add input validation (1-600s range) with ArgumentOutOfRangeException
- Document valid range in ToolOptions XML docs
- Preserve original SDK behavior when timeout is null: call
  McpClientFactory.CreateAsync without McpClientOptions instead of
  passing a default instance
- Add 13 unit tests covering: null default, valid/invalid values,
  ArgumentOutOfRangeException for out-of-bounds inputs

Co-Authored-By: Claude Code <noreply@anthropic.com>
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