Skip to content

Commit 62c839b

Browse files
McpApprovalServer: run MCP transport in stateless mode
Configures HttpServerTransportOptions.Stateless = true so each tools/call POST is self-contained and does not require the client to keep the initialize SSE stream open. ModelContextProtocol.AspNetCore 1.2.0 destroys the server-side session as soon as the initialize SSE disconnects, which caused tools/call from Docker to return HTTP 500. Safe here because application state lives in CommandHistoryService / ApprovalRequestQueue, ExecuteCommandTool uses a hardcoded "default" session id, calls are request/response, and no server-initiated messages need a persistent SSE channel. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1c968a9 commit 62c839b

1 file changed

Lines changed: 15 additions & 11 deletions

File tree

src/PostSharp.Engineering.McpApprovalServer/Services/McpHttpServer.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using PostSharp.Engineering.McpApprovalServer.Mcp.Services;
99
using PostSharp.Engineering.McpApprovalServer.Mcp.Tools;
1010
using System;
11-
using System.Threading;
1211
using System.Threading.Tasks;
1312

1413
namespace PostSharp.Engineering.McpApprovalServer.Services;
@@ -62,19 +61,24 @@ public async Task StartAsync()
6261
options.Limits.MinResponseDataRate = null;
6362
} );
6463

65-
// Disable the MCP server's per-session idle timeout so long Claude
66-
// sessions (hours or days) don't get their server-side session
67-
// disposed while the user is idle. Without this, the SDK defaults
68-
// to 2 hours, after which a reconnect is required.
64+
// Run the MCP transport in stateless mode so each tools/call POST is
65+
// self-contained and does not require the client to hold an SSE
66+
// stream open between the initialize handshake and subsequent calls.
6967
//
70-
// The SDK treats only Timeout.InfiniteTimeSpan (negative ticks) as
71-
// the "never expire" sentinel — TimeSpan.MaxValue is interpreted as
72-
// a finite timeout and silently overflows the pruner's tick math,
73-
// which manifests as sessions being dropped after a few hours.
68+
// Background: ModelContextProtocol.AspNetCore 1.2.0 destroys the
69+
// server-side session as soon as the SSE stream from the initialize
70+
// request disconnects, which made tools/call return HTTP 500 from
71+
// Docker clients that did not keep a background SSE connection
72+
// alive. This is safe here because (a) application state lives in
73+
// CommandHistoryService and ApprovalRequestQueue, not MCP sessions,
74+
// (b) ExecuteCommandTool uses a hardcoded "default" session ID,
75+
// (c) tool calls are request/response — the POST blocks until the
76+
// approval flow completes and returns the result on the same
77+
// connection, and (d) no server-initiated messages need a
78+
// persistent SSE channel.
7479
builder.Services.Configure<HttpServerTransportOptions>( options =>
7580
{
76-
options.IdleTimeout = Timeout.InfiniteTimeSpan;
77-
options.MaxIdleSessionCount = int.MaxValue;
81+
options.Stateless = true;
7882
} );
7983

8084
// Register services for tool dependencies (use shared instances from main DI container)

0 commit comments

Comments
 (0)