Drop-in PII detection and redaction. One small Python package, three ways to plug in:
- Library —
pip installand call it in-process. Only dependency: pydantic. - REST API —
privacy-filterstarts a local FastAPI server. Any language can call it. - MCP server —
privacy-filter-mcpexposes it as Model Context Protocol tools for Claude and other LLM agents. - Docker — optional. A Dockerfile is included, but nothing requires it.
Detection layers: 11 deterministic regex rules (emails, UK phones, postcodes, NI numbers,
bank details, secrets, …), heuristic name/address/date patterns, and — optionally — the
openai/privacy-filter token-classification model for ML-grade person/address detection.
Results always say which engine ran (rules-only or model+rules).
New here? Read the plain-English Overview, or jump to the Technical Reference for the deep dive.
⭐ If you find this useful, give the repo a star — it helps other people find it.
That's the document redactor example app — a drag-and-drop web page that runs entirely on your machine:
pip install .
python examples\document-redactor\app.pypip install . # from the repo root (or the path where you cloned it)from privacy_filter import redact
result = redact("Email jane.smith@example.org or call 07700 900123.")
print(result.text) # Email [EMAIL REDACTED] or call [PHONE REDACTED].
print(result.findings) # spans with type, offsets, confidenceSee examples/quickstart.py for policies and the reusable Redactor engine.
pip install ".[server]" # from the repo root
privacy-filter
# -> serving on http://127.0.0.1:8081 (POST /v1/redact, POST /v1/detect, GET /health)See examples/rest-examples.md and .env.example for configuration.
pip install ".[model]" # from the repo root
# download weights once (or copy them in for air-gapped machines):
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='openai/privacy-filter', local_dir='models/privacy-filter', ignore_patterns=['onnx/*','original/*'])"
$env:PRIVACY_FILTER_MODEL_PATH = "C:\path\to\models\privacy-filter"
privacy-filter # /health now reports "model+rules"Library equivalent: Redactor(model_path=r"C:\path\to\models\privacy-filter").
By default the package never downloads anything: local_files_only=true unless you
opt out, the server binds to 127.0.0.1, input text is never logged, and the server
rejects request bodies over 64 KiB (PRIVACY_FILTER_MAX_TEXT_BYTES, default 65536)
as a denial-of-service guard — raise it if you need to process larger documents.
Expose the module to Claude (and any MCP client) as local tools — it imports the library in-process, so there's no network hop and nothing extra to run:
pip install ".[mcp]" # from the repo root
privacy-filter-mcp # stdio MCP serverWire it into Claude Code / Claude Desktop by adding this to your .mcp.json
(see examples/claude-mcp-config.json):
{
"mcpServers": {
"privacy-filter": { "command": "privacy-filter-mcp" }
}
}Tools exposed: redact_text(text, policy?, threshold?), detect_pii(text, policy?, threshold?),
and list_policies(). The same PRIVACY_FILTER_* env vars apply (model path,
default policy, size cap).
A ready-made Claude skill lives in skills/pii-redaction/ —
it teaches Claude when and how to redact PII with these tools. Copy or symlink it
into your ~/.claude/skills/ (or a plugin's skills/) to activate it.
A policy says which categories get redacted (a category is active iff it has a label),
what replaces them, and the review threshold. Presets: default,
witness_statement_external_disclosure, training_data_sanitisation,
transcript_standard, external_sharing.
from privacy_filter import Policy, Redactor
emails_only = Policy(name="emails_only", labels={"private_email": "<email removed>"})
Redactor(policy=emails_only).redact("Write to a@b.co")Load a policy from a JSON file in library code:
from pathlib import Path
policy = Policy.model_validate_json(Path("my_policy.json").read_text())Servers can register extra policies at startup: PRIVACY_FILTER_POLICY_FILE=policies.json
(a JSON list of Policy objects).
Low-confidence findings from the rule and heuristic detectors are redacted and
flagged (review_required=true, reason="low_confidence"), never silently
dropped. The optional ML detector is the one exception: it filters spans scoring
below the threshold at the source (they are not returned or flagged), so lower
the threshold if you want borderline ML detections surfaced for review.
docker build -t privacy-filter .
docker run --rm -p 8081:8081 privacy-filter
# with the ML runtime baked in and local weights mounted:
docker build --build-arg INSTALL_MODEL_RUNTIME=true -t privacy-filter:model .
docker run --rm -p 8081:8081 -v C:\path\to\models\privacy-filter:/models/privacy-filter `
-e PRIVACY_FILTER_MODEL_PATH=/models/privacy-filter privacy-filter:modelpython -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[dev,server,mcp]"
.\.venv\Scripts\python.exe -m pytest- docs/overview.md — plain-English guide for non-technical readers (what it does, how to use it, how good it is, privacy).
- docs/technical-reference.md — full technical reference (architecture, pipeline, data models, Python/REST/MCP APIs, config, security, extending).
- docs/README.md — docs index.
