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>
This commit is contained in:
parent
fe2ce2bc60
commit
38b4a26998
1 changed files with 5169 additions and 3786 deletions
|
|
@ -0,0 +1,109 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue