diff --git a/src/Tests/Microsoft.Agents.A365.Tooling.Tests/Services/McpClientInitializationTimeoutTests.cs b/src/Tests/Microsoft.Agents.A365.Tooling.Tests/Services/McpClientInitializationTimeoutTests.cs
new file mode 100644
index 00000000..f0f258e1
--- /dev/null
+++ b/src/Tests/Microsoft.Agents.A365.Tooling.Tests/Services/McpClientInitializationTimeoutTests.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using FluentAssertions;
+using Microsoft.Agents.A365.Tooling.Models;
+using Microsoft.Agents.A365.Tooling.Services;
+using Microsoft.Agents.Builder;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Http;
+using Microsoft.Extensions.Logging;
+using Moq;
+using Xunit;
+
+namespace Microsoft.Agents.A365.Tooling.Tests.Services
+{
+ ///
+ /// Unit tests for McpClientInitializationTimeoutSeconds in ToolOptions
+ /// and its application in McpToolServerConfigurationService.
+ ///
+ public class McpClientInitializationTimeoutTests
+ {
+ private readonly Mock> _loggerMock;
+ private readonly Mock _configurationMock;
+ private readonly Mock _serviceProviderMock;
+ private readonly Mock _httpClientFactoryMock;
+
+ public McpClientInitializationTimeoutTests()
+ {
+ _loggerMock = new Mock>();
+ _configurationMock = new Mock();
+ _serviceProviderMock = new Mock();
+ _httpClientFactoryMock = new Mock();
+
+ _httpClientFactoryMock.Setup(f => f.CreateClient(It.IsAny()))
+ .Returns(new HttpClient());
+ }
+
+ [Fact]
+ public void ToolOptions_McpClientInitializationTimeoutSeconds_DefaultsToNull()
+ {
+ // Arrange & Act
+ var options = new ToolOptions();
+
+ // Assert
+ options.McpClientInitializationTimeoutSeconds.Should().BeNull();
+ }
+
+ [Fact]
+ public void ToolOptions_McpClientInitializationTimeoutSeconds_CanBeSetToValidValue()
+ {
+ // Arrange & Act
+ var options = new ToolOptions
+ {
+ McpClientInitializationTimeoutSeconds = 180
+ };
+
+ // Assert
+ options.McpClientInitializationTimeoutSeconds.Should().Be(180);
+ }
+
+ [Fact]
+ public void ToolOptions_McpClientInitializationTimeoutSeconds_CanBeSetToNull()
+ {
+ // Arrange
+ var options = new ToolOptions
+ {
+ McpClientInitializationTimeoutSeconds = 120
+ };
+
+ // Act
+ options.McpClientInitializationTimeoutSeconds = null;
+
+ // Assert
+ options.McpClientInitializationTimeoutSeconds.Should().BeNull();
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(-1)]
+ [InlineData(-100)]
+ [InlineData(601)]
+ [InlineData(int.MaxValue)]
+ public async Task GetMcpClientToolsAsync_InvalidTimeoutValue_ThrowsArgumentOutOfRangeException(int invalidTimeout)
+ {
+ // Arrange
+ var configMock = new Mock();
+ configMock.Setup(c => c["ASPNETCORE_ENVIRONMENT"]).Returns("Development");
+
+ var service = new McpToolServerConfigurationService(
+ _loggerMock.Object,
+ configMock.Object,
+ _serviceProviderMock.Object,
+ _httpClientFactoryMock.Object);
+
+ var serverConfig = new MCPServerConfig
+ {
+ mcpServerName = "test_server",
+ url = "https://localhost:52856/agents/servers/test_server",
+ id = "test-id",
+ scope = "test-scope",
+ audience = "test-audience",
+ publisher = "test-publisher",
+ };
+
+ var toolOptions = new ToolOptions
+ {
+ McpClientInitializationTimeoutSeconds = invalidTimeout
+ };
+
+ var turnContextMock = new Mock();
+
+ // Act
+ Func act = async () => await service.GetMcpClientToolsAsync(
+ turnContextMock.Object, serverConfig, "test-token", toolOptions);
+
+ // Assert - ArgumentOutOfRangeException is wrapped in InvalidOperationException by the catch block
+ var ex = await act.Should().ThrowAsync();
+ ex.WithInnerException();
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(60)]
+ [InlineData(120)]
+ [InlineData(300)]
+ [InlineData(600)]
+ public void ToolOptions_McpClientInitializationTimeoutSeconds_AcceptsValidValues(int validTimeout)
+ {
+ // Arrange & Act
+ var options = new ToolOptions
+ {
+ McpClientInitializationTimeoutSeconds = validTimeout
+ };
+
+ // Assert
+ options.McpClientInitializationTimeoutSeconds.Should().Be(validTimeout);
+ }
+ }
+}
diff --git a/src/Tooling/Core/Models/ToolOptions.cs b/src/Tooling/Core/Models/ToolOptions.cs
index 3a63f213..ade9bf3b 100644
--- a/src/Tooling/Core/Models/ToolOptions.cs
+++ b/src/Tooling/Core/Models/ToolOptions.cs
@@ -14,5 +14,15 @@ public class ToolOptions
/// Gets or sets the user agent configuration for this orchestrator.
///
public IUserAgentConfiguration? UserAgentConfiguration { get; set; }
+
+ ///
+ /// 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.
+ /// Valid range: 1 to 600 seconds. Values outside this range will throw .
+ ///
+ public int? McpClientInitializationTimeoutSeconds { get; set; }
}
}
diff --git a/src/Tooling/Core/Services/McpToolServerConfigurationService.cs b/src/Tooling/Core/Services/McpToolServerConfigurationService.cs
index db26decb..5fdc604d 100644
--- a/src/Tooling/Core/Services/McpToolServerConfigurationService.cs
+++ b/src/Tooling/Core/Services/McpToolServerConfigurationService.cs
@@ -503,10 +503,36 @@ private async Task CreateMcpClientWithAuthHandlers(ITurnContext turn
// Create HTTP client with the authentication handler chain
var httpClient = new HttpClient(loggingHandler);
+ // Apply custom timeout only when explicitly configured
+ if (toolOptions.McpClientInitializationTimeoutSeconds.HasValue)
+ {
+ var timeoutSeconds = toolOptions.McpClientInitializationTimeoutSeconds.Value;
+ if (timeoutSeconds < 1 || timeoutSeconds > 600)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(toolOptions),
+ timeoutSeconds,
+ "McpClientInitializationTimeoutSeconds must be between 1 and 600 seconds.");
+ }
+
+ httpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ }
+
var clientTransport = new SseClientTransport(options, httpClient);
try
{
+ // Only pass McpClientOptions when a custom timeout is set to preserve default SDK behavior
+ if (toolOptions.McpClientInitializationTimeoutSeconds.HasValue)
+ {
+ var clientOptions = new McpClientOptions
+ {
+ InitializationTimeout = TimeSpan.FromSeconds(toolOptions.McpClientInitializationTimeoutSeconds.Value),
+ };
+
+ return await McpClientFactory.CreateAsync(clientTransport, clientOptions, loggerFactory: this._loggerFactory);
+ }
+
return await McpClientFactory.CreateAsync(clientTransport, loggerFactory: this._loggerFactory);
}
catch (Exception ex)