From ba4e0d8f53bf2d0ab83777b79050f82b0fc71dbd Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Fri, 18 Apr 2025 01:52:11 +0800 Subject: [PATCH 1/3] add parameter occupyRootUrl for MapMcp() Make sure that the homepage is not forcibly occupied during the integration, keep custom(or default from .NET Core setting) homepage, `/sse` and `/message` urls under root url together. --- .../McpEndpointRouteBuilderExtensions.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs index ac424cc8b..f7858cb6c 100644 --- a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs +++ b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs @@ -16,14 +16,18 @@ public static class McpEndpointRouteBuilderExtensions /// /// The web application to attach MCP HTTP endpoints. /// The route pattern prefix to map to. + /// Whether to use MapGet to occupy the default home page(/) /// Returns a builder for configuring additional endpoint conventions like authorization policies. - public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern = "") + public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern = "", bool occupyRootUrl = true) { var handler = endpoints.ServiceProvider.GetService() ?? throw new InvalidOperationException("You must call WithHttpTransport(). Unable to find required services. Call builder.Services.AddMcpServer().WithHttpTransport() in application startup code."); var routeGroup = endpoints.MapGroup(pattern); - routeGroup.MapGet("", handler.HandleRequestAsync); + if (occupyRootUrl) + { + routeGroup.MapGet("", handler.HandleRequestAsync); + } routeGroup.MapGet("/sse", handler.HandleRequestAsync); routeGroup.MapPost("/message", handler.HandleRequestAsync); return routeGroup; From febe1b33d5ba0886ea078721af32ae2a8d8594a1 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Sat, 19 Apr 2025 23:46:23 +0800 Subject: [PATCH 2/3] add occupyRootUrl paremeter for MapMcp(), retain the customization ability of the homepage --- .../McpEndpointRouteBuilderExtensions.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs index 21a787cc0..35ec90c05 100644 --- a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs +++ b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs @@ -32,13 +32,18 @@ public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpo .WithDisplayName(b => $"MCP Streamable HTTP | {b.DisplayName}") .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status404NotFound, typeof(JsonRpcError), contentTypes: ["application/json"])); - streamableHttpGroup.MapPost("", streamableHttpHandler.HandlePostRequestAsync) - .WithMetadata(new AcceptsMetadata(["application/json"])) - .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])) - .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted)); - streamableHttpGroup.MapGet("", streamableHttpHandler.HandleGetRequestAsync) - .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])); - streamableHttpGroup.MapDelete("", streamableHttpHandler.HandleDeleteRequestAsync); + + var routeGroup = endpoints.MapGroup(pattern); + if (occupyRootUrl) + { + streamableHttpGroup.MapPost("", streamableHttpHandler.HandlePostRequestAsync) + .WithMetadata(new AcceptsMetadata(["application/json"])) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted)); + streamableHttpGroup.MapGet("", streamableHttpHandler.HandleGetRequestAsync) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])); + streamableHttpGroup.MapDelete("", streamableHttpHandler.HandleDeleteRequestAsync); + } // Map legacy HTTP with SSE endpoints. var sseHandler = endpoints.ServiceProvider.GetRequiredService(); From 15679deab3b2491e0045585fa4ca4b2360f02211 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Sun, 20 Apr 2025 00:47:50 +0800 Subject: [PATCH 3/3] reference https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http streamable HTTP use /mcp, and retain the customization ability of the homepage --- .../McpEndpointRouteBuilderExtensions.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs index 35ec90c05..f37d5fc80 100644 --- a/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs +++ b/src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs @@ -28,12 +28,19 @@ public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpo throw new InvalidOperationException("You must call WithHttpTransport(). Unable to find required services. Call builder.Services.AddMcpServer().WithHttpTransport() in application startup code."); var mcpGroup = endpoints.MapGroup(pattern); + var streamableHttpGroup = mcpGroup.MapGroup("") - .WithDisplayName(b => $"MCP Streamable HTTP | {b.DisplayName}") - .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status404NotFound, typeof(JsonRpcError), contentTypes: ["application/json"])); + .WithDisplayName(b => $"MCP Streamable HTTP | {b.DisplayName}") + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status404NotFound, typeof(JsonRpcError), contentTypes: ["application/json"])); + streamableHttpGroup.MapPost("mcp", streamableHttpHandler.HandlePostRequestAsync) + .WithMetadata(new AcceptsMetadata(["application/json"])) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted)); + streamableHttpGroup.MapGet("mcp", streamableHttpHandler.HandleGetRequestAsync) + .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["text/event-stream"])); + streamableHttpGroup.MapDelete("mcp", streamableHttpHandler.HandleDeleteRequestAsync); - var routeGroup = endpoints.MapGroup(pattern); if (occupyRootUrl) { streamableHttpGroup.MapPost("", streamableHttpHandler.HandlePostRequestAsync)