pii-anonymizer
by kofi-sketch
README.md
# PII Anonymizer — MCP Server
An MCP server that lets AI assistants (Claude, ChatGPT, Cursor, etc.) automatically detect and redact PII before processing sensitive text.
**All processing is local. Zero network calls. No data leaves the machine.**
---
## Quick Start (2 minutes)
```bash
# 1. Clone and install
git clone https://github.com/kofi-sketch/pii-anonymizer-mcp.git
cd pii-anonymizer-mcp
npm install
# 2. Verify it works
npm test
```
You should see:
```
✓ Detected 6 PII items
✓ anonymize_text works
✓ deanonymize_text works
✓ All tests passed
```
**That's it. The server is ready.**
---
## Connect to Your AI Client
The server runs over **stdio** (standard MCP transport). Add it to whichever client you use:
### Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"pii-anonymizer": {
"command": "node",
"args": ["/absolute/path/to/pii-anonymizer-mcp/server.js"]
}
}
}
```
### Cursor
Edit `.cursor/mcp.json` in your project root (or global settings):
```json
{
"mcpServers": {
"pii-anonymizer": {
"command": "node",
"args": ["/absolute/path/to/pii-anonymizer-mcp/server.js"]
}
}
}
```
### VS Code (GitHub Copilot)
Add to your VS Code `settings.json`:
```json
{
"mcp": {
"servers": {
"pii-anonymizer": {
"command": "node",
"args": ["/absolute/path/to/pii-anonymizer-mcp/server.js"]
}
}
}
}
```
### Any Other MCP Client
stdio transport — pipe JSON-RPC 2.0 over stdin/stdout:
```bash
node server.js
```
> **Note:** Replace `/absolute/path/to/` with the actual path where you cloned the repo. Run `pwd` in the repo directory to get it.
---
## What You Get: 6 Tools
### `anonymize_text`
Pass any text. Get back sanitized text + an entity map.
```
Input: "Hi, I'm John Smith. SSN: 123-45-6789. Email: john@example.com"
Output: "Hi, I'm [PERSON_NAME_1] [PERSON_NAME_2]. SSN: [SSN]. Email: [EMAIL]"
```
The entity map lets you reverse it later:
```json
{
"[PERSON_NAME_1]": "John",
"[PERSON_NAME_2]": "Smith",
"[SSN]": "123-45-6789",
"[EMAIL]": "john@example.com"
}
```
**Parameters:**
- `text` (required) — the text to anonymize
- `detectors` (optional) — array of detector IDs to use (default: all). Get IDs from `list_detectors`.
### `deanonymize_text`
After the AI processes the sanitized text, restore the originals:
```
Input: "[PERSON_NAME_1]'s account has been updated" + entityMap
Output: "John's account has been updated"
```
**Parameters:**
- `anonymizedText` (required) — text with placeholders
- `entityMap` (required) — the map from `anonymize_text`
### `list_detectors`
Returns all 30+ detector patterns with IDs, categories, and descriptions. Use this to selectively enable/disable detectors.
### `add_custom_names`
Add your own names, usernames, codenames, or any terms that should be flagged as PII:
```json
{ "names": ["Satoshi", "Nakamoto", "kraken_admin_42", "ProjectPhoenix"] }
```
These persist for the session and get detected as `[PERSON_NAME]`.
### `add_custom_patterns`
Add custom regex patterns for organization-specific PII — employee IDs, internal codes, ticket numbers:
```json
{
"patterns": [
{ "regex": "EMP-\\d{6}", "label": "Employee ID", "placeholder": "EMPLOYEE_ID" },
{ "regex": "PROJ-[A-Z]{3}-\\d{4}", "label": "Project Code", "placeholder": "PROJECT_ID" },
{ "regex": "TICKET-\\d+", "label": "Support Ticket", "placeholder": "TICKET_ID" }
]
}
```
### `clear_custom_dictionaries`
Reset all custom names and patterns. Built-in detectors are unaffected.
---
## What It Detects
| Category | Examples |
|----------|---------|
| **Names** | 6,000+ first/last names across 40+ cultures. Context-aware — won't flag "Will" in "will do" but catches "Dear Will," |
| **Financial** | Credit cards (Luhn-validated), IBANs, routing numbers, account numbers, UK sort codes |
| **Identity** | US SSNs, UK NI numbers, passport numbers, driver's licenses, dates of birth |
| **Contact** | Email addresses, international phone numbers, street addresses, UK postcodes, US ZIP codes |
| **Crypto/Keys** | Ethereum/Bitcoin addresses, BIP-39 seed phrases, JWT tokens, API keys (Stripe, GitHub, GitLab, Slack) |
| **System** | UUIDs, session tokens, device IDs, user IDs |
Plus **context-aware classification** — catches standalone numbers near keywords like "account", "routing", "SSN" even without a fixed format.
---
## Typical Workflow
```
1. User pastes sensitive text into AI chat
2. AI calls anonymize_text → gets clean text + entity map
3. AI processes the sanitized text (summarize, classify, extract, translate, etc.)
4. AI calls deanonymize_text → restores originals in the output
5. User gets the result with real data intact
```
The user never has to manually scrub anything. The AI handles it automatically.
---
## CLI Tools
Two standalone command-line tools included — no MCP client needed.
### `pii-anonymize` — Scrub & Restore
```bash
# Pipe text
echo "John Smith, SSN 123-45-6789" | node cli.js
# → [PERSON_NAME_1] [PERSON_NAME_2], SSN [SSN]
# Scrub a file, save entity map for later
node cli.js --file ticket.txt -o clean.txt --map map.json
# Restore originals after AI processing
node cli.js --restore --file ai-response.txt --map map.json
# Full JSON output (anonymized text + entity map + stats)
node cli.js --file data.txt --json
# Use custom org dictionary
node cli.js --config kraken-pii.json --file logs.txt
# Only run specific detectors
node cli.js --detectors email,credit_card,ssn --file data.csv
# Stats only
node cli.js --stats --file data.txt
```
### `pii-scan` — Read-Only Scanner
Reports PII findings without modifying anything. Useful for audits and CI/CD gates.
```bash
# Scan a file
node scan.js --file ticket.txt
# ⚠ ticket.txt: 6 PII items found
# PERSON_NAME (2)
# → J***
# → S****
# SSN (1)
# → ***6789
# CREDIT_CARD (1)
# → ***0366
# Severity: HIGH
# Show context around each finding
node scan.js --file ticket.txt --verbose
# JSON report
node scan.js --file data.csv --json
# CI/CD gate — exit code 0=clean, 1=PII found
node scan.js --file output.txt -q || echo "BLOCKED: PII detected"
# Batch scan a directory
find ./logs -name "*.txt" -exec node scan.js --file {} -q \;
```
---
## Custom Dictionaries
Add your organization's own names and patterns via a config file.
Create `pii-config.json` next to `server.js` (auto-loaded on startup):
```json
{
"names": ["Jesse Powell", "internal_admin_42"],
"nameFiles": ["employees.txt", "contractors.csv"],
"patterns": [
{ "regex": "EMP-\\d{6}", "label": "Employee ID", "placeholder": "EMPLOYEE_ID" },
{ "regex": "TICKET-\\d+", "label": "Support Ticket", "placeholder": "TICKET_ID" }
]
}
```
- `names` — inline list of names/terms to flag
- `nameFiles` — paths to text files (one name per line) or CSVs
- `patterns` — custom regex with label and placeholder type
For the CLI, pass `--config /path/to/config.json`. For the MCP server, pass `--config=` as an arg or drop `pii-config.json` next to `server.js`.
See `pii-config.example.json` for a full example.
---
## Works With Everything
This tool uses MCP (Model Context Protocol) — an open standard. It works alongside any other MCP server or data tool your team already uses.
```
┌─────────────────────────────────────────────────────┐
│ AI Client (Claude, Cursor, etc.) │
│ │
│ "Get support tickets from Superset, │
│ summarize them, but scrub PII first" │
└──────┬──────────────────────────┬────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Superset │ │ PII Anonymizer │
│ MCP Server │ │ MCP Server │
│ (your data) │ │ (scrubs PII) │
└──────────────┘ └──────────────────┘
```
**Example flow:**
1. AI pulls query results from Superset (or DataHub, Postgres, Slack, any MCP server)
2. AI passes the results through `anonymize_text` → PII gets replaced
3. AI processes the clean data (summarize, classify, extract)
4. AI calls `deanonymize_text` → originals restored in final output
The CLI tools work the same way with any pipeline:
```bash
# Superset export → scrub → feed to AI
superset export --query 42 | pii-anonymize -o clean.csv --map map.json
# Scan any data source for PII without changing it
cat database_dump.csv | pii-scan
# CI/CD: block deploys that leak PII
pii-scan --file api-response.json -q || exit 1
```
**It doesn't need to know what your other tools are.** It just processes whatever text passes through it — queries, logs, tickets, chat transcripts, API responses, database exports. If it's text, it can be scrubbed.
---
## Architecture
```
┌─────────────┐ stdio (JSON-RPC) ┌──────────────────┐
│ MCP Client │ ◄──────────────────────► │ pii-anonymizer │
│ (Claude, │ │ server.js │
│ Cursor, │ anonymize_text() │ engine.js │
│ VS Code) │ deanonymize_text() │ (all local) │
└─────────────┘ list_detectors() └──────────────────┘
│
No network calls
No external APIs
No data storage
```
- **2 files** that matter: `server.js` (MCP wrapper) and `engine.js` (detection logic)
- **1 dependency**: `@modelcontextprotocol/sdk` (the MCP protocol library)
- **Runs in-process** — no Docker, no cloud, no accounts
---
## Docker
Run containerised — prints to STDOUT, no network required.
```bash
# Build
docker build -t pii-anonymizer .
# Pipe text through the container
echo "John Smith, SSN 123-45-6789" | docker run -i pii-anonymizer
# Scrub a file (mount it in)
docker run -i pii-anonymizer < ticket.txt > clean.txt
# JSON output
echo "John Smith, SSN 123-45-6789" | docker run -i pii-anonymizer cli.js --json
# Read-only scan
echo "John Smith, SSN 123-45-6789" | docker run -i pii-anonymizer scan.js
# MCP server mode (for AI client integration)
docker run -i pii-anonymizer server.js
# With custom config (mount your config file)
docker run -i -v /path/to/pii-config.json:/app/pii-config.json pii-anonymizer
# CI/CD gate — exit code 1 if PII found
echo "some text" | docker run -i pii-anonymizer scan.js -q || echo "BLOCKED"
```
**Image:** `node:18-alpine` (~50MB). Zero network calls at runtime. No volumes needed unless using custom dictionaries.
---
## Requirements
- Node.js 18+ (or Docker)
- That's it. No API keys, no accounts, no network.
## License
MIT
---
Built by @kofi.owusu on Slack
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues