csharp-sdk/tests/ModelContextProtocol.Tests/Server/CustomRequestHandlerCollisionTests.cs
Pranav Senthilnathan 83523b0a42
Add initial task extension conformance coverage (#1734)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7c99928c-a943-49f4-9a63-8b04a0e4d18b
2026-07-24 17:16:20 -07:00

102 lines
3.5 KiB
C#

using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Text.Json.Nodes;
namespace ModelContextProtocol.Tests.Server;
/// <summary>
/// Verifies that custom request handlers registered through
/// <see cref="McpServerOptions.RequestHandlers"/> cannot silently replace a built-in method
/// or another custom handler.
/// </summary>
public class CustomRequestHandlerCollisionTests(ITestOutputHelper testOutputHelper) : LoggedTest(testOutputHelper)
{
#pragma warning disable MCPEXP002
private static McpServerRequestHandler CreateHandler(string method) => new()
{
Method = method,
Handler = (request, cancellationToken) => new ValueTask<JsonNode?>((JsonNode?)null),
};
[Fact]
public async Task CustomHandler_CollidingWithBuiltInMethod_Throws()
{
await using var transport = new StreamServerTransport(Stream.Null, Stream.Null);
var options = new McpServerOptions
{
Capabilities = new() { Tools = new() },
RequestHandlers = [CreateHandler("tools/call")],
};
var ex = Assert.Throws<InvalidOperationException>(
() => McpServer.Create(transport, options, LoggerFactory));
Assert.Contains("tools/call", ex.Message);
}
[Fact]
public async Task CustomHandler_CollidingWithInitialize_Throws()
{
await using var transport = new StreamServerTransport(Stream.Null, Stream.Null);
var options = new McpServerOptions
{
RequestHandlers = [CreateHandler("initialize")],
};
Assert.Throws<InvalidOperationException>(
() => McpServer.Create(transport, options, LoggerFactory));
}
[Fact]
public async Task CustomHandler_DuplicateCustomMethod_Throws()
{
await using var transport = new StreamServerTransport(Stream.Null, Stream.Null);
var options = new McpServerOptions
{
RequestHandlers = [CreateHandler("custom/method"), CreateHandler("custom/method")],
};
var ex = Assert.Throws<InvalidOperationException>(
() => McpServer.Create(transport, options, LoggerFactory));
Assert.Contains("custom/method", ex.Message);
}
[Fact]
public async Task CustomHandler_UniqueMethod_Succeeds()
{
await using var transport = new StreamServerTransport(Stream.Null, Stream.Null);
var options = new McpServerOptions
{
RequestHandlers = [CreateHandler("custom/method")],
};
await using var server = McpServer.Create(transport, options, LoggerFactory);
Assert.NotNull(server);
}
[Fact]
public async Task CustomHandler_EmptyRoutingNameParameter_Throws()
{
await using var transport = new StreamServerTransport(Stream.Null, Stream.Null);
var options = new McpServerOptions
{
RequestHandlers =
[
new McpServerRequestHandler
{
Method = "custom/method",
RoutingNameParameter = " ",
Handler = (request, cancellationToken) => new ValueTask<JsonNode?>((JsonNode?)null),
},
],
};
var ex = Assert.Throws<InvalidOperationException>(
() => McpServer.Create(transport, options, LoggerFactory));
Assert.Contains(nameof(McpServerRequestHandler.RoutingNameParameter), ex.Message);
}
#pragma warning restore MCPEXP002
}