Metadata-Version: 2.5
Name: safe-fix-mcp
Version: 0.1.1
Summary: MCP server: finds real dead code (vulture + deptry) and proposes a branch+PR for the one class safe to auto-remove -- unused imports on a single-name line -- gated on a real full test-suite pass. A human still merges.
Project-URL: Homepage, https://github.com/pamela-0/safe-fix-mcp
Project-URL: Repository, https://github.com/pamela-0/safe-fix-mcp
Project-URL: Issues, https://github.com/pamela-0/safe-fix-mcp/issues
Author-email: pamela-0 <154478423+pamela-0@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: automated-pr,claude,claude-code,code-quality,dead-code,deptry,mcp,model-context-protocol,static-analysis,vulture
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: deptry>=0.20
Requires-Dist: mcp[cli]>=1.26
Requires-Dist: vulture>=2.11
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# safe-fix-mcp

<!-- mcp-name: io.github.pamela-0/safe-fix-mcp -->

[![PyPI](https://img.shields.io/pypi/v/safe-fix-mcp)](https://pypi.org/project/safe-fix-mcp/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 .
```

## 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
```
