|  | 
| 10 | 10 | using ModelContextProtocol.Server; | 
| 11 | 11 | using ModelContextProtocol.Tests.Utils; | 
| 12 | 12 | using ModelContextProtocol.Utils.Json; | 
|  | 13 | +using TestServerWithHosting.Tools; | 
| 13 | 14 | 
 | 
| 14 | 15 | namespace ModelContextProtocol.Tests; | 
| 15 | 16 | 
 | 
| @@ -95,6 +96,56 @@ public async Task ConnectAndReceiveNotification_InMemoryServer() | 
| 95 | 96 |         Assert.Equal("Hello from server!", message); | 
| 96 | 97 |     } | 
| 97 | 98 | 
 | 
|  | 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 | + | 
| 98 | 149 |     private static void MapAbsoluteEndpointUriMcp(IEndpointRouteBuilder endpoints) | 
| 99 | 150 |     { | 
| 100 | 151 |         var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>(); | 
|  | 
0 commit comments