RepoDoctor MCP Server
by daamles
README.md
<!-- repodoctor: ignore-file (this README quotes example findings) -->
<div align="center">
# ๐ฉบ RepoDoctor
**A read-only health check for your repository โ leaked secrets, code quality and project structure, in one command.**
[](https://github.com/daamles/repodoctor/actions/workflows/ci.yml)
[](pyproject.toml)
[](LICENSE)
[](#use-it-from-claude-mcp)
_made by **daamles**_
</div>
RepoDoctor scans a project and tells you what you would rather find before
`git push` does: API keys sitting in source files, forgotten `FIXME` notes,
oversized files and code that has grown hard to review. It works from the
terminal, in CI, and as an [MCP](https://modelcontextprotocol.io) server so
Claude can analyse your repositories for you.
```text
โญโ RepoDoctor v0.1.0 โโโโโโโโโโโโโโโโโโโโโโโฎ
โ Target demo-shop โ
โ Mode read-only ยท .gitignore respected โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโ made by daamles โโฏ
Findings
Level Rule Location Message
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ERROR RD005 hardcoded-secret app/main.py:3 Possible AWS access key ID: AK********LK
ERROR RD005 hardcoded-secret public/payment.js:1 Possible Stripe live key: sk_l********Xa9S
WARN RD004 long-file app/legacy.py File has 1200 lines (limit 1000); considerโฆ
WARN RD002 todo-comment includes/db.php:3 FIXME: query built by string concatenationโฆ
INFO RD002 todo-comment app/main.py:7 TODO: validate the cart before charging
โข Scanned 7 files (1.2 MB) in 0.02s ยท 1 dir pruned ยท 2 files ignored by .gitignore
โ 3 errors ยท 4 warnings ยท 3 info
```
Full examples: [terminal output](examples/example-output.txt) ยท
[Markdown report](examples/example-report.md) ยท
[JSON report](examples/example-report.json)
## Why RepoDoctor
- **Safe by design.** It never executes, imports or modifies the code it scans.
- **Finds real problems.** Committed credentials are the most damaging mistake
in public repositories; RepoDoctor catches them and **masks every value** in
its output, so its own reports are safe to share.
- **Low noise.** Respects `.gitignore`, skips binaries and minified bundles,
filters placeholders like `your_api_key_here`, and only reports work markers
that are actually in comments.
- **Fits your workflow.** Rich terminal report, JSON for tooling, Markdown for
pull requests, exit codes for CI, and MCP tools for AI assistants.
- **Small.** Two runtime dependencies (Typer, Rich); MCP support is optional.
## Safety guarantees
These are part of the contract and are covered by tests:
| Guarantee | How |
| --- | --- |
| Project code is never executed | Files are read as bytes and decoded as text; nothing is imported, evaluated or run. |
| Scanned files are never modified | Only `.gitignore`-style files and source text are read. The only file RepoDoctor ever writes is the report you ask for with `--output`. |
| Scans cannot escape the target | Symbolic links are not followed. |
| Secrets stay secret | Values are masked (`AK********LK`) in the terminal, JSON, Markdown and MCP output. |
| Huge or binary files are not read | Files over 2 MB and files containing NUL bytes are skipped. |
| MCP access is confined | The MCP server only scans directories inside the roots you allow. |
## Installation
Requires Python 3.11 or newer.
```bash
# From GitHub
pip install "git+https://github.com/daamles/repodoctor.git"
# With MCP server support
pip install "repodoctor[mcp] @ git+https://github.com/daamles/repodoctor.git"
```
From a local clone (recommended for development):
```bash
git clone https://github.com/daamles/repodoctor.git
cd repodoctor
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```
## Usage
```bash
repodoctor scan . # scan the current directory
repodoctor scan path/to/project --top 10 # list the 10 largest files
repodoctor scan . -o report.md # also write a Markdown report
repodoctor scan . -f json > report.json # JSON to stdout (pipe-friendly)
repodoctor rules # list all rules
```
### `scan` options
| Option | Description |
| --- | --- |
| `-f`, `--format` | `text` (default), `json` or `markdown`. JSON and Markdown print to stdout unless `--output` is set. |
| `-o`, `--output FILE` | Write a report file. With `--format text`, the format is taken from the extension (`.json`, `.md`). |
| `--fail-on LEVEL` | Exit with code 1 when a finding at or above `error`, `warning` (default) or `info` exists; `never` disables it. |
| `--no-gitignore` | Also scan files that `.gitignore` excludes. |
| `-t`, `--top N` | Number of largest files to list; `0` hides the section. Default `5`. |
| `-l`, `--limit N` | Maximum findings to print; `0` shows all. Default `50`. |
### Exit codes
| Code | Meaning |
| --- | --- |
| `0` | No findings at or above `--fail-on` |
| `1` | Findings at or above `--fail-on` |
| `2` | The scan could not run (bad path, invalid option, unwritable output) |
### Use it in GitHub Actions
```yaml
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install "git+https://github.com/daamles/repodoctor.git"
- run: repodoctor scan . --fail-on error --output repodoctor-report.md
```
## Rules
| ID | Name | Level | Reports |
| --- | --- | --- | --- |
| `RD001` | `large-file` | warning | Files larger than 1 MB (consider Git LFS) |
| `RD002` | `todo-comment` | info / warning | `TODO` and `XXX` (info), `FIXME` and `HACK` (warning) in comments |
| `RD003` | `long-line` | info | Source files with lines over 120 characters (one finding per file) |
| `RD004` | `long-file` | warning | Source files with more than 1000 lines |
| `RD005` | `hardcoded-secret` | error / warning | Credentials committed to source, always masked |
Line-based rules apply to Python, PHP and JavaScript and skip minified files
(`*.min.js`). In other text files such as Markdown, work markers are reported
anywhere on the line.
### Secret detection
Known formats are reported as **errors**:
- AWS access key IDs, GitHub tokens (classic and fine-grained), Stripe live
keys, Slack tokens, Google API keys, OpenAI and Anthropic API keys
- Private key blocks (`BEGIN โฆ PRIVATE KEY`)
- Passwords embedded in database URLs (PostgreSQL, MySQL, MongoDB, Redis, โฆ)
Less certain matches are reported as **warnings**: JSON Web Tokens, and
generic assignments such as `password = "โฆ"` or `'api_key' => 'โฆ'`. Generic
values must look random (Shannon entropy โฅ 3.0) and must not be placeholders
(`example`, `changeme`, `your_โฆ`, `<โฆ>`, `${โฆ}`, `********`, โฆ). Unquoted
`KEY=value` lines are only considered in `.env`, `.ini`, `.cfg`, `.conf` and
`.properties` files.
If RepoDoctor reports a real secret: **rotate it first**, then remove it from
the code and from Git history. Deleting the line is not enough once it has
been pushed.
### Suppressing findings
```python
EXAMPLE_KEY = "..." # repodoctor: ignore
```
- `repodoctor: ignore` anywhere on a line hides findings on that line.
- `repodoctor: ignore-file` in the first five lines skips the whole file.
Generated Markdown reports include this marker, so committing a report does
not make the next scan report it again.
## Use it from Claude (MCP)
RepoDoctor ships a read-only [Model Context Protocol](https://modelcontextprotocol.io)
server. Install the extra, then point your MCP client at `repodoctor mcp`:
```bash
pip install "repodoctor[mcp] @ git+https://github.com/daamles/repodoctor.git"
repodoctor mcp --root ~/projects # stdio server; --root is repeatable
```
**Claude Desktop** โ add this to `claude_desktop_config.json` (on Windows use
the full path to `repodoctor.exe` inside your virtual environment):
```json
{
"mcpServers": {
"repodoctor": {
"command": "repodoctor",
"args": ["mcp", "--root", "/path/to/your/projects"]
}
}
}
```
**Claude Code** โ register it from your project directory (see the
[Claude Code MCP documentation](https://docs.claude.com/en/docs/claude-code/mcp)):
```bash
claude mcp add repodoctor -- repodoctor mcp --root .
```
Then ask things like _"Scan this repository and explain the most serious
findings"_ or _"Are there any secrets under src/?"_.
| Tool | Purpose |
| --- | --- |
| `scan_repository(path, respect_gitignore)` | Fresh scan; returns a summary and the 20 most severe findings. |
| `get_report(path, format, respect_gitignore, refresh)` | Full report as `markdown` or `json` text. |
| `list_findings(path, min_severity, rule, file, limit, โฆ)` | Filter findings by severity, rule (`RD005` or `hardcoded-secret`) and path prefix. |
Every tool is annotated `readOnlyHint: true`. Paths are resolved against the
first `--root`, and anything outside the allowed roots is refused, so an AI
client cannot use RepoDoctor to read arbitrary directories.
## How it works
```text
cli.py โโโโโโโโโโโ โโโ ui.py (Rich terminal output)
mcp_server.py โโโโผโ> scanner.analyze() โโโผโโ reporters/ (JSON, Markdown)
service.py โโโโโโโ โ โโโ formatting.py
โโโ walker.py which files: .gitignore, pruning, no symlinks
โ โโโ ignore.py dependency-free gitignore matcher
โโโ checks/ what is wrong: one class per rule
โโโ base.py FileContext reads each file once via reader.py
โโโ size.py ยท todos.py ยท lines.py ยท secrets.py
```
- `walker.py` decides **which** files to look at; `checks/` decides **what is
wrong** with them. Both feed a plain `ScanResult` dataclass.
- `reader.py` is the only module that opens source files: size-capped, binary
files rejected, decoding never fails.
- The CLI and the MCP server are thin layers over the same `analyze()` call.
- Adding a rule means writing one `Check` subclass and registering it in
`checks/__init__.py`.
## Development
```bash
pip install -e ".[dev]"
pytest # 200+ tests
ruff check . && ruff format --check .
repodoctor scan . --fail-on warning # RepoDoctor checks itself in CI
python examples/make_demo.py # regenerate the example outputs
```
Test data never contains a complete fake credential: values in
`tests/fakes.py` are assembled at runtime, so neither RepoDoctor nor GitHub
push protection flags this repository.
## Roadmap
- Configuration through `[tool.repodoctor]` in `pyproject.toml`
- SARIF output for GitHub code scanning
- More languages and secret formats
- Git history scanning for secrets that were removed but never rotated
## License
[MIT](LICENSE) ยฉ 2026 **daamles**
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues