Context
A same-day operator report showed Claude marking neo-mjs-knowledge-base and neo-mjs-github-workflow as Server disconnected immediately after initialize, while the MCP client log had no useful stderr. Live reproduction against current dev confirmed the process exited with code 1 and empty stderr.
The repo-local server log did contain the actionable root cause: assertConfigFresh() detected stale gitignored config overlays and told the operator to run npm run prepare -- --migrate-config. Running node ./ai/scripts/setup/initServerConfigs.mjs --migrate-config in the affected sibling checkouts repaired the local failure; both exact server entrypoints then stayed alive after the same initialize payload.
Release classification: post-release / boardless. This improves diagnostics for an already-guarded failure mode; it is not required for #13875 or the v13 release line.
The Problem
The config-freshness contract is already present and tested, but fatal MCP startup errors are only logged through the per-server logger. For stdio MCP clients, that means the client sees a transport disconnect with no stderr explanation, even though the repo log has the actionable --migrate-config message.
This turns a solved stale-overlay class into avoidable operator friction: the right diagnosis exists, but it is hidden from the surface the operator is actually looking at.
The Architectural Reality
ai/mcp/server/knowledge-base/mcp-server.mjs, ai/mcp/server/github-workflow/mcp-server.mjs, ai/mcp/server/memory-core/mcp-server.mjs, ai/mcp/server/gitlab-workflow/mcp-server.mjs, and ai/mcp/server/neural-link/mcp-server.mjs catch boot failures, call logger.error('Fatal error during server initialization:', error), then process.exit(1).
ai/scripts/setup/initServerConfigs.mjs owns assertConfigFresh(), which already names stale overlay leaves and the --migrate-config remediation.
- Prior freshness-guard tickets covered fail-fast behavior, not this stderr visibility gap.
The Fix
When a fatal MCP boot error is caught, also write a concise stack/message to stderr before exiting. Keep stdout untouched because stdio MCP protocol frames use stdout. Prefer a shared tiny helper if that avoids duplicating formatting across MCP entrypoints without adding broader startup machinery.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| Stdio MCP startup failure diagnostics |
MCP clients surface stderr when a server exits during startup |
Fatal boot failures include the same actionable reason on stderr that is currently written to repo logs |
Existing per-server log remains authoritative if stderr is unavailable |
Inline comments only if formatting is non-obvious |
Unit/source test or isolated spawn test proving stale-overlay failure writes stderr and exits non-zero |
Decision Record impact
none
Acceptance Criteria
Out of Scope
- Changing the config migration semantics.
- Auto-mutating ignored config files during MCP startup.
- Changing Claude/Codex/other client configuration files.
- Broad MCP server lifecycle redesign.
Related
Related: #13560
Related: #13570
Related: #13875
Origin Session ID: db5b2ecf-db91-4b7d-9498-ccef00426a1c
Handoff Retrieval Hints: MCP startup stale config overlay stderr, assertConfigFresh logger-only fatal exit, Claude Server disconnected empty stderr.
Context
A same-day operator report showed Claude marking
neo-mjs-knowledge-baseandneo-mjs-github-workflowasServer disconnectedimmediately afterinitialize, while the MCP client log had no useful stderr. Live reproduction against currentdevconfirmed the process exited with code 1 and empty stderr.The repo-local server log did contain the actionable root cause:
assertConfigFresh()detected stale gitignored config overlays and told the operator to runnpm run prepare -- --migrate-config. Runningnode ./ai/scripts/setup/initServerConfigs.mjs --migrate-configin the affected sibling checkouts repaired the local failure; both exact server entrypoints then stayed alive after the same initialize payload.Release classification: post-release / boardless. This improves diagnostics for an already-guarded failure mode; it is not required for #13875 or the v13 release line.
The Problem
The config-freshness contract is already present and tested, but fatal MCP startup errors are only logged through the per-server logger. For stdio MCP clients, that means the client sees a transport disconnect with no stderr explanation, even though the repo log has the actionable
--migrate-configmessage.This turns a solved stale-overlay class into avoidable operator friction: the right diagnosis exists, but it is hidden from the surface the operator is actually looking at.
The Architectural Reality
ai/mcp/server/knowledge-base/mcp-server.mjs,ai/mcp/server/github-workflow/mcp-server.mjs,ai/mcp/server/memory-core/mcp-server.mjs,ai/mcp/server/gitlab-workflow/mcp-server.mjs, andai/mcp/server/neural-link/mcp-server.mjscatch boot failures, calllogger.error('Fatal error during server initialization:', error), thenprocess.exit(1).ai/scripts/setup/initServerConfigs.mjsownsassertConfigFresh(), which already names stale overlay leaves and the--migrate-configremediation.The Fix
When a fatal MCP boot error is caught, also write a concise stack/message to stderr before exiting. Keep stdout untouched because stdio MCP protocol frames use stdout. Prefer a shared tiny helper if that avoids duplicating formatting across MCP entrypoints without adding broader startup machinery.
Contract Ledger Matrix
Decision Record impact
none
Acceptance Criteria
assertConfigFresh()during MCP startup exits non-zero and writes the actionable stale-overlay message to stderr.Out of Scope
Related
Related: #13560 Related: #13570 Related: #13875
Origin Session ID: db5b2ecf-db91-4b7d-9498-ccef00426a1c
Handoff Retrieval Hints:
MCP startup stale config overlay stderr,assertConfigFresh logger-only fatal exit,Claude Server disconnected empty stderr.