safe-fix-mcp
# safe-fix-mcp
<!-- mcp-name: io.github.pamela-0/safe-fix-mcp -->
[](https://pypi.org/project/safe-fix-mcp/) [](LICENSE)
A real MCP server that finds dead code in a Python repository and proposes a **branch + PR** for the one class of finding that's actually safe to auto-remove: unused imports on a single-name import line. Everything else it finds — unused functions, classes, unused dependencies — stays report-only. A human always merges. This tool never merges anything itself.
## Why this exists
Most "dead code" tools stop at reporting. The ones that go further usually delete with no safety net. This does neither: it drafts a real, minimal, reviewable PR, gated on the target repo's own full test suite actually passing after the removal — not a heuristic, a real `pytest` run. If the suite fails, nothing is committed or pushed; the repo is left exactly as it started.
## Tools
### `scan_dead_code(repo_path=".", min_confidence=60)`
Read-only. Runs [vulture](https://github.com/jendrikseipp/vulture) (dead code) and [deptry](https://github.com/fpgmaas/deptry) (dependency issues) and returns a human-readable list of findings. Never modifies anything.
### `propose_removal_pr(repo_path=".")`
- Refuses on a dirty working tree — never edits on top of uncommitted work.
- Filters to unused imports at ≥90% vulture confidence, on single-name import lines only (`from x import y, z` is skipped — removing the whole line would silently remove `z` too).
- Creates a real branch, removes the qualifying imports, runs the repo's real full test suite.
- Only on a real pass: commits, pushes, and tries to open a PR via `gh pr create`.
- If `gh` isn't installed/authenticated, the branch is still committed and pushed for real — only PR creation degrades, with the real error returned so you can open it manually.
## Install
```bash
pip install safe-fix-mcp
```
Add to your MCP client config (e.g. Claude Code):
```bash
claude mcp add safe-fix-mcp -- safe-fix-mcp
```
Or run directly for local testing:
```bash
python -m safe_fix_mcp.server
```
Installing from source instead of PyPI:
```bash
pip install -e .
```
## Skill
A `SKILL.md` ships inside this same package — not a separate download — and teaches Claude when and how to use the two tools correctly (e.g. that `propose_removal_pr` only ever auto-removes single-name unused imports, never functions/classes/dependencies). Install it into your project:
```bash
safe-fix-mcp-install-skill
```
Run it from the project root you want the skill active in — it writes to `.claude/skills/safe-fix-mcp/SKILL.md` relative to your current directory.
## Requirements
- Python ≥ 3.10
- `git` on PATH
- `gh` (GitHub CLI) on PATH and authenticated, if you want `propose_removal_pr` to actually open the PR — without it, the branch still gets pushed for real, and the tool tells you to open the PR by hand.
## Development
```bash
pip install -e ".[dev]"
pytest
```
## Known limitation
`vulture` flags `scan_dead_code`/`propose_removal_pr` themselves as "unused" — a known false-positive class, not a real bug: they're dispatched by the `@mcp.tool()` decorator at runtime, not called directly anywhere in the source, so static call-graph analysis can't see the real caller (the MCP framework itself).
## Verifying it for real
`scripts/verify_real_client.py` launches the packaged server as a real subprocess and talks to it with the real `mcp.client.ClientSession` — the same path a real MCP client uses. Useful as a smoke test after any change:
```bash
python scripts/verify_real_client.py
```
TDQS
Scored across 2 tools
scan_dead_code is purely read-only analysis, while propose_removal_pr actually creates a branch and PR. Their purposes are complementary and unlikely to be confused.
Both tool names use lower_snake verb-first conventions: scan_dead_code and propose_removal_pr. The pattern is predictable, though the second name is slightly less clean because 'removal_pr' bundles an action and an object into one noun phrase.
Two tools is on the thin side and sits at the borderline of feeling complete. However, each tool has a distinct role in a focused analyze-then-propose workflow, so the small count is plausible for a narrow server.
The scan surfaces both dead code and dependency issues, but propose_removal_pr only handles unused imports. Dependency fixes and non-import dead-code removals are therefore dead ends, creating a notable gap in the advertised safe-fix domain.