A mid-run profile switch (reviewer -> author -> reviewer) left workflow-load proof, reviewer lease binding, the review decision lock, live namespace health, and preflight identity/capability stamps intact, so a formal verdict could be recorded under contaminated session state. - gitea_activate_profile now invalidates all review-critical session state on a cross-profile switch, in memory and in durable state keyed by either profile identity, and reports the invalidation + re-preflight requirement. - Full reviewer preflight (whoami, load_review_workflow, resolve_task_capability(review_pr), head re-pin, lease re-acquire) is required before any formal verdict after a switch; switching back cannot resurrect the stale run. - Namespace provenance: optional launcher-declared GITEA_MCP_NAMESPACE is reported by whoami/runtime context/capability resolution, and a declared namespace that disagrees with a task's required namespace fails closed. - Docs: supported pattern is separate session/namespace per role, not in-process profile hopping mid-review.
110 lines
4.6 KiB
Markdown
110 lines
4.6 KiB
Markdown
# 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
|
|
`client is closing: EOF`, `transport closed`, or an empty response when calling
|
|
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 **client-namespace** evidence
|
|
that the required tool is callable through the configured IDE MCP namespace.
|
|
|
|
## Probe sources (do not confuse them)
|
|
|
|
| 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
|
|
```
|
|
|
|
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 |
|
|
| --- | --- |
|
|
| `gitea-author` | `gitea_whoami` |
|
|
| `gitea-reviewer` | `gitea_whoami` |
|
|
| `gitea-merger` | `gitea_whoami` |
|
|
| `gitea-tools` | `gitea_list_profiles` |
|
|
|
|
## Recovery (canonical)
|
|
|
|
When a namespace returns EOF, follow
|
|
`docs/mcp-namespace-eof-recovery.md` in order:
|
|
|
|
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.
|
|
|
|
## Enforcement
|
|
|
|
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).
|
|
|
|
When blocked, repair the IDE namespace and re-record a healthy
|
|
`client_namespace` assessment before retrying the mutation.
|
|
|
|
## Namespace provenance (#690)
|
|
|
|
A server process cannot derive the name of the client-managed namespace it is
|
|
registered under, so the launcher may declare it with the
|
|
`GITEA_MCP_NAMESPACE` environment variable (e.g. `GITEA_MCP_NAMESPACE=gitea-reviewer`).
|
|
|
|
- `gitea_whoami`, `gitea_get_runtime_context`, and
|
|
`gitea_resolve_task_capability` report `namespace_provenance`: the declared
|
|
client namespace, the active execution profile, and — for tasks with a
|
|
required namespace (`review_pr`/`submit_review` → `gitea-reviewer`,
|
|
`merge_pr` → `gitea-merger`) — a `mismatch` verdict.
|
|
- A declared namespace that disagrees with the requested task's required
|
|
namespace **fails closed** (`allowed_in_current_session=false` with a STOP
|
|
guidance entry).
|
|
- An undeclared namespace is reported as `namespace_source="unknown"` and is
|
|
never treated as proof either way.
|
|
- A profile switch via `gitea_activate_profile` clears all recorded live
|
|
namespace-health assessments; re-probe through the client before further
|
|
review/merge mutations.
|
|
|