using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; using ModelContextProtocol.Tests.Utils; using System.Text.Json.Nodes; namespace ModelContextProtocol.Tests.Server; /// /// Verifies that custom request handlers registered through /// cannot silently replace a built-in method /// or another custom handler. /// 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?)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( () => 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( () => 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( () => 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?)null), }, ], }; var ex = Assert.Throws( () => McpServer.Create(transport, options, LoggerFactory)); Assert.Contains(nameof(McpServerRequestHandler.RoutingNameParameter), ex.Message); } #pragma warning restore MCPEXP002 }