Azure / Azure/data-api-builder

--mcp-stdio blocks initialize response until all schema introspection completes, causing MCP reconnect timeouts

Open
#3,430 1 comment 3 reactions 1 assignee Claimed by @souvikghosh04 View on GitHub
bug cri mcp-server perf
Dominant language
C#
Stars
1.5k
Forks
370
Avg merge
3d 17h
Merged PRs (30d)
8

Description

## Summary

When DAB starts in `--mcp-stdio` mode, it does not send the MCP `initialize` response until it has finished introspecting the schema for every registered entity. Against a remote Azure SQL Managed Instance (~160 ms round-trip per query × 2 queries per entity × 53 entities), this results in a **~17-second startup delay** before any MCP message is emitted on stdout.

This works fine on the initial connection because MCP clients wait indefinitely for first contact. However, on **reconnect**, clients typically apply a shorter timeout. Any client whose reconnect timeout is less than the schema-introspection time fails with a connection error, even though the DAB process is healthy and the database is reachable.

## Environment

- **DAB version:** Microsoft.DataApiBuilder 1.7.92
- **Database:** Azure SQL Managed Instance (remote, ~160 ms RTT)
- **Entities registered:** 53
- **MCP client:** Claude Code (desktop app)
- **OS:** Windows 11

## Steps to Reproduce

1. Register 50+ entities backed by a remote Azure SQL MI (≥ 100 ms RTT).
2. Run `dab start --config dab-config.json --mcp-stdio`.
3. Connect an MCP client (e.g. Claude Code). **First connection succeeds** after ~17 s.
4. Trigger a client reconnect (e.g. `/mcp` command in Claude Code). **Reconnect fails** — "Failed to reconnect" — because the client's reconnect timeout (< 17 s) expires before DAB emits the `initialize` response.

## Observed Behavior

DAB performs the full schema introspection synchronously before responding to the MCP `initialize` request:

```
t=0s Claude Code sends initialize
t=0s DAB starts schema introspection (53 entities × 2 queries × ~160ms = ~17s)
t=17s DAB sends initialize response ← too late for reconnect timeout
t=17s Claude Code has already given up; closes stdin
```

Wrapper log evidence:
```
[01:49:16.922Z] wrapper invoked
[01:49:16.922Z] client protocolVersion: 2025-11-25 ← initialize received immediately
[01:49:34.383Z] forwarding initialize response ← 17s later
[01:49:34.390Z] stdin EOF ← client already closed connection
```

## Expected Behavior

DAB should send the MCP `initialize` response **immediately** upon receiving the client's `initialize` request, then complete schema introspection in the background. The `tools/list` response can be delayed until introspection is done — the client will wait because the connection is already established.

Proposed flow:
```
t=0s Claude Code sends initialize
t=0s DAB sends initialize response immediately ← handshake complete
t=0s DAB starts schema introspection in background
t=17s DAB sends tools/list response (client has been waiting, connection alive)
```

## Workaround

Wrap the `dab` process in a Node.js stdio proxy that:
1. Intercepts the client's `initialize` request and replies immediately with a minimal valid response.
2. Discards DAB's own `initialize` response when it eventually arrives.
3. Passes all subsequent traffic (`tools/list`, `tools/call`, etc.) through normally.

This workaround is required to make reconnects usable against remote databases with non-trivial schema introspection times.

## Impact

- Any MCP client with a reconnect timeout shorter than the schema-introspection time cannot use reconnect commands — only a full client restart works.
- Severity increases with entity count and database RTT. On a local SQL Server instance this is unlikely to manifest; on a remote managed instance with 50+ entities it is reliably reproducible.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.