debt-mcp
# debt-mcp
An MCP server that finds `TODO` / `FIXME` / `HACK` / `XXX` / `BUG` markers and other cheap tech-debt
signals in a repository and tells you **how old each one is and who added it**, using real `git blame`.
Read-only.
**What is different from other TODO scanners and TODO MCP servers** (for example tododebt, codescan-mcp, and CLI tools such as todo-age):
the scanner is comment-aware, not grep. A small tokenizer understands strings, comments and regex
literals in 27 language families, so `"TODO"`, `'# FIXME'`, `http://x/TODO`, `TODOS`, `todo_list` and
`Mastodon` are not reported. On top of age and author it adds three things most scanners lack: *stale in a
hot file vs stale in a dead file* (git log activity), *debt x churn hotspots* (git numstat), and
suppression counts (`# noqa`, `@ts-ignore`, `#[allow(...)]`, ...) by directory. Ages come from `blame -w -M`, so
re-indenting and moving a line within a file does not reset its age; `todo_history` additionally tells you when the marker
text first appeared, even if a later commit reworded the line.
## Install
Requires Python 3.10+ and `git` on PATH.
Claude Code:
```
claude mcp add debt -- uvx debt-mcp --dir /path/to/your/project
```
Claude Desktop (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"debt": {
"command": "uvx",
"args": ["debt-mcp", "--dir", "C:\Users\you\code\myproject"]
}
}
}
```
`--dir` is repeatable. It is the **sandbox**: the server reads nothing outside the directories you list
(default: the current directory). Tools take an optional `repo` argument, a directory inside the sandbox;
without it the first `--dir` is used. `repo` may be a subdirectory of a git repository.
## Tools
| Tool | What it answers |
|---|---|
| `debt_summary` | Counts by marker, directory, language; 5 oldest; top authors; markers added per quarter; ticketed vs orphan; median age |
| `list_todos` | Every marker, oldest first, with file:line, text, parsed tickets, added date, age in days, author, commit. Filters: `markers`, `path` glob, `author`, `older_than` (`90d`, `12w`, `6m`, `2y`), `has_ticket`, `limit` |
| `stale_todos` | Old markers split into hot (file still edited), cooling, dead (file untouched) |
| `todo_history` | For `path:line` or a text: last-touching commit, blame origin with copy detection, first appearance via `git log -S` (or `-G`) |
| `hotspot_debt` | Files ranked by debt (markers + suppressions) times churn over a window |
| `large_files_and_complexity` | Long files and functions, deep nesting, commented-out code, suppression counts by kind and directory |
Marker syntax understood: `TODO`, `TODO: text`, `TODO(alice): text`, `FIXME[JIRA-9]`, `HACK @carol`, `TODO(bob, #123)`,
tickets as `ABC-123`, `#123` or an `/issues/123` URL. A marker must be the **first word of a comment line**, so prose
such as "see the TODO list" is not a finding. Markers are case-sensitive; pass `markers=["TODO","NOTE"]` to customise.
Handled: `.gitignore` (via `git ls-files`; a plain walk with default ignores outside git), untracked files (age "uncommitted"), submodules and
symlinks (skipped), binary / minified / vendored / lock files (skipped), non-UTF-8 and UTF-16 files, BOMs, CRLF, 300 KB lines, mailmap,
shallow clones (warned, ages marked as lower bounds), repos with no commits.
Limits worth knowing: at most 10,000 files per call (the reply says when it stops early), files over 1 MB are skipped,
HTML `<script>` blocks are not parsed as JavaScript, function and nesting measures are line-counting heuristics.
## Try asking
- "How much tech debt does this repo have? Who owns most of it?"
- "List the TODOs older than a year that have no ticket."
- "Which stale TODOs are in files we still edit every week?"
- "When was the TODO at src/api/client.py:212 added, and was it moved from another file?"
- "Where do debt and churn overlap? Which files should we clean up first?"
- "Which directories rely most on `# type: ignore` / `eslint-disable`?"
- "Are there big blocks of commented-out code?"
## Security
- Read-only: only `git ls-files`, `blame`, `log`, `rev-parse` run (always as argument lists, never through a shell, with `GIT_OPTIONAL_LOCKS=0` so the index is never refreshed). Tests assert
that a repository is byte-identical (every file under `.git` too) after every tool.
- Path sandbox via `--dir`; `repo`, `todo_history` locations and symlinks are resolved and checked, `..` escapes are refused.
- Tool arguments are never passed to git as options: search text is bound to `-S<text>` as one argument.
- git itself reads the repo's own configuration (including diff drivers), so point this at repositories you would run `git blame` in.
- Output contains file paths, comment text and author names from the repository.
## Development
```
uv sync --extra test
uv run pytest
uv run ruff check src tests && uv run mypy --strict src
```
Set `DEBT_MCP_TEST_REPO` to a local git repository to also run the read-only real-repo test.
MIT licensed.
TDQS
Scored across 6 tools
Each tool occupies a fairly clear niche: list_todos (flat listing), stale_todos (liveness-split listing), debt_summary (aggregate overview), todo_history (single-marker forensics), hotspot_debt (debt×churn), and large_files_and_complexity (non-git heuristics). The main risk is list_todos vs stale_todos vs debt_summary, which all report marker counts and could be confused, though the detailed descriptions do enough to separate them.
All names are snake_case and descriptive, which keeps things readable. However, the conventions are mixed: only list_todos follows a verb_noun pattern, while the rest are noun phrases (debt_summary, todo_history, hotspot_debt) or adjective-led (stale_todos, large_files_and_complexity).
Six tools is well within the sweet spot, and each one covers a distinct analysis angle (listing, staleness, aggregation, history, churn correlation, non-git signals). No tool feels redundant or like filler.
Coverage of the debt-analysis domain is strong: detection, aging, ownership, trend, hotspots, and non-git complexity signals are all present, and the surface is read-only by design so no CRUD gaps matter. Minor gaps remain, such as diff/PR-scoped debt detection or exporting a report, but agents can work around these by combining tools.