A session that ran `pkill -f mcp_server.py`, waited for the IDE to respawn the daemons, and then closed an issue left no trace distinguishing that closure from one performed over a sanctioned runtime. Detection existed only as advisory strings (`native_mcp_preference.classify_command_path`, `review_workflow_boundary`) and never failed closed on the mutations that followed. Adds `runtime_recovery_guard.py`, mirroring the #671 stable-branch contamination model so the two cannot drift apart: classification of kill/pkill/killall commands and known-pid kills, a durable `runtime_recovery_contamination` marker, a fail-closed pre-flight gate over the shared review/merge/close/completion task set, and reconciler-only clearing. `comment_issue` and `lock_issue` stay allowed so a contaminated worker can still post its audit comment and hand off. The marker is recovery-critical, so contamination cannot expire into cleanliness with the session-state TTL. Read-only inspection (`ps aux | grep mcp_server`), sanctioned reconnects, and process management unrelated to the daemons are never flagged; a bare `kill` of an unknown pid is reported as ambiguous rather than contaminating, so ordinary subprocess work is not false-blocked. A pattern broad enough to sweep unrelated namespaces (`pkill -f python`) is contamination even when it never names MCP. Operator-authorized host maintenance stays permitted, but the authorization is read from GITEA_OPERATOR_DAEMON_MAINTENANCE_AUTHORIZATION in the process environment only and never from a tool argument, so a session cannot authorize itself. Final-report rules reject clean-session claims and require the contaminated recovery to be surfaced. Two tools are registered (inventory 112 to 114) and the sanctioned-versus-forbidden contrast is documented in the namespace recovery doc and the workflow skill. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
173 lines
5.9 KiB
Markdown
173 lines
5.9 KiB
Markdown
# Registered MCP tool inventory
|
|
|
|
This is the canonical list of tools the Gitea-Tools MCP server registers. It
|
|
exists because documentation and the registered inventory drifted: the workflow
|
|
documented a `gitea_edit_issue` tool that no namespace had ever registered, so a
|
|
mutation could be planned against a tool that did not exist and only fail at
|
|
execution time (#781).
|
|
|
|
## The rule
|
|
|
|
**Documentation must never name a tool an actor cannot reach.**
|
|
|
|
Two guards enforce it, both in `tests/test_issue_781_edit_issue_tool.py`:
|
|
|
|
1. The list below must equal the registered tool set exactly — sorted, no
|
|
duplicates, nothing missing in either direction. Adding a tool without
|
|
documenting it fails, and documenting a tool without registering it fails.
|
|
2. Every backticked `gitea_*` / `mcp_*` identifier in `skills/**/*.md` must be a
|
|
registered tool. Module and script names that share the prefix are listed
|
|
explicitly in `mcp_tool_inventory.NON_TOOL_IDENTIFIERS` rather than being
|
|
waved through by a looser pattern.
|
|
|
|
## Updating this file
|
|
|
|
When you add or remove an `@mcp.tool()`, regenerate the block below:
|
|
|
|
```bash
|
|
PYTEST_CURRENT_TEST=1 venv/bin/python -c "
|
|
import mcp_server, mcp_tool_inventory
|
|
print(mcp_tool_inventory.render_inventory_block(
|
|
mcp_server.mcp._tool_manager._tools))
|
|
"
|
|
```
|
|
|
|
Replace everything between the markers with that output. Do not hand-edit
|
|
individual entries — the generator and the guard share one ordering rule.
|
|
|
|
## Registered tools
|
|
|
|
Namespaces (`gitea-tools`, `gitea-reviewer`, `gitea-merger`, `gitea-reconciler`)
|
|
register the same tool set; what differs per namespace is the execution profile
|
|
that gates each call, not which tools exist.
|
|
|
|
<!-- BEGIN REGISTERED TOOL INVENTORY -->
|
|
|
|
- `gitea_abandon_workflow_lease`
|
|
- `gitea_acquire_conflict_fix_lease`
|
|
- `gitea_acquire_merger_pr_lease`
|
|
- `gitea_acquire_reviewer_pr_lease`
|
|
- `gitea_activate_profile`
|
|
- `gitea_adopt_merger_pr_lease`
|
|
- `gitea_adopt_workflow_lease`
|
|
- `gitea_allocate_next_work`
|
|
- `gitea_assess_already_landed_reconciliation`
|
|
- `gitea_assess_conflict_fix_classification`
|
|
- `gitea_assess_conflict_fix_push`
|
|
- `gitea_assess_gitea_operation_path`
|
|
- `gitea_assess_master_parity`
|
|
- `gitea_assess_mcp_namespace_health`
|
|
- `gitea_assess_pr_sync_status`
|
|
- `gitea_assess_review_merge_state_machine`
|
|
- `gitea_assess_reviewer_pr_lease`
|
|
- `gitea_assess_terminal_label_hygiene`
|
|
- `gitea_assess_work_issue_duplicate`
|
|
- `gitea_assess_worktree_cleanup_integrity`
|
|
- `gitea_audit_config`
|
|
- `gitea_audit_runtime_recovery_contamination`
|
|
- `gitea_audit_stable_branch_contamination`
|
|
- `gitea_audit_worktree_cleanup`
|
|
- `gitea_authorize_reconciliation_cleanup_phase`
|
|
- `gitea_authorize_review_correction`
|
|
- `gitea_capability_stop_terminal_report`
|
|
- `gitea_capture_branches_worktree_snapshot`
|
|
- `gitea_check_pr_eligibility`
|
|
- `gitea_cleanup_merged_pr_branch`
|
|
- `gitea_cleanup_obsolete_reviewer_comment_lease`
|
|
- `gitea_cleanup_post_merge_moot_lease`
|
|
- `gitea_cleanup_stale_claims`
|
|
- `gitea_cleanup_stale_review_decision_lock`
|
|
- `gitea_cleanup_terminal_pr_labels`
|
|
- `gitea_close_issue`
|
|
- `gitea_commit_files`
|
|
- `gitea_consume_irrecoverable_decision_lock_provenance`
|
|
- `gitea_create_issue`
|
|
- `gitea_create_issue_comment`
|
|
- `gitea_create_label`
|
|
- `gitea_create_pr`
|
|
- `gitea_delete_branch`
|
|
- `gitea_diagnose_review_decision_lock`
|
|
- `gitea_diagnose_reviewer_pr_lease_handoff`
|
|
- `gitea_diagnose_terminal`
|
|
- `gitea_dry_run_pr_review`
|
|
- `gitea_edit_issue`
|
|
- `gitea_edit_pr`
|
|
- `gitea_expire_workflow_leases`
|
|
- `gitea_get_authenticated_user`
|
|
- `gitea_get_current_user`
|
|
- `gitea_get_file`
|
|
- `gitea_get_pr_review_feedback`
|
|
- `gitea_get_profile`
|
|
- `gitea_get_runtime_context`
|
|
- `gitea_get_shell_health`
|
|
- `gitea_heartbeat_reviewer_pr_lease`
|
|
- `gitea_inspect_workflow_lease`
|
|
- `gitea_issue_irrecoverable_provenance_authorization`
|
|
- `gitea_list_dependency_edges`
|
|
- `gitea_list_issue_comments`
|
|
- `gitea_list_issues`
|
|
- `gitea_list_labels`
|
|
- `gitea_list_profiles`
|
|
- `gitea_list_prs`
|
|
- `gitea_list_workflow_leases`
|
|
- `gitea_load_review_workflow`
|
|
- `gitea_lock_issue`
|
|
- `gitea_mark_final_review_decision`
|
|
- `gitea_mark_issue`
|
|
- `gitea_merge_pr`
|
|
- `gitea_mirror_refs`
|
|
- `gitea_observability_link_issue`
|
|
- `gitea_observability_list_projects`
|
|
- `gitea_observability_reconcile_incident`
|
|
- `gitea_post_heartbeat`
|
|
- `gitea_quarantine_contaminated_review`
|
|
- `gitea_reclaim_expired_workflow_lease`
|
|
- `gitea_reconcile_already_landed_pr`
|
|
- `gitea_reconcile_issue_claims`
|
|
- `gitea_reconcile_merged_cleanups`
|
|
- `gitea_reconcile_superseded_by_merged_pr`
|
|
- `gitea_record_daemon_process_kill_attempt`
|
|
- `gitea_record_irrecoverable_decision_lock_provenance`
|
|
- `gitea_record_pre_review_command`
|
|
- `gitea_record_shell_spawn_outcome`
|
|
- `gitea_record_stable_branch_push_attempt`
|
|
- `gitea_release_merger_pr_lease`
|
|
- `gitea_release_reviewer_pr_lease`
|
|
- `gitea_release_workflow_lease`
|
|
- `gitea_resolve_task_capability`
|
|
- `gitea_resume_review_draft`
|
|
- `gitea_review_pr`
|
|
- `gitea_route_task_session`
|
|
- `gitea_save_review_draft`
|
|
- `gitea_scan_already_landed_open_prs`
|
|
- `gitea_sentry_get_issue_events`
|
|
- `gitea_sentry_link_gitea_issue`
|
|
- `gitea_sentry_list_issues`
|
|
- `gitea_sentry_reconcile_issue`
|
|
- `gitea_sentry_watchdog`
|
|
- `gitea_set_issue_labels`
|
|
- `gitea_submit_pr_review`
|
|
- `gitea_update_pr_branch_by_merge`
|
|
- `gitea_validate_review_final_report`
|
|
- `gitea_view_issue`
|
|
- `gitea_view_pr`
|
|
- `gitea_whoami`
|
|
- `gitea_workflow_dashboard`
|
|
- `mcp_check_workflow_skill_preflight`
|
|
- `mcp_get_control_plane_guide`
|
|
- `mcp_get_skill_guide`
|
|
- `mcp_list_project_skills`
|
|
|
|
<!-- END REGISTERED TOOL INVENTORY -->
|
|
|
|
## Issue-content editing
|
|
|
|
`gitea_edit_issue` is the only path that changes an issue's title or body. It
|
|
PATCHes the issue endpoint, refuses a pull-request number, sends only the fields
|
|
the caller named, and proves the result by read-after-write — including that
|
|
state, labels, assignees, and milestone did not move.
|
|
|
|
`gitea_edit_pr` remains pull-request-only. The two paths never merge: a single
|
|
tool that accepted either kind would make the narrower capability reachable
|
|
through the wider one.
|