# rust-doctor
<p align="center">
<a href="https://crates.io/crates/rust-doctor"><img alt="Crates.io" src="https://img.shields.io/crates/v/rust-doctor?logo=rust"></a>
<a href="https://www.npmjs.com/package/rust-doctor"><img alt="npm" src="https://img.shields.io/npm/v/rust-doctor?logo=npm"></a>
<a href="https://docs.rs/rust-doctor"><img alt="docs.rs" src="https://img.shields.io/docsrs/rust-doctor?logo=docsdotrs&label=docs.rs"></a>
<a href="https://github.com/ArthurDEV44/rust-doctor/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/ArthurDEV44/rust-doctor/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://crates.io/crates/rust-doctor"><img alt="Downloads" src="https://img.shields.io/crates/d/rust-doctor?label=downloads"></a>
<a href="#license"><img alt="License" src="https://img.shields.io/crates/l/rust-doctor"></a>
<img alt="MSRV" src="https://img.shields.io/badge/MSRV-1.85-blue?logo=rust">
</p>
**The one-command health check for your Rust project.** rust-doctor scans for security, performance, correctness, architecture, and dependency issues, then folds everything into a single 0–100 score with diagnostics you can act on.
It runs `cargo clippy`, `cargo-audit`, `cargo-deny`, `cargo-geiger`, and 19 custom AST rules in one pass, and ships as a CLI, a library crate, an [MCP](https://modelcontextprotocol.io/) server, an npm package, and a GitHub Action — so it works in your terminal, your CI, and inside Claude Code, Cursor, or any MCP agent.
```console
$ rust-doctor # rust-doctor scanning its own codebase
◠ ◠ rust-doctor
▽ 99 / 100 Great
████████████████████████████████████████
Security 99 · Reliability 99 · Maintainability 100 · Performance 99 · Dependencies 99
✓ 0 errors ⚠ 44 warnings ℹ 42 infos · 60 files scanned in 32.9s
```
## Quickstart
No Rust toolchain required — `npx` downloads a pre-built native binary for your platform:
```bash
npx rust-doctor # scan the current directory and print the score
```
Prefer cargo? `cargo install rust-doctor`. Want it in your AI agent? `npx rust-doctor setup`. Other formats are in [Installation](#installation).
### See it in action →
https://github.com/user-attachments/assets/6766a5d8-9a47-4eb8-892e-76c1a23eb122
## Where it fits
Rust already has excellent point tools. rust-doctor runs them together, adds rules they don't cover, and turns the result into one number you can track over time.
| `cargo clippy` | 700+ built-in lints | Category + severity mapping, 19 custom AST rules (security, async, framework, architecture), and a single 0–100 score |
| `cargo audit` / `cargo deny` | CVE and supply-chain checks | One pass that also runs clippy, geiger, and machete — skipping gracefully when a tool isn't installed |
| Separate CI steps | Each tool's own output | One command with `--json`, `--sarif`, `--diff`, `--score`, and PR comments |
| Claude Code / Cursor | Code generation | An MCP server and a slash-command skill, so the agent scans, scores, and fixes as it writes |
## Features
- **700+ clippy lints** with explicit severity overrides and category mapping
- **19 custom AST rules** via [syn](https://crates.io/crates/syn): error handling, performance, security, async, architecture, and framework anti-patterns
- **Async anti-pattern detection**: blocking calls and `block_on` inside an async context
- **Framework-aware rules**: tokio, axum, actix-web — only run when the dependency is present
- **Supply-chain auditing**: CVEs via `cargo-audit`, bans/licenses via `cargo-deny`, unsafe via `cargo-geiger`, unused deps via `cargo-machete`
- **A 0–100 health score** across five weighted dimensions, with an ASCII doctor that reacts to the result
- **MCP server**: 4 read-only tools + 2 expert audit prompts for Claude Code, Cursor, Windsurf, or any MCP client
- **Diff mode**: `--diff` scans only changed files for fast CI feedback
- **Workspace support**: scan every crate or pick specific members
- **Inline suppression**: `// rust-doctor-disable-next-line <rule>`
- **Output modes**: terminal, `--json`, `--score` (bare integer for CI), `--sarif` (GitHub code scanning)
- **`--fix`**: apply machine-applicable fixes to source files
- **Setup wizard**: `rust-doctor setup` auto-detects Claude Code, Cursor, and Windsurf and wires up MCP or installs the skill in one command
- **Distributed everywhere**: CLI binary, library crate, MCP server, npm package, and GitHub Action
## Installation
### npm / npx (recommended for MCP users)
```bash
npx rust-doctor --mcp
```
Or install globally:
```bash
npm install -g rust-doctor
```
This downloads a pre-built native binary for your platform — no Rust toolchain required.
### cargo install (from source)
```bash
cargo install rust-doctor
```
### cargo binstall (pre-built binary)
```bash
cargo binstall rust-doctor
```
### Shell installer (Linux/macOS)
```bash
### PowerShell installer (Windows)
```powershell
### GitHub Releases
Download pre-built binaries from [GitHub Releases](https://github.com/ArthurDEV44/rust-doctor/releases).
Available platforms:
- `x86_64-unknown-linux-gnu`
- `aarch64-unknown-linux-gnu`
- `x86_64-apple-darwin`
- `aarch64-apple-darwin`
- `x86_64-pc-windows-msvc`
## Usage
```bash
# Scan current directory
rust-doctor
# Scan a specific directory
rust-doctor /path/to/project
# Get bare score for CI
rust-doctor --score
# JSON output
rust-doctor --json
# Scan only changed files
rust-doctor --diff
# Scan against a specific branch
rust-doctor --diff main
# Fail CI on errors
rust-doctor --fail-on error
# Scan specific workspace members
rust-doctor --project core,api
# Verbose output with file:line details
rust-doctor --verbose
# Install missing external tools (cargo-deny, cargo-audit, etc.)
rust-doctor --install-deps
# Run as MCP server
rust-doctor --mcp
# Setup wizard — configure AI agents automatically
rust-doctor setup
```
## Exit Codes
rust-doctor returns distinct exit codes so CI pipelines can tell a quality-gate
failure apart from a crash:
| `0` | Success — scan completed and all quality gates passed |
| `1` | Setup error — MCP server, setup wizard, or `--install-deps` failed |
| `2` | Scan error — project discovery/compile failure or output rendering failed |
| `3` | Quality gate failed — score below `[score] fail_below`, or `--fail-on` threshold reached |
Gate the build on a quality failure without masking a crash:
```bash
rust-doctor --fail-on error
if [ $? -eq 3 ]; then
echo "Quality gate failed"
exit 1
fi
```
## AI Agent Setup (recommended)
The fastest way to integrate rust-doctor with your AI coding agent:
```bash
npx rust-doctor@latest setup
```
The wizard auto-detects installed agents (Claude Code, Cursor, Windsurf) and lets you choose:
- **CLI + Skills** (default) — installs a `SKILL.md` that teaches your agent to use the rust-doctor CLI with deep analysis capabilities
- **MCP Server** — configures the `rust-doctor --mcp` stdio server in your agent's config file
The wizard handles detection, configuration, and verification in one command. For manual setup, see the sections below.
## MCP Server
rust-doctor includes a built-in [Model Context Protocol](https://modelcontextprotocol.io/) server, allowing AI coding assistants to scan and analyze Rust projects directly.
### Tools
| `scan` | Scan a Rust project for code health issues. Returns diagnostics with a 0–100 health score. |
| `score` | Get the health score (0–100) of a Rust project as a single integer. |
| `explain_rule` | Get a detailed explanation of a rule: what it checks, why it matters, and how to fix violations. |
| `list_rules` | List all available rules with their categories and severities. |
All tools are read-only (`readOnlyHint: true`).
### Prompts
| `deep-audit` | Comprehensive 6-phase expert audit: codebase exploration, static analysis, deep code review (51-item checklist), best practices research, synthesis report, and remediation choices (implement all / generate PRD / manual). |
| `health-check` | Quick scan + prioritized remediation plan (P0–P3) + fix workflow. |
### Claude Code
**Automatic setup (recommended):**
```bash
rust-doctor setup # detects Claude Code and configures MCP or installs skill
```
**Or one-command MCP install:**
```bash
claude mcp add --transport stdio rust-doctor -- npx -y rust-doctor --mcp
```
**Or via Claude Code plugin:**
```
/plugin install rust-doctor@ArthurDEV44/rust-doctor
```
**Or add manually** to your `~/.claude/settings.json`:
```json
{
"mcpServers": {
"rust-doctor": {
"command": "rust-doctor",
"args": ["--mcp"]
}
}
}
```
**Or share with your team** via `.mcp.json` in your project root (committed to git):
```json
{
"mcpServers": {
"rust-doctor": {
"command": "npx",
"args": ["-y", "rust-doctor", "--mcp"]
}
}
}
```
### Cursor
Add to your `.cursor/mcp.json`:
```json
{
"mcpServers": {
"rust-doctor": {
"command": "npx",
"args": ["-y", "rust-doctor", "--mcp"]
}
}
}
```
### VS Code
Add to your `.vscode/settings.json`:
```json
{
"mcp": {
"servers": {
"rust-doctor": {
"type": "stdio",
"command": "npx",
"args": ["-y", "rust-doctor", "--mcp"]
}
}
}
}
```
### Windsurf
Add to your `~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"rust-doctor": {
"command": "npx",
"args": ["-y", "rust-doctor", "--mcp"]
}
}
}
```
### Other MCP clients
rust-doctor uses stdio transport. Any MCP client that supports stdio can connect by running `rust-doctor --mcp`.
Built with [rmcp](https://crates.io/crates/rmcp) v1.x (official Rust MCP SDK).
## Claude Code Skill (no MCP required)
If you prefer slash commands over MCP servers, rust-doctor ships a Claude Code skill.
**Automatic install (recommended):**
```bash
rust-doctor setup # choose "CLI + Skills", select Claude Code
```
**Or via npx:**
```bash
npx skills add https://github.com/ArthurDEV44/rust-doctor --skill rust-doctor
```
**Or copy manually:**
```bash
cp -r skills/rust-doctor/ ~/.claude/skills/rust-doctor/
```
**Usage:**
```
/rust-doctor # scan current project
/rust-doctor --diff # scan changed files only
/rust-doctor --fix # scan + apply fixes
/rust-doctor --plan # scan + remediation plan
/rust-doctor src/ # scan a specific directory
```
The skill runs the `rust-doctor` CLI under the hood, parses the output, categorizes findings by priority, and provides actionable fix guidance with before/after code.
## GitHub Actions
```yaml
- uses: ArthurDEV44/rust-doctor@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
fail-on: warning
```
The action posts a PR comment with the health score, error/warning counts, and top diagnostics.
## Configuration
Create a `rust-doctor.toml` in your project root, or add `[package.metadata.rust-doctor]` to your `Cargo.toml`:
```toml
# rust-doctor.toml
verbose = false
fail_on = "none"
[ignore]
rules = ["excessive-clone", "string-from-literal"]
files = ["**/generated/**", "tests/**"]
```
CLI flags override config file values.
## Inline Suppression
```rust
// rust-doctor-disable-next-line unwrap-in-production
let value = some_option.unwrap();
let x = risky_call(); // rust-doctor-disable-line
```
## Rules
### Custom AST Rules (19 rules) — heuristic
These rules analyze the syntax tree only (via `syn`): no type resolution, no
macro expansion. They run fast and offline, but emit a **heuristic** signal, not
a type-checked verdict — calibrate confidence accordingly. Findings are tagged
`heuristic` in `--json` and SARIF output, and marked `~heuristic` in the
terminal. Rules flagged ⚠ have known structural blind spots (see below).
| Error Handling | `unwrap-in-production` ⚠ | Warning |
| Error Handling | `panic-in-library` | Error |
| Error Handling | `box-dyn-error-in-public-api` | Warning |
| Error Handling | `result-unit-error` | Warning |
| Performance | `excessive-clone` | Warning |
| Performance | `string-from-literal` | Info |
| Performance | `collect-then-iterate` | Warning |
| Performance | `large-enum-variant` ⚠ | Warning |
| Performance | `unnecessary-allocation` | Warning |
| Architecture | `high-cyclomatic-complexity` | Warning |
| Security | `hardcoded-secrets` | Error |
| Security | `unsafe-block-audit` | Warning |
| Security | `sql-injection-risk` ⚠ | Error |
| Async | `blocking-in-async` ⚠ | Error |
| Async | `block-on-in-async` | Error |
| Framework | `tokio-main-missing` | Error |
| Framework | `tokio-spawn-without-move` | Error |
| Framework | `axum-handler-not-async` | Warning |
| Framework | `actix-blocking-handler` | Warning |
#### Known heuristic limitations (⚠)
Without type information these rules have documented blind spots. They're still
worth surfacing, but a finding is a prompt to look, not a confirmed defect:
- `unwrap-in-production` — matches `.unwrap()`/`.expect()` syntactically; cannot tell a provably-infallible unwrap from a risky one.
- `large-enum-variant` — counts a variant's fields, not its byte size; a few wide-type fields can outweigh many small ones.
- `blocking-in-async` — flags known blocking calls by name inside async fns; cannot follow calls into other functions or resolve aliased imports.
- `sql-injection-risk` — flags string-built queries heuristically; cannot confirm the interpolated value is actually untrusted input.
### Clippy Lints (75+ with overrides) — type-aware
rust-doctor runs `cargo clippy` with pedantic, nursery, and cargo lint groups. 75+ lints have explicit category and severity overrides across: Error Handling, Performance, Security, Correctness, Architecture, Cargo, Async, Style. Unlike the custom rules above, clippy resolves types against the compiler, so its findings are more authoritative — they are **not** tagged `heuristic`.
### External Tools (optional, auto-detected)
These tools are optional — rust-doctor gracefully skips any that are missing and shows which passes were skipped. Run `rust-doctor --install-deps` to install them all at once.
| clippy | `rustup component add clippy` | 700+ lint checks |
| cargo-deny | `cargo install cargo-deny` | Supply-chain checking (advisories, licenses, bans) |
| cargo-audit | `cargo install cargo-audit` | CVE vulnerability scanning |
| cargo-geiger | `cargo install cargo-geiger` | Unsafe code auditing across dependency tree |
| cargo-machete | `cargo install cargo-machete` | Unused dependency detection |
| cargo-semver-checks | `cargo install cargo-semver-checks` | Semver violation detection |
## Library Usage
rust-doctor is available as a library crate:
```rust
use std::path::Path;
// Discover the project (finds Cargo.toml, loads config)
let (dir, info, config) = rust_doctor::discovery::bootstrap_project(
Path::new("/path/to/project"), false,
)?;
// Resolve config with defaults
let resolved = rust_doctor::config::resolve_config_defaults(config.as_ref());
// Run the scan
let result = rust_doctor::scan::scan_project(&info, &resolved, false, &[], true)?;
println!("Score: {}/100 ({})", result.score, result.score_label);
```
Full API docs are on [docs.rs/rust-doctor](https://docs.rs/rust-doctor).
## Score Calculation
**Read the 0–100 score as a compass, not a thermometer.** It points you toward
the weakest dimension; it isn't a precision measurement. The per-dimension
scores (shown in the terminal box and in `--json`) carry the real signal — they
tell you *where* to act.
### How it's computed
The score is a weighted average across 5 dimensions:
| Security | ×2.0 | Security rules (hardcoded secrets, unsafe, SQL injection) |
| Reliability | ×1.5 | Correctness, error handling, async, framework |
| Maintainability | ×1.0 | Architecture, style |
| Performance | ×1.0 | Performance |
| Dependencies | ×1.0 | Cargo, dependencies, advisory findings (RUSTSEC / cargo-deny) |
Each dimension starts at 100 and loses points per **unique rule** violated,
weighted by severity:
`dimension = 100 − (unique_error_rules × 1.5) − (unique_warning_rules × 0.75) − (unique_info_rules × 0.25)`
The dimension is clamped to `[0, 100]`, and the overall score is the weighted
average of the five, also clamped to `[0, 100]`.
The score counts unique rules, not occurrences — fixing one `.unwrap()` won't
move it, but removing the last `.unwrap()` drops the penalty entirely.
| 75–100 | Great | ◠ ◠ |
| 50–74 | Needs work | • • |
| 0–49 | Critical | x x |
### Known limits
- **Dimension saturation.** Penalties are linear and the floor is 0, so once a
dimension accumulates ~67 distinct Error-severity rules (`100 ÷ 1.5`), it sits
at 0 and further distinct rules in that dimension stop moving the number — it's
directional past that point, not proportional.
- **Heuristic inputs.** The custom AST rules are `syn`-only (no types, no macro
expansion), so part of what feeds the score is a heuristic signal — see
[Rules](#rules). Clippy and external-tool findings are type-aware. The score
does not currently weight heuristic vs type-aware findings differently.
- **Hand-tuned weights.** The dimension weights and severity penalties are
deliberate but not empirically calibrated; treat cross-project score
comparisons with caution.
- **Empty projects.** A directory with no Rust source files scores 100 and emits
`No Rust source files found` — expected, not a clean bill of health.
## Contributing
Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for the dev
setup, the CI gates to run before opening a PR (`cargo fmt`, `cargo clippy`,
`cargo test`), and the guide to authoring a new rule. By participating you agree
to the [Code of Conduct](CODE_OF_CONDUCT.md). For security issues, follow the
[Security Policy](SECURITY.md) — please don't open a public issue.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.