44using Microsoft . AspNetCore . WebUtilities ;
55using Microsoft . Extensions . Logging ;
66using Microsoft . Extensions . Options ;
7+ using Microsoft . Extensions . Primitives ;
78using Microsoft . Net . Http . Headers ;
89using ModelContextProtocol . AspNetCore . Stateless ;
910using ModelContextProtocol . Protocol ;
@@ -28,9 +29,6 @@ internal sealed class StreamableHttpHandler(
2829{
2930 private static readonly JsonTypeInfo < JsonRpcError > s_errorTypeInfo = GetRequiredJsonTypeInfo < JsonRpcError > ( ) ;
3031
31- private static readonly MediaTypeHeaderValue s_applicationJsonMediaType = new ( "application/json" ) ;
32- private static readonly MediaTypeHeaderValue s_textEventStreamMediaType = new ( "text/event-stream" ) ;
33-
3432 public ConcurrentDictionary < string , HttpMcpSession < StreamableHttpServerTransport > > Sessions { get ; } = new ( StringComparer . Ordinal ) ;
3533
3634 public HttpServerTransportOptions HttpServerTransportOptions => httpServerTransportOptions . Value ;
@@ -43,8 +41,8 @@ public async Task HandlePostRequestAsync(HttpContext context)
4341 // ASP.NET Core Minimal APIs mostly try to stay out of the business of response content negotiation,
4442 // so we have to do this manually. The spec doesn't mandate that servers MUST reject these requests,
4543 // but it's probably good to at least start out trying to be strict.
46- var acceptHeaders = context . Request . GetTypedHeaders ( ) . Accept ;
47- if ( ! acceptHeaders . Contains ( s_applicationJsonMediaType ) || ! acceptHeaders . Contains ( s_textEventStreamMediaType ) )
44+ var typedHeaders = context . Request . GetTypedHeaders ( ) ;
45+ if ( ! typedHeaders . Accept . Any ( MatchesApplicationJsonMediaType ) || ! typedHeaders . Accept . Any ( MatchesTextEventStreamMediaType ) )
4846 {
4947 await WriteJsonRpcErrorAsync ( context ,
5048 "Not Acceptable: Client must accept both application/json and text/event-stream" ,
@@ -85,8 +83,7 @@ await WriteJsonRpcErrorAsync(context,
8583
8684 public async Task HandleGetRequestAsync ( HttpContext context )
8785 {
88- var acceptHeaders = context . Request . GetTypedHeaders ( ) . Accept ;
89- if ( ! acceptHeaders . Contains ( s_textEventStreamMediaType ) )
86+ if ( ! context . Request . GetTypedHeaders ( ) . Accept . Any ( MatchesTextEventStreamMediaType ) )
9087 {
9188 await WriteJsonRpcErrorAsync ( context ,
9289 "Not Acceptable: Client must accept text/event-stream" ,
@@ -331,6 +328,12 @@ internal static Task RunSessionAsync(HttpContext httpContext, IMcpServer session
331328
332329 private static JsonTypeInfo < T > GetRequiredJsonTypeInfo < T > ( ) => ( JsonTypeInfo < T > ) McpJsonUtilities . DefaultOptions . GetTypeInfo ( typeof ( T ) ) ;
333330
331+ private static bool MatchesApplicationJsonMediaType ( MediaTypeHeaderValue acceptHeaderValue )
332+ => acceptHeaderValue . MatchesMediaType ( "application/json" ) ;
333+
334+ private static bool MatchesTextEventStreamMediaType ( MediaTypeHeaderValue acceptHeaderValue )
335+ => acceptHeaderValue . MatchesMediaType ( "text/event-stream" ) ;
336+
334337 private sealed class HttpDuplexPipe ( HttpContext context ) : IDuplexPipe
335338 {
336339 public PipeReader Input => context . Request . BodyReader ;
0 commit comments