You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/concepts/transports/transports.md
+16-1Lines changed: 16 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -185,6 +185,21 @@ app.Run();
185
185
186
186
By default, the HTTP transport runs **statelessly** — the server does not assign an `Mcp-Session-Id` or track transport session state in memory. This simplifies deployment, enables horizontal scaling without session affinity, and matches the `2026-07-28` Streamable HTTP wire format. Set `Stateless = false` explicitly when your server needs stateful sessions for unsolicited notifications, resource subscriptions, or per-client isolation. For a detailed guide on when to use stateless vs. stateful mode, configure session options, and understand [cancellation and disposal](xref:stateless#cancellation-and-disposal) behavior during shutdown, see [Stateless and Stateful](xref:stateless).
187
187
188
+
#### JSON response mode
189
+
190
+
By default, each Streamable HTTP POST returns an SSE stream so the server can send progress notifications and other intermediate messages before the final JSON-RPC response. Set <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableJsonResponse> to `true` when an intermediary, such as a web application firewall or reverse proxy, cannot pass SSE responses:
191
+
192
+
```csharp
193
+
builder.Services.AddMcpServer()
194
+
.WithHttpTransport(options=>
195
+
{
196
+
options.EnableJsonResponse=true;
197
+
})
198
+
.WithTools<MyTools>();
199
+
```
200
+
201
+
In this mode, POST requests return the final JSON-RPC response directly with an `application/json` content type. Request-related progress notifications and other intermediate messages are omitted, event-store resumability and polling are unavailable for POST responses, and notification-only POST requests still return an empty `202 Accepted` response. Stateful servers can still use the standalone GET SSE stream for unsolicited messages.
202
+
188
203
#### Host name validation
189
204
190
205
For local HTTP servers, keep the set of accepted host names limited to loopback values. This helps protect against DNS rebinding, where a browser reaches a local server through an attacker-controlled DNS name while sending that DNS name in the HTTP `Host` header. ASP.NET Core's Kestrel server doesn't validate `Host` headers by default, so configure `AllowedHosts` with known host names rather than `"*"`. This also avoids reflecting untrusted host names through ASP.NET Core features such as absolute URL generation. See [Host filtering with ASP.NET Core Kestrel web server | Microsoft Learn](https://learn.microsoft.com/aspnet/core/fundamentals/servers/kestrel/host-filtering) and [URL generation concepts | Microsoft Learn](https://learn.microsoft.com/aspnet/core/fundamentals/routing#url-generation-concepts).
In Streamable HTTP, client requests arrive as HTTP `POST` requests. The server holds each `POST` response body open as an SSE stream and writes the JSON-RPC response — plus any intermediate messages like progress notifications or server-to-client requests — back through it. This provides natural HTTP-level backpressure: each `POST` holds its connection until the handler completes.
262
+
In Streamable HTTP, client requests arrive as HTTP POST requests. By default, the server holds each POST response body open as an SSE stream and writes the JSON-RPC response — plus any intermediate messages like progress notifications or server-to-client requests — back through it. JSON response mode instead returns only the final response as `application/json`. Both modes provide natural HTTP-level backpressure because each POST remains open until the handler completes.
248
263
249
264
In stateful mode, the client can also open a long-lived `GET` request to receive **unsolicited** messages — notifications or server-to-client requests that the server initiates outside any active request handler (for example, resource-changed notifications from a background watcher). In stateless mode, the `GET` endpoint is not mapped, so every message must be part of a `POST` response. For a detailed breakdown, see [How Streamable HTTP delivers messages](xref:stateless#how-streamable-http-delivers-messages).
Copy file name to clipboardExpand all lines: src/ModelContextProtocol.Core/Server/StreamableHttpServerTransport.cs
+14Lines changed: 14 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -69,6 +69,20 @@ public StreamableHttpServerTransport(ILoggerFactory? loggerFactory = null)
69
69
/// </summary>
70
70
publicboolStateless{get;init;}
71
71
72
+
/// <summary>
73
+
/// Gets or initializes a value that indicates whether POST requests return the final JSON-RPC response as JSON
74
+
/// instead of streaming messages as SSE events.
75
+
/// </summary>
76
+
/// <value>
77
+
/// <see langword="true"/> to write a single JSON response; <see langword="false"/> to use SSE. The default is
78
+
/// <see langword="false"/>.
79
+
/// </value>
80
+
/// <remarks>
81
+
/// When enabled, request-related notifications and other intermediate messages are omitted from POST responses.
82
+
/// Standalone GET requests are unaffected and continue to use SSE.
83
+
/// </remarks>
84
+
publicboolEnableJsonResponse{get;init;}
85
+
72
86
/// <summary>
73
87
/// Gets or initializes a value indicating whether the execution context should flow from the calls to <see cref="HandlePostRequestAsync(JsonRpcMessage, Stream, CancellationToken)"/>
74
88
/// to the corresponding <see cref="JsonRpcMessageContext.ExecutionContext"/> property contained in the <see cref="JsonRpcMessage"/> instances returned by the <see cref="MessageReader"/>.
0 commit comments