csharp-sdk/tests/ModelContextProtocol.Tests/Transport/HttpClientTransportAutoDetectTests.cs
Mackinnon Buck 38b4a26998
Restructure IMcpEndpoint, IMcpClient, and IMcpServer (#723)
* Remove `McpEndpoint` & `IMcpClient`

* Revise naming, add abstract classes

* Fix missing renames

* Move extension methods to instance methods

* Add back old API, but obsoleted

* Add back obsoleted factory classes

* Move deprecation attributes

* `SseClientTransport` -> `HttpClientTransport`

* Update src/ModelContextProtocol.Core/Server/AugmentedServiceProvider.cs

Co-authored-by: Stephen Halter <halter73@gmail.com>

* PR feedback

* PR feedback

* Add back dispose lock

* Fix docs projects

* PR feedback

* Undo `docs/*.md` changes

---------

Co-authored-by: Stephen Halter <halter73@gmail.com>
2025-09-16 10:22:10 -07:00

109 lines
No EOL
4 KiB
C#

using ModelContextProtocol.Client;
using ModelContextProtocol.Tests.Utils;
using System.Net;
namespace ModelContextProtocol.Tests.Transport;
public class HttpClientTransportAutoDetectTests(ITestOutputHelper testOutputHelper) : LoggedTest(testOutputHelper)
{
[Fact]
public async Task AutoDetectMode_UsesStreamableHttp_WhenServerSupportsIt()
{
var options = new HttpClientTransportOptions
{
Endpoint = new Uri("http://localhost"),
TransportMode = HttpTransportMode.AutoDetect,
Name = "AutoDetect test client"
};
using var mockHttpHandler = new MockHttpHandler();
using var httpClient = new HttpClient(mockHttpHandler);
await using var transport = new HttpClientTransport(options, httpClient, LoggerFactory);
// Simulate successful Streamable HTTP response for initialize
mockHttpHandler.RequestHandler = (request) =>
{
if (request.Method == HttpMethod.Post)
{
return Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{\"jsonrpc\":\"2.0\",\"id\":\"init-id\",\"result\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{}}}}"),
Headers =
{
{ "Content-Type", "application/json" },
{ "mcp-session-id", "test-session" }
}
});
}
// Shouldn't reach here for successful Streamable HTTP
throw new InvalidOperationException("Unexpected request");
};
await using var session = await transport.ConnectAsync(TestContext.Current.CancellationToken);
// The auto-detecting transport should be returned
Assert.NotNull(session);
}
[Fact]
public async Task AutoDetectMode_FallsBackToSse_WhenStreamableHttpFails()
{
var options = new HttpClientTransportOptions
{
Endpoint = new Uri("http://localhost"),
TransportMode = HttpTransportMode.AutoDetect,
Name = "AutoDetect test client"
};
using var mockHttpHandler = new MockHttpHandler();
using var httpClient = new HttpClient(mockHttpHandler);
await using var transport = new HttpClientTransport(options, httpClient, LoggerFactory);
var requestCount = 0;
mockHttpHandler.RequestHandler = (request) =>
{
requestCount++;
if (request.Method == HttpMethod.Post && requestCount == 1)
{
// First POST (Streamable HTTP) fails
return Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.NotFound,
Content = new StringContent("Streamable HTTP not supported")
});
}
if (request.Method == HttpMethod.Get)
{
// SSE connection request
return Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("event: endpoint\r\ndata: /sse-endpoint\r\n\r\n"),
Headers = { { "Content-Type", "text/event-stream" } }
});
}
if (request.Method == HttpMethod.Post && requestCount > 1)
{
// Subsequent POST to SSE endpoint succeeds
return Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("accepted")
});
}
throw new InvalidOperationException($"Unexpected request: {request.Method}, count: {requestCount}");
};
await using var session = await transport.ConnectAsync(TestContext.Current.CancellationToken);
// The auto-detecting transport should be returned
Assert.NotNull(session);
}
}