diff options
| author | main <main@swarm.moe> | 2026-03-19 15:49:41 -0400 |
|---|---|---|
| committer | main <main@swarm.moe> | 2026-03-19 15:49:41 -0400 |
| commit | fa1bd32800b65aab31ea732dd240261b4047522c (patch) | |
| tree | 2fd08af6f36b8beb3c7c941990becc1a0a091d62 /README.md | |
| download | adequate-rust-mcp-fa1bd32800b65aab31ea732dd240261b4047522c.zip | |
Release adequate-rust-mcp 1.0.0v1.0.0
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..640056e --- /dev/null +++ b/README.md @@ -0,0 +1,221 @@ +# adequate-rust-mcp + +Industrial-grade, self-healing MCP server for `rust-analyzer`. + +## Dependency Note + +This workspace currently consumes `libmcp` from the public swarm Git surface: + +- `https://git.swarm.moe/libmcp.git` + +Cargo will fetch that dependency automatically on first build. + +## Crates + +- `ra-mcp-domain`: strict domain types and lifecycle algebra +- `ra-mcp-engine`: resilient rust-analyzer transport + supervisor +- `adequate-rust-mcp`: MCP stdio server exposing analysis tools + +## Guarantees + +- automatic recovery from broken pipes and dead workers +- bounded request/startup deadlines +- typed boundaries for lifecycle, faults, and source positions +- fault-injection integration tests for restart behavior + +## Runtime Modes + +- default (`adequate-rust-mcp`): stable host process (Codex-facing stdio endpoint) +- worker (`adequate-rust-mcp --worker`): actual MCP tool server process + +The host supervises worker generations, drains pending requests with explicit JSON-RPC +errors on worker loss, and automatically respawns. + +## Quick Start + +From the workspace root: + +```bash +cargo run -p adequate-rust-mcp +``` + +To launch the disposable worker directly: + +```bash +cargo run -p adequate-rust-mcp -- --worker +``` + +## Live Reload + +Host mode watches the worker binary path for updates. On Unix it self-reexecs into +the new binary and restores live session state before spawning the next worker generation; +on other platforms it falls back to worker-only restart. It replays `initialize` and +reconstructs `notifications/initialized` if needed so replacement workers come up fully live. +In-flight requests are replayed across host/worker restarts with bounded at-least-once semantics, +so transient rollout and recovery churn is hidden from clients by default. + +Environment knobs: + +- `ADEQUATE_MCP_WORKER_BINARY`: worker binary path to execute/watch (default: current executable) +- `ADEQUATE_MCP_HOST_RELOAD_DEBOUNCE_MS`: file-change debounce before reload (default: `250`) +- `ADEQUATE_MCP_HOST_RESPAWN_FLOOR_MS`: initial respawn backoff floor (default: `100`) +- `ADEQUATE_MCP_HOST_RESPAWN_CEILING_MS`: respawn backoff ceiling (default: `3000`) +- `ADEQUATE_MCP_HOST_QUEUE_CAPACITY`: max queued inbound frames while recovering (default: `512`) +- `ADEQUATE_MCP_HOST_MAX_REPLAY_ATTEMPTS`: replay budget per in-flight request before surfacing an error (default: `8`) + +## Tool Surface + +In addition to typed core tools (`hover`, `definition`, `references`, `rename_symbol`, +`diagnostics`, `clippy_diagnostics`, `fix_everything`, `health_snapshot`, `telemetry_snapshot`), the server exposes +`advanced_lsp_request` for +editing-heavy and advanced rust-analyzer capabilities: + +- completions +- completion/resolve +- code actions + resolve +- code lens + resolve +- prepare rename +- rename +- execute command +- formatting (document/range/on-type) +- symbols + workspace symbol resolve +- document links + resolve +- colors + color presentation +- linked editing range, inline value, moniker +- document/workspace diagnostics +- selection/folding/inlay hints + inlay resolve +- semantic tokens (full/range/full-delta), call hierarchy, type hierarchy + +`hover`, `definition`, `references`, `health_snapshot`, and `telemetry_snapshot` +also default to line-oriented porcelain text with `render=json` opt-in. + +`diagnostics` and `clippy_diagnostics` split detail from presentation: + +- `mode`: `compact` (default), `full`, or `summary` +- `render`: `porcelain` (default line-oriented text) or `json` +- default responses are model-friendly porcelain text in `content` +- `render=json` exposes the structured payload and schema for programmatic consumers +- empty `definition`/`references`/`hover` results may include an indexing note during early rust-analyzer warm-up when the empty result could be a false negative +- transient early-startup `unlinked-file` / `not part of any crate` diagnostics are retried during the same warm-up window before surfacing to clients + +## One-Stop Autofix + +`fix_everything` is the do-the-obvious thing button for agent loops. +It runs, in order: + +- workspace formatter command (default: `cargo fmt --all`) +- workspace clippy fix command (default: `cargo clippy --fix --workspace --all-targets --all-features --allow-dirty --allow-staged`) + +The output is structured and model-friendly: + +- `success`: whether all steps exited with status `0` +- `workspace_root`: resolved workspace root path +- `steps`: per-step command argv, exit code, and bounded stdout/stderr excerpts + +## Input Normalization + +User-facing tool inputs are intentionally permissive and normalized at the boundary: + +- `file_path` accepts absolute paths, `file://` URIs, and relative paths (resolved against workspace root) +- position indices accept numeric strings and integer-like floats +- `0` indices are normalized to `1` for one-indexed fields +- alias spellings are accepted for common fields (`filePath`, `lineNumber`, `character`, `newName`, etc.) +- `advanced_lsp_request.method` accepts snake_case, camelCase, kebab-case, and full LSP method paths +- `diagnostics.mode` accepts `compact` (default), `full` (aliases: `raw`, `verbose`), or `summary` +- `diagnostics.render` accepts `porcelain` (default; aliases: `text`, `plain`, `plain_text`) or `json` (alias: `structured`) +- `diagnostics.max_items` and `diagnostics.max_message_chars` accept numeric strings and integer-like floats +- `diagnostics` and `clippy_diagnostics` accept either a single file (`file_path`) or many files (`file_paths`/`files`/`paths`) +- `clippy_diagnostics` accepts the same render controls as `diagnostics` +- multi-file diagnostics preserve requested file order and return one fused diagnostics list +- `render=json` compact diagnostics use descriptive field names (`severity`, `file_path`, `start_line`, etc.) and descriptive counters (`error_count`, `total_count`, ...) + +## Clippy Gate Integration + +`clippy_diagnostics` runs `cargo clippy` and filters results to the requested file. +Strictness lives in workspace lint configuration: + +- `[workspace.lints.clippy]` in `Cargo.toml` is the canonical policy (`all` + `pedantic` with explicit carve-outs) +- `./check.py` runs `cargo clippy` without inline lint flags, so commit-gate behavior is sourced from `Cargo.toml` + +The MCP worker executes: + +- `cargo clippy --workspace --all-targets --all-features --message-format=json` + +To override command shape (for example, alternate target selection), configure: + +```toml +[workspace.metadata.adequate-rust-mcp] +format_command = ["cargo", "fmt", "--all"] +clippy_command = [ + "cargo", + "clippy", + "--workspace", + "--all-targets", + "--all-features", + "--message-format=json", +] +fix_command = [ + "cargo", + "clippy", + "--fix", + "--workspace", + "--all-targets", + "--all-features", + "--allow-dirty", + "--allow-staged", +] +``` + +## Telemetry + +Use `telemetry_snapshot` as first-line triage when the system slows down or starts flapping. +It reports: + +- lifecycle state + generation +- uptime +- consecutive failures and restart count +- total request counters (success, response error, transport fault, retries) +- per-method counters and latency aggregates (`last`, `avg`, `max`) +- last observed method-level error text +- latest restart-triggering fault + +Useful choke signatures: + +- rising `transport_fault_count` + `restart_count`: rust-analyzer process instability or I/O link failure +- rising `response_error_count` with low restart count: semantic/query misuse or LSP-level request issues +- high `avg_latency_ms`/`max_latency_ms` in one method: method-specific hot spot in large workspaces +- non-zero `consecutive_failures` while state is `recovering`: active degradation loop + +## Host JSONL Telemetry (XDG State) + +The host process also emits append-only JSONL telemetry for path-level heat/error analysis. + +- default path: `$XDG_STATE_HOME/adequate-rust-mcp/telemetry.jsonl` +- fallback path: `$HOME/.local/state/adequate-rust-mcp/telemetry.jsonl` +- snapshot cadence: `ADEQUATE_MCP_TELEMETRY_SNAPSHOT_EVERY` (default: `100`) + +Each tool call writes a `tool_call` event with: + +- `repo_root` +- `request_id` +- `tool_name` and optional `lsp_method` +- `path_hint` (best-effort normalized path extraction) +- `latency_ms`, `replay_attempts` +- `outcome` (`ok`/`error`) +- optional `error_code`, `error_kind`, `error_message` + +Every N calls, and once on shutdown, the host writes `hot_paths_snapshot` with: + +- `hottest_paths` (highest request volume) +- `slowest_paths` (highest average latency) +- `error_count` per path + +## QA Checklist + +For a lean live MCP smoke pass against this repo, run: + +```bash +python3 qa_checklist.py +``` + +The literal step list lives in `docs/qa-checklist.md`. |