PII Shield
Anonymize documents before Claude sees them. Restore real data after analysis.
Built for Claude Desktop. No coding required.
Your data stays on your computer. Claude only sees placeholders.
Pipeline: local → API → local
The document is anonymized locally; only placeholder text reaches Claude. When the model is done, PII is stitched back in — without ever leaving your disk.
Document → Shield: anonymize_file({ file_path, session_id? }) → { session_id, entity_count, output_path, … }
Shield → Claude API: Claude reads output_path from the connected folder. Only placeholders cross the wire.
Claude API → Shield: model output flows back as text — still placeholder-only.
Shield → Result: deanonymize_docx({ file_path, session_id }) or deanonymize_text({ text, session_id }) stitches the real PII back in, locally.
The problem with "just use a model"
Pasting client material into a consumer LLM is both a privacy and a privilege problem.
A federal court has already confirmed the privilege side directly: in United States v. Heppner (SDNY, February 2026), documents a defendant created with consumer Claude were ruled outside attorney-client privilege. An AI tool is not an attorney and owes no duty of confidentiality.
PII Shield runs the redaction step before the model sees anything. It's an MCP server for Claude Desktop — drop a file, Claude receives placeholders like <PERSON_1> and <ORG_2>, and the real values are stitched back into the output on your machine. The original text never leaves your disk.
v1 was Python + Presidio + spaCy — every install fought Claude Desktop's bundled runtime. v2.0.2 is a complete Node.js rewrite over onnxruntime-node + @xenova/transformers: same detection coverage, no Python.
Plugin: 700 KB on Windows/Linux (host Node), or 83 MB on macOS (bundled Node 24.15.0). The 634 MB gliner-pii-base-v1.0 model installs on-demand via an in-chat panel — never bundled in the artefact. CI runs the matrix on Ubuntu/Windows/macOS × Node 18/20.
v1 is still public at gregmos/PII-Shield for the legacy crowd.
Six legal-document workflows out of the box
The companion pii-contract-analyze skill ships six task-shaped modes. Each runs the same anonymize → analyze → deanonymize pipeline, just optimized for a different deliverable.
National IDs, tax numbers, passports — and the obvious stuff too
17 country-specific patterns on top of generic entity detection. The fields a "find all emails" regex would never catch.
- UK5 NIN · NHS · Passport · CRN · Driving licence
- DE2 Tax ID · Social security
- FR2 NIR · CNI
- IT2 Fiscal code · VAT
- ES2 DNI · NIE
- CY2 TIC · National ID
- EU2 VAT · Passport
- person
- org
- location
- phone
- IBAN
- credit card
- crypto wallet
- US SSN
- US EIN
- medical licence
- money
- date
- GLiNER zero-shot
- person
- org
- location
- all 17 jurisdiction patterns
- email · phone
- IBAN · credit card · crypto
- SSN · EIN · licences
- money · date
Four steps. No terminal.
Download two files, drop them into Claude Desktop, connect a folder. The 634 MB GLiNER model installs itself in-chat on the first anonymization — no PowerShell, no bash, no scripts.
.mcpb for your OS plus the contract-analyze skill. Direct links to the v2.0.2 release ↗.Settings → Extensions → Advanced Settings → Install extension and select your .mcpb. The first time you use it, PII Shield will spend 2–3 minutes setting itself up. After that, it's instant.Settings → Extensions → Advanced Settings → Install extension and select your .mcpb. On first call, PII Shield runs npm ci --ignore-scripts to install pinned runtime deps (onnxruntime-node, @xenova/transformers, gliner) into ~/.pii_shield/deps/ — 2–3 minutes once per machine, instant thereafter.Customize → Skills → + → Upload a skill and select pii-contract-analyze.skill. The skill orchestrates the full anonymize → review → analyze → deanonymize flow without you spelling out each step.pii-contract-analyze skill, connect a folder with your documents, then tell Claude what you need.you > analyze risks for the purchaser in contract.pdf
and prepare a short memo
[Skill: pii-contract-analyze · Folder: ~/Documents/contracts]
claude > // calls anonymize_file → sees <PERSON_1>, <ORG_1>…
// HITL review — you confirm/edit detected entities
// runs MEMO mode, drafts the memo
// calls deanonymize_docx on the output
here's contract-risks.memo.docx
~/.pii_shield/models/). Subsequent runs skip the panel entirely.
npm ci --ignore-scripts --legacy-peer-deps
npm run build # esbuild bundle
npm run smoke # node scripts/smoke-protocol.mjs
npm test # 8 suites, incl. multi-doc HITL & session archival
CI runs the same matrix on Ubuntu / Windows / macOS × Node 18 / 20.
Three logs, all on-disk
Everything PII Shield does is recorded locally. No telemetry, no remote logging endpoint. If your DPO asks "what did the tool do with that file at 14:02 UTC," the answer is on your filesystem.
What's in the box
anonymize_file reads the document on your machine and returns only a file path + session id. Claude reads the anonymized file from disk — PII never enters an API request.gliner-pii-base-v1.0 over onnxruntime-node. Handles ALL-CAPS, domain-specific names, multilingual text. No Python, no PyTorch.<ORG_1>, "Acme Corp." → <ORG_1a>, "Acme Corporation" → <ORG_1b>. One canonical form; every variant maps back correctly on restore..docx carries its session_id in Word custom properties. Weeks later, in a brand-new chat, drop the file in — PII is restored from the embedded id.session_id; identical entities share placeholders across files. One deanonymize call restores PII everywhere.export_session(passphrase) packs the mapping + anonymized documents into an encrypted .pii-session archive (AES-GCM via scrypt). Colleague runs import_session. PII never transits.~/.pii_shield/audit/. NER bootstrap, session lifecycle, dropped stderr — all on disk, appendable, off-network.17 tools, four groups
Hover any tool name for a one-line description. Group by purpose; each tool is a single MCP function callable from Claude Desktop.
// 1. anonymize a file — original stays on disk; only placeholders cross the wire
→ anonymize_file({ file_path: "~/contracts/nda.docx" })
← { status: "success",
session_id: "m9q8x4-7b5a91",
entity_count: 14,
output_path: ".../nda.anon.txt",
docx_output_path: ".../nda.anon.docx",
by_type: { PERSON: 4, ORG: 3, MONEY: 2, ... } }
// 2. Claude reads output_path and drafts a memo using placeholders only
// 3. restore PII into the draft, locally
→ deanonymize_text({ text: draft, session_id: "m9q8x4-7b5a91" })
← { status: "success",
deanonymized_text: "...John Smith signed the NDA..." }
All inputs validated with Zod. Errors return MCP-protocol-shaped { isError: true, content: [...] } instead of throwing.