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.
This commit is contained in:
2026-07-09 18:44:04 -04:00
parent af9f1c5944
commit ebd06b73c9
7 changed files with 341 additions and 94 deletions
+61 -49
View File
@@ -1,4 +1,4 @@
# MCP namespace EOF recovery
# MCP namespace health diagnostics (#543)
Gitea MCP tools can be registered in the Python FastMCP server while the IDE's
live MCP namespace is still unusable. The failure usually appears as
@@ -6,19 +6,54 @@ live MCP namespace is still unusable. The failure usually appears as
a tool such as `gitea_whoami`.
Do not treat static tool registration as proof that review or merge workflows
can proceed. A reviewer or merger flow must have live namespace evidence that
the required tool is callable through the configured namespace.
can proceed. A reviewer or merger flow must have **client-namespace** evidence
that the required tool is callable through the configured IDE MCP namespace.
## Required namespace probes
## Probe sources (do not confuse them)
Run the health check after changing MCP config, switching branches that affect
server code, or seeing EOF from any Gitea namespace:
| Source | How obtained | Proves IDE namespace? |
| --- | --- | --- |
| `client_namespace` | Tool call through the IDE-managed MCP client | **Yes** |
| `offline_spawn` | `test_mcp_conn.py` subprocess JSON-RPC handshake | **No** (offline only) |
`gitea_assess_mcp_namespace_health` accepts `probe_source` and only sets
`ide_namespace_proven=true` for `client_namespace` success. Offline spawn
success never clears review/merge mutation gates.
## Client-namespace health check (canonical)
1. Through the IDE client, call a cheap tool on the target namespace
(`gitea_whoami` or `gitea_list_profiles`).
2. Feed the live result into:
```text
gitea_assess_mcp_namespace_health(
namespace="gitea-merger", # or reviewer / author / tools
registered_tools=[...], # optional static list
probe_result={"success": true, "result": {...}},
probe_source="client_namespace",
)
```
3. A healthy client-namespace assessment is recorded in the MCP session and
consulted by **live** `gitea_submit_pr_review` / `gitea_merge_pr` gates.
4. If the probe fails with EOF, recover via **client reconnect only** — see
`docs/mcp-namespace-eof-recovery.md`. Do **not** kill PIDs or touch MCP
config mtimes as a recovery procedure.
## Offline spawn probe (non-authoritative)
```bash
python3 test_mcp_conn.py --config ~/.gemini/config/mcp_config.json
```
By default the script checks these namespaces and required tools:
This script spawns a **separate** server process from config, performs
JSON-RPC `initialize``tools/list``tools/call`, and classifies the
result with `probe_source=offline_spawn`. Use it for offline debugging of
launch command / registration. It does **not** prove the IDE-managed
namespace is healthy.
By default the script checks:
| Namespace | Required tool |
| --- | --- |
@@ -27,50 +62,27 @@ By default the script checks these namespaces and required tools:
| `gitea-merger` | `gitea_whoami` |
| `gitea-tools` | `gitea_list_profiles` |
Use `--namespace gitea-reviewer` to test only one namespace. A passing probe
requires:
## Recovery (canonical)
1. The namespace exists in the MCP config.
2. JSON-RPC initialize succeeds.
3. `tools/list` returns the required tool.
4. `tools/call` successfully invokes the required tool.
When a namespace returns EOF, follow
`docs/mcp-namespace-eof-recovery.md` in order:
## Recovery steps
1. Confirm blast radius (Gitea namespace vs all MCP servers).
2. **Reconnect the namespace through the client** (IDE reconnect / relaunch).
3. Do not repair via shell imports, raw JSON-RPC, PID kills, or config mtime
touches — those do not restore the client's closed transport.
4. Re-verify the **specific** required tool through the target namespace.
5. Resume review/merge only after a successful `client_namespace` assessment.
When a namespace returns EOF:
## Enforcement
1. Note the reported namespace, required tool, PID, profile, environment
summary, and config path.
2. Stop any stale MCP server process for that PID.
3. Reload or touch the MCP config so the IDE reconnects the namespace.
4. Verify the namespace command, args, profile env, and checkout path point at
the current repository.
5. Re-run `python3 test_mcp_conn.py --namespace <name>`.
6. Resume review or merge work only after the required tool call passes.
1. **State machine (read-only):** feed `blocks_merge_workflow` from a
`client_namespace` assessment into
`gitea_assess_review_merge_state_machine(live_namespace_broken=...)`.
2. **Live mutations:** `gitea_submit_pr_review` and `gitea_merge_pr` call
`_live_namespace_health_gate` and fail closed when the session has a
recorded unhealthy or non-client probe for the required namespace
(`gitea-reviewer` for review, `gitea-merger` for merge).
The read-only `gitea_assess_mcp_namespace_health` tool can classify probe
evidence supplied by a client. It intentionally distinguishes
`required_tool_registered=true` from `required_tool_callable=false`; the latter
must block reviewer and merger workflows until the live namespace is repaired.
## Enforcement in the review/merge state machine
The block is not advisory. Feed the `blocks_merge_workflow` verdict from
`gitea_assess_mcp_namespace_health` into
`gitea_assess_review_merge_state_machine` as `live_namespace_broken`:
```text
health = gitea_assess_mcp_namespace_health(namespace="gitea-merger", ...)
state = gitea_assess_review_merge_state_machine(
state_completion=...,
pre_merge_gates=...,
live_namespace_broken=health["blocks_merge_workflow"],
)
# state["merge"]["allowed"] is False whenever the live namespace is broken,
# even when every review state and pre-merge gate is otherwise satisfied.
```
When `live_namespace_broken=True`, `assess_workflow_blockers`,
`can_approve`, `can_merge`, and `workflow_status` all fail closed. This is the
guard that stops a merge from proceeding on a false-ready namespace, as in the
PR #418 halt that motivated this work (#543).
When blocked, repair the IDE namespace and re-record a healthy
`client_namespace` assessment before retrying the mutation.