Avoid starting web host in MCP stdio mode#3676
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes MCP stdio mode (dab start --mcp-stdio) so it no longer starts the ASP.NET Core web host (and therefore doesn’t attempt to bind HTTP ports via Kestrel) before completing the MCP initialize handshake.
Changes:
- Remove the
IHost.Start()/StopAsync()lifecycle calls from the MCP stdio helper path. - Keep MCP tool discovery/registration via DI, then run the stdio JSON-RPC loop.
- Add a regression unit test asserting stdio mode does not start the host while still running the stdio server loop.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Service/Utilities/McpStdioHelper.cs | Stops starting/stopping the host in MCP stdio mode; runs tool registry init + stdio loop without binding HTTP ports. |
| src/Service.Tests/UnitTests/McpStdioHelperTests.cs | Adds a regression test validating stdio mode doesn’t start the web host while still running the stdio server loop. |
| stdio.RunAsync(lifetime.ApplicationStopping).GetAwaiter().GetResult(); | ||
| host.StopAsync().GetAwaiter().GetResult(); | ||
|
|
||
| return true; |
There was a problem hiding this comment.
Fixed in the latest revision. RunMcpStdioHost now disposes the host in a finally after the stdio loop exits, without calling StartAsync() or StopAsync().
| using System; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; |
There was a problem hiding this comment.
Fixed in the latest revision. The using System; directive was removed, and the test now references System.IServiceProvider explicitly.
JerryNixon
left a comment
There was a problem hiding this comment.
Clean and simple. Thank you for the PR. Looks great. Small tweaks.
RubenCerna2079
left a comment
There was a problem hiding this comment.
LGTM! Just need you to address the comments made by copilot. If you think they are not necessary, just give your reasoning and resolve the issue.
8ea2547 to
1a12bf9
Compare
Summary
dab start --mcp-stdiocommunicates over stdin/stdout, but the stdio helper was callinghost.Start(). That starts the web host and Kestrel, so stdio mode still tried to bind the configured HTTP port. When the port was occupied, the process exited before the MCPinitializehandshake.The stdio path already initializes and registers MCP tools directly from DI, so it can run the stdio JSON-RPC loop without starting Kestrel.
Fixes #3675.
Changes
StartAsync()orStopAsync().RunMcpStdioHost()does not callIHost.StartAsync()/StopAsync(), still starts the stdio server loop, and disposes the host.Verification
A runtime smoke also occupied
127.0.0.1:5155, pointedASPNETCORE_URLSat that occupied port, and verified that the service completed MCPinitializeandshutdownover stdio without an address-in-use error.