Files
Gitea-Tools/docs/mcp-namespace-eof-recovery.md
T
sysadmin ebd06b73c9 fix: address PR #587 REQUEST_CHANGES for MCP namespace health (#543)
- Distinguish client_namespace vs offline_spawn probe sources; only IDE
  client probes prove namespace health and feed mutation gates.
- Record assessments in session; gate gitea_submit_pr_review and
  gitea_merge_pr when client-namespace health is unhealthy/non-proven.
- Mark test_mcp_conn.py offline-only; align recovery docs to client
  reconnect (no PID-kill/config-touch as canonical recovery).
- Rebase onto current master so #590 ledger isolation keeps
  TestMergePR.test_unknown_profile_blocks green.
2026-07-09 18:44:04 -04:00

5.8 KiB

Recovering from client is closing: EOF on a Gitea MCP namespace (#543)

Symptom

A tool call through a Gitea MCP namespace — gitea-author, gitea-reviewer, gitea-merger, or the shared gitea-tools namespace — fails immediately with:

client is closing: EOF

Every subsequent call to that same namespace returns the same error, including cheap read tools such as gitea_whoami and gitea_list_profiles. Other MCP servers registered with the same client (for example context7) keep working, so this is not a global MCP-client outage.

Why this is not a code defect

This failure is a transport-level condition in the IDE / MCP client manager, not a missing or broken tool:

  • The tool can be present and registered in the Python FastMCP tool manager.
  • Direct Python inspection of the server confirms the tool exists.
  • Running the server manually and sending JSON-RPC over stdio works fine (offline spawn) — that path does not prove the IDE namespace is healthy.

The client manager entered a closed state after the backing subprocess for that namespace terminated (or was killed) behind its back. Once closed, the client does not re-spawn the child on the next tool call — it just replays client is closing: EOF. The OS process may even still be alive if a parent language-server process is holding the stdio pipes open.

This is the canonical "registered in FastMCP ≠ callable through the namespace" false-ready state. It is distinct from the stale-runtime family in #531 / #544, where the process is reachable but running behind master; that case is detected by the ps-based _check_mcp_runtimes_diagnostics in gitea_mcp_server.py. The EOF case is a dead/closed transport, not a stale one, so the ps check alone will not surface it.

Recovery path (canonical — client reconnect only)

Do the steps in order. Stop as soon as a live client-namespace call succeeds.

  1. Confirm the blast radius. Call a cheap read tool on the failing namespace (gitea_whoami or gitea_list_profiles). Then call the same tool on a different MCP server (e.g. context7).

    • Only the Gitea namespace fails → single-namespace transport close. Continue.
    • Every server fails → restart the whole MCP client, not just one namespace.
  2. Reconnect the namespace through the client, not the shell. Use the IDE / client MCP-reconnect action for that server entry (in Claude Code: /mcp → reconnect the affected gitea-* server). Reconnecting forces the client to spawn a fresh subprocess and re-open the pipe. This clears the closed-client state that a bare kill/respawn from a terminal does not.

  3. Do not "fix" it by importing the server or poking the process. Reaching for python -c 'import gitea_mcp_server ...', raw JSON-RPC from a shell, killing PIDs to force a respawn, or touching MCP config mtimes does not restore the client's view of the namespace and violates the daemon-import guard (#558, docs/mcp-daemon-import-guard.md). The only sanctioned repair is a client reconnect / relaunch.

  4. Verify through the same path the workflow will use. After reconnect, call the specific tool the blocked workflow needs — not just any tool — through the target namespace. For a merge that means calling the merger-authorized adoption/merge tool through gitea-merger. A green gitea_whoami on one namespace does not prove another namespace or another tool is callable. Record success with:

    gitea_assess_mcp_namespace_health(..., probe_source="client_namespace")
    
  5. If reconnect does not clear it, relaunch the client entirely, then repeat step 4. If EOF persists after a full relaunch, the backing subprocess is failing to start — inspect its stderr / launch config (command path, venv, *_MCP_CONFIG, *_MCP_PROFILE env) rather than retrying the call. Still do not use PID kill or config-touch as the primary recovery.

Diagnostics to capture when reporting EOF

Include all of these so the failure is actionable and reproducible:

  • Namespace name that returned EOF (gitea-author / gitea-reviewer / gitea-merger / gitea-tools).
  • Tool that was called and the exact error string.
  • PID of the backing process (if any) and whether it was still alive (informational only — not a recovery action).
  • Profile / env for that namespace (execution profile, *_MCP_PROFILE, worktree binding such as GITEA_AUTHOR_WORKTREE).
  • Config path the client launched the server from.
  • Result of the cross-server control call (did context7 succeed?).

Offline spawn probe (non-authoritative)

test_mcp_conn.py performs a full JSON-RPC handshake against a fresh subprocess (initializeinitializedtools/listtools/call) and classifies with probe_source=offline_spawn. That is useful for offline launch/registration debugging. It is not proof the IDE-managed namespace is healthy. See docs/mcp-namespace-health.md.

Do-not list during EOF recovery

  • Do not retry a blocked merge/adoption until the required tool is confirmed callable through the merger-authorized client namespace (see #543).
  • Do not clean, reset, or rebind a foreign worktree to work around the error.
  • Do not bypass the namespace with direct imports, raw API/curl, or in-memory state restoration.
  • Do not kill MCP PIDs or touch config mtimes as a substitute for client reconnect.
  • #531 / #544 — stale-runtime detection (ps-based); sibling failure mode.
  • #558 / docs/mcp-daemon-import-guard.md — why shell imports are not a repair.
  • docs/mcp-client-registration.md — per-server registration contract.
  • docs/mcp-namespace-health.md — probe sources and mutation enforcement.