Skip to content

fix: UseUrls for reliable Kestrel bind on background threads, custom GET /mcp/ handler for SSE discovery#2246

Open
velteyn wants to merge 2 commits into
OpenRakis:masterfrom
velteyn:spice86-mcp-fix
Open

fix: UseUrls for reliable Kestrel bind on background threads, custom GET /mcp/ handler for SSE discovery#2246
velteyn wants to merge 2 commits into
OpenRakis:masterfrom
velteyn:spice86-mcp-fix

Conversation

@velteyn

@velteyn velteyn commented Jun 24, 2026

Copy link
Copy Markdown

Problem

The MCP HTTP server has two reliability issues when running on a background thread (as it does in headless/embedded usage):

  1. Kestrel binding fails silently: ConfigureKestrel with ListenLocalhost does not bind the port on background threads. No error is thrown, but no TCP listener is created. Switching to builder.WebHost.UseUrls(...) resolves this.

  2. WebApplication.Run() returns prematurely: On a background thread with no console, ConsoleLifetime has no signal to watch, causing WaitForShutdownAsync() to return after ~28 seconds. Replaced with StartAsync() + ManualResetEvent.WaitOne() for indefinite keepalive.

  3. No GET endpoint for SSE discovery: In stateless mode, MCP SDK registers only POST. Added MapGet("/mcp/", ...) returning event: endpoint\ndata: /mcp/\n\n, allowing SSE-based discovery while keeping stateless mode as default.

Changes

  • McpHttpHost.cs: UseUrls instead of ConfigureKestrel; StartAsync + ManualResetEvent instead of Run(); custom GET handler.
  • RegisterValueSet.cs: Add VGA mode 0x69 (640x480 16-color) — needed by 1988 BattleTech.
  • DosDriveManager.cs / DosDriveBase.cs: Mount A: and B: drives to same path as C:.

Testing

GET /mcp/ returns SSE endpoint event, POST /mcp/ returns tool listing, server stays alive indefinitely, VGA mode 0x69 no longer crashes.

velteyn added 2 commits June 24, 2026 08:06
Change Stateless=false so the SDK maps GET /mcp endpoint
(required by opencode remote MCP client). The GET endpoint
establishes an SSE connection for session negotiation, and
POST handles JSON-RPC requests.

Previously stateless mode was used to avoid session-ID issues
with custom AI client code, but opencode native remote MCP
support uses the official MCP SDK which handles session
negotiation correctly.
…SE compatibility

MCP transport fixes for opencode remote MCP client compatibility: Use builder.WebHost.UseUrls() instead of ConfigureKestrel.ListenLocalhost() which was silently failing to bind on background threads. Replace _app.Run() (premature return after ~28s) with StartAsync() + ManualResetEvent so the server stays up on a background thread. Add custom GET /mcp/ handler returning SSE endpoint: /mcp/ event so MCP clients that probe with GET first (like opencode) can discover the POST endpoint. Stateless mode retained; custom GET handler coexists with SDK POST handler.
}

// Wait forever so the thread never exits.
new System.Threading.ManualResetEvent(false).WaitOne();
Comment on lines +509 to +517
foreach (char ch in text)
{
if (TryGetPhysicalKey(ch, out PhysicalKey pk))
{
hub.PostKeyboardEvent(new KeyboardEventArgs(pk, true));
hub.PostKeyboardEvent(new KeyboardEventArgs(pk, false));
count++;
}
}
@maximilien-noal maximilien-noal added the MCP related to the MCP server label Jun 26, 2026

@maximilien-noal maximilien-noal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@velteyn Can the Battletech code and DOS changes be removed ?

@velteyn

velteyn commented Jun 26, 2026

Copy link
Copy Markdown
Author

ops pardon, my bad I accidentally pushed my work in this !!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

MCP related to the MCP server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants