Skip to content

Avoid starting web host in MCP stdio mode#3676

Open
jstar0 wants to merge 2 commits into
Azure:mainfrom
jstar0:fix/mcp-stdio-no-webhost-bind
Open

Avoid starting web host in MCP stdio mode#3676
jstar0 wants to merge 2 commits into
Azure:mainfrom
jstar0:fix/mcp-stdio-no-webhost-bind

Conversation

@jstar0

@jstar0 jstar0 commented Jun 21, 2026

Copy link
Copy Markdown

Summary

dab start --mcp-stdio communicates over stdin/stdout, but the stdio helper was calling host.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 MCP initialize handshake.

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

  • Avoid starting the ASP.NET Core host when running the engine in MCP stdio mode.
  • Keep MCP tool registry initialization in the stdio path before running the JSON-RPC loop.
  • Dispose the host after the stdio loop exits without calling StartAsync() or StopAsync().
  • Add a regression test that proves RunMcpStdioHost() does not call IHost.StartAsync()/StopAsync(), still starts the stdio server loop, and disposes the host.

Verification

dotnet test src/Service.Tests/Azure.DataApiBuilder.Service.Tests.csproj --filter 'FullyQualifiedName~McpStdioHelperTests' -p:RuntimeIdentifiers= -p:UseAppHost=false
dotnet test src/Service.Tests/Azure.DataApiBuilder.Service.Tests.csproj --filter 'FullyQualifiedName~McpStdioHelperTests|FullyQualifiedName~McpStdioServerRunAsyncTests|FullyQualifiedName~McpStdioServerInitializeTests|FullyQualifiedName~McpStdioServerContentBlockTests|FullyQualifiedName~McpToolRegistryTests' -p:RuntimeIdentifiers= -p:UseAppHost=false
git diff --check

A runtime smoke also occupied 127.0.0.1:5155, pointed ASPNETCORE_URLS at that occupied port, and verified that the service completed MCP initialize and shutdown over stdio without an address-in-use error.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Comment thread src/Service/Utilities/McpStdioHelper.cs Outdated
Comment on lines 86 to 93
stdio.RunAsync(lifetime.ApplicationStopping).GetAwaiter().GetResult();
host.StopAsync().GetAwaiter().GetResult();

return true;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest revision. RunMcpStdioHost now disposes the host in a finally after the stdio loop exits, without calling StartAsync() or StopAsync().

Comment on lines +6 to +8
using System;
using System.Threading;
using System.Threading.Tasks;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest revision. The using System; directive was removed, and the test now references System.IServiceProvider explicitly.

@JerryNixon JerryNixon added the cri Customer Reported issue label Jun 22, 2026

@JerryNixon JerryNixon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clean and simple. Thank you for the PR. Looks great. Small tweaks.

@RubenCerna2079 RubenCerna2079 self-assigned this Jun 24, 2026

@RubenCerna2079 RubenCerna2079 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@jstar0 jstar0 force-pushed the fix/mcp-stdio-no-webhost-bind branch from 8ea2547 to 1a12bf9 Compare June 25, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cri Customer Reported issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DAB --mcp-stdio binds an HTTP port on startup and crashes if the port is occupied

4 participants