Skip to content

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

Description

@yding-git

Describe the bug

When running Data API Builder in MCP stdio mode, the process still attempts to bind an HTTP port via Kestrel. If the port is unavailable, the process exits with code -1 before the MCP handshake can complete.

This is unexpected because --mcp-stdio communicates over stdin/stdout and should not depend on an HTTP port.

Two scenarios trigger the crash:

  1. ASPNETCORE_URLS is set to an occupied port.
  2. ASPNETCORE_URLS is not set — in my local test, DAB appeared to bind to http://127.0.0.1:5000 and crashed when port 5000 was occupied.

Environment

  • OS: Windows
  • Data API Builder version: Microsoft.DataApiBuilder 2.0.8
  • Database: SQL Server (via @env() connection string)
  • MCP client: Codex (stdio transport)
  • Command: dab start --mcp-stdio --config dab-config.json

Expected behavior

dab start --mcp-stdio should communicate with the MCP client over stdin/stdout and should not require an HTTP port. It should either:

  1. Skip Kestrel port binding in stdio mode, or
  2. Ignore ASPNETCORE_URLS in stdio mode, or
  3. Produce a clear error when the port is occupied instead of exiting silently.

Actual behavior

If the target port is occupied (either explicitly via ASPNETCORE_URLS or implicitly via the default port 5000), the DAB process exits with code -1 and produces an AddressInUseException in stderr.

The MCP client only sees a generic handshake failure: "connection closed: initialize response".

Reproduction steps

  1. Occupy a local port (e.g. start a DAB REST instance on port 5155, or have another process on port 5000).

  2. Configure DAB MCP stdio:

[mcp_servers.dab_sql]
command = "dab"
args = ["start", "--mcp-stdio", "--config", 'C:\path\to\dab-config.json']

[mcp_servers.dab_sql.env]
ASPNETCORE_URLS = "http://127.0.0.1:5155"
DAB_SQL_CONNECTION_STRING = "<connection string>"
  1. Start the MCP client. The DAB process exits before the handshake.

What I verified (Node.js MCP stdio client)

With ASPNETCORE_URLS set to a free port, all MCP methods work correctly:

  • initialize → serverInfo, capabilities, instructions returned
  • tools/list → 6 tools: describe_entities, create_record, read_records, update_record, execute_entity, aggregate_records

Also confirmed:

  • @env(DAB_SQL_CONNECTION_STRING) resolves correctly in stdio mode.
  • All configured entities (tables and views) load successfully.
  • Schema introspection works for both tables and views.
  • The issue is not caused by SQL Server connectivity, entity configuration, or MCP protocol incompatibility.

Workaround

Set ASPNETCORE_URLS to a known free port:

ASPNETCORE_URLS = "http://127.0.0.1:5199"

Note: removing ASPNETCORE_URLS is not a reliable workaround, because DAB will then default to port 5000, which may also be occupied.

Suggested improvement

In --mcp-stdio mode, DAB should not attempt to bind an HTTP port. If the current behavior is intentional (e.g. Kestrel lifecycle is required even in stdio mode), DAB should bind to a random available port (port 0) or the documentation should explicitly warn that ASPNETCORE_URLS or the default port 5000 must be free.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions