Skip to content

Commit b3a5a82

Browse files
authored
Use TryAddEnumerable for McpServerOptionsSetup (#252)
1 parent cae5dff commit b3a5a82

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

src/ModelContextProtocol/Configuration/McpServerServiceCollectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using ModelContextProtocol.Server;
22
using Microsoft.Extensions.Options;
3+
using Microsoft.Extensions.DependencyInjection.Extensions;
34

45
namespace Microsoft.Extensions.DependencyInjection;
56

@@ -17,7 +18,7 @@ public static class McpServerServiceCollectionExtensions
1718
public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, Action<McpServerOptions>? configureOptions = null)
1819
{
1920
services.AddOptions();
20-
services.AddTransient<IConfigureOptions<McpServerOptions>, McpServerOptionsSetup>();
21+
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<McpServerOptions>, McpServerOptionsSetup>());
2122
if (configureOptions is not null)
2223
{
2324
services.Configure(configureOptions);

tests/ModelContextProtocol.Tests/SseIntegrationTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using ModelContextProtocol.Server;
1111
using ModelContextProtocol.Tests.Utils;
1212
using ModelContextProtocol.Utils.Json;
13+
using TestServerWithHosting.Tools;
1314

1415
namespace ModelContextProtocol.Tests;
1516

@@ -95,6 +96,56 @@ public async Task ConnectAndReceiveNotification_InMemoryServer()
9596
Assert.Equal("Hello from server!", message);
9697
}
9798

99+
[Fact]
100+
public async Task AddMcpServer_CanBeCalled_MultipleTimes()
101+
{
102+
var firstOptionsCallbackCallCount = 0;
103+
var secondOptionsCallbackCallCount = 0;
104+
105+
Builder.Services.AddMcpServer(options =>
106+
{
107+
Interlocked.Increment(ref firstOptionsCallbackCallCount);
108+
})
109+
.WithTools<EchoTool>();
110+
111+
Builder.Services.AddMcpServer(options =>
112+
{
113+
Interlocked.Increment(ref secondOptionsCallbackCallCount);
114+
})
115+
.WithTools<SampleLlmTool>();
116+
117+
118+
await using var app = Builder.Build();
119+
app.MapMcp();
120+
await app.StartAsync(TestContext.Current.CancellationToken);
121+
122+
using var httpClient = CreateHttpClient();
123+
await using var mcpClient = await ConnectMcpClient(httpClient);
124+
125+
// Options can be lazily initialized, but they must be instantiated by the time an MCP client can finish connecting.
126+
// Callbacks can be called multiple times if configureOptionsAsync is configured, because that uses the IOptionsFactory,
127+
// but that's not the case in this test.
128+
Assert.Equal(1, firstOptionsCallbackCallCount);
129+
Assert.Equal(1, secondOptionsCallbackCallCount);
130+
131+
var tools = await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
132+
133+
Assert.Equal(2, tools.Count);
134+
Assert.Contains(tools, tools => tools.Name == "Echo");
135+
Assert.Contains(tools, tools => tools.Name == "sampleLLM");
136+
137+
var echoResponse = await mcpClient.CallToolAsync(
138+
"Echo",
139+
new Dictionary<string, object?>
140+
{
141+
["message"] = "from client!"
142+
},
143+
cancellationToken: TestContext.Current.CancellationToken);
144+
var textContent = Assert.Single(echoResponse.Content, c => c.Type == "text");
145+
146+
Assert.Equal("hello from client!", textContent.Text);
147+
}
148+
98149
private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints)
99150
{
100151
var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();

0 commit comments

Comments
 (0)