4
4
using Microsoft . AspNetCore . WebUtilities ;
5
5
using Microsoft . Extensions . Logging ;
6
6
using Microsoft . Extensions . Options ;
7
+ using Microsoft . Extensions . Primitives ;
7
8
using Microsoft . Net . Http . Headers ;
8
9
using ModelContextProtocol . AspNetCore . Stateless ;
9
10
using ModelContextProtocol . Protocol ;
@@ -28,9 +29,6 @@ internal sealed class StreamableHttpHandler(
28
29
{
29
30
private static readonly JsonTypeInfo < JsonRpcError > s_errorTypeInfo = GetRequiredJsonTypeInfo < JsonRpcError > ( ) ;
30
31
31
- private static readonly MediaTypeHeaderValue s_applicationJsonMediaType = new ( "application/json" ) ;
32
- private static readonly MediaTypeHeaderValue s_textEventStreamMediaType = new ( "text/event-stream" ) ;
33
-
34
32
public ConcurrentDictionary < string , HttpMcpSession < StreamableHttpServerTransport > > Sessions { get ; } = new ( StringComparer . Ordinal ) ;
35
33
36
34
public HttpServerTransportOptions HttpServerTransportOptions => httpServerTransportOptions . Value ;
@@ -43,8 +41,8 @@ public async Task HandlePostRequestAsync(HttpContext context)
43
41
// ASP.NET Core Minimal APIs mostly try to stay out of the business of response content negotiation,
44
42
// so we have to do this manually. The spec doesn't mandate that servers MUST reject these requests,
45
43
// 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 ) )
48
46
{
49
47
await WriteJsonRpcErrorAsync ( context ,
50
48
"Not Acceptable: Client must accept both application/json and text/event-stream" ,
@@ -85,8 +83,7 @@ await WriteJsonRpcErrorAsync(context,
85
83
86
84
public async Task HandleGetRequestAsync ( HttpContext context )
87
85
{
88
- var acceptHeaders = context . Request . GetTypedHeaders ( ) . Accept ;
89
- if ( ! acceptHeaders . Contains ( s_textEventStreamMediaType ) )
86
+ if ( ! context . Request . GetTypedHeaders ( ) . Accept . Any ( MatchesTextEventStreamMediaType ) )
90
87
{
91
88
await WriteJsonRpcErrorAsync ( context ,
92
89
"Not Acceptable: Client must accept text/event-stream" ,
@@ -331,6 +328,12 @@ internal static Task RunSessionAsync(HttpContext httpContext, IMcpServer session
331
328
332
329
private static JsonTypeInfo < T > GetRequiredJsonTypeInfo < T > ( ) => ( JsonTypeInfo < T > ) McpJsonUtilities . DefaultOptions . GetTypeInfo ( typeof ( T ) ) ;
333
330
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
+
334
337
private sealed class HttpDuplexPipe ( HttpContext context ) : IDuplexPipe
335
338
{
336
339
public PipeReader Input => context . Request . BodyReader ;
0 commit comments