Secret Scanner MCP Server
by 0xmortuex
README.md
# Secret Scanner MCP Server: The Hard Way
Build an MCP server that finds secrets committed to a git repository, in the files and in history,
and tells an AI agent what to do about them, **without ever letting the secret reach the agent**.
Start from a regex and end with a server that is tested over real stdio for leaks.
At the end you will have built a minimal version of [secretscan-mcp](https://github.com/0xmortuex/secretscan-mcp)
yourself, and you will understand the parts most scanner tutorials skip: walking `git log -p` correctly,
keeping false positives low enough that people keep the tool on, and **redaction as a property of the types**
rather than a promise about the output.
## Why This Exists
A secret scanner is a security tool, and an MCP scanner is a security tool that talks to a language model.
That changes the design:
- Whatever the tool returns goes into a transcript, a log, and a model context. A scanner that prints the
secret it found has just leaked it again.
- An agent will paste things to you and to tools. An error message that echoes its input will echo a secret.
- "Deleted in the last commit" is not "gone". The interesting leaks are in history.
- A noisy scanner is a disabled scanner. Precision is a feature, not polish.
## Who This Is For
- Developers who have built a "hello world" MCP server and want a real one with a safety property to defend
- Anyone curious how gitleaks and trufflehog work underneath
- Anyone building MCP tools whose output must never contain sensitive data
**Prerequisites:** Python 3.10+ and `git` on your PATH. Regexes help; everything is explained where it appears.
```bash
pip install -r requirements.txt
```
## Modules
| # | Module | What You'll Build |
|---|--------|-------------------|
| 01 | [Regex Rules](01-regex-rules/) | A rule table for provider tokens and PEM keys, and why anchors and alphabets matter |
| 02 | [Entropy](02-entropy/) | Shannon entropy plus name context for secrets with no prefix, and where entropy alone fails |
| 03 | [Walking Git History](03-walking-git-history/) | Streaming `git log -p -U0`, a hunk-count parser, and finding a secret that was added then deleted |
| 04 | [False-Positive Control](04-false-positive-control/) | Filters for hashes, UUIDs, placeholders and code, and a fingerprint-based baseline file |
| 05 | [Redaction by Design](05-redaction-by-design/) | A `Finding` type with nowhere to put the secret, an output guard, and a leak hunt |
| 06 | [Remediation](06-remediation/) | An ordered, provider-specific checklist as text, and an `.env` hygiene check |
| 07 | [The MCP Wrapper and Testing](07-the-mcp-wrapper-and-testing/) | The finished server, errors that reach the agent, and a stdio test that fails if a secret leaks |
Each module contains:
- **`README.md`**: the concepts and design decisions
- **`main.py`**: a working, commented implementation of that stage (standalone, no imports between modules)
- **`break-it.md`**: challenges that break your own code, with hints
## Every Module Is Verified
```bash
python verify.py
```
For each module `verify.py` runs `main.py` and then checks the result independently. Module 03 is checked
against git's own pickaxe search (`git log -S`) as an oracle for which commits touched the secret. Module 07
is driven over real MCP stdio by a second client. CI runs it on Linux, macOS and Windows every push and weekly.
**No real secrets, ever.** Every fake credential is assembled at run time from fragments plus seeded random
characters, so nothing in this repository looks like a leak to a scanner or to GitHub push protection. The only
literal is AWS's documented dummy key `AKIAIOSFODNN7EXAMPLE`.
## Architecture Overview
```
AI agent (Claude Code, ...)
| MCP over stdio <- Module 07
v
+--------------------------------------------------------+
| tools: scan_tree, scan_history, explain_finding, ... |
| guard(): re-check every result and error for secrets | <- Module 05
| Finding: no field can hold the raw value | <- Module 05
| filters + baseline: keep the signal | <- Module 04
| detector: regex rules + entropy + context | <- Module 01, 02
| history: git log -p -U0, hunk-count parser | <- Module 03
| remediation: ordered checklist, text only | <- Module 06
+--------------------------------------------------------+
| argument lists, read-only subcommands
v
the real `git` binary -> your repository
```
## Key Design Principles
**1. The agent never sees a secret.** Not in results, not in errors, not in logs. Enforce it with types, then
test it from the outside.
**2. Rotation is the fix; rewriting history is housekeeping.** Anything that was pushed was copied. Tell the user
that, in that order.
**3. Be quiet unless sure.** Prefer a missed weak secret to a scanner people disable.
**4. Shell out to the real tool, with argument lists.** `git` knows its own ignore rules and diff format.
A shell string turns a file name into a command.
**5. Errors are instructions.** "path is not a directory: ..." tells the agent what to fix. A traceback does not.
## Tech Stack
- **Language:** Python 3.10+
- **Dependencies:** `mcp` only. Detection, entropy, and the git walk are standard library.
## License
MIT. Use it, modify it, ship it.
---
Built by [0xmortuex](https://github.com/0xmortuex)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues