Skip to main content
Glama
kpshinnik

docs-masked

by kpshinnik

docs-masked

Local document anonymization before sending to a language model — and reverse substitution after the response.

The document never leaves the machine in its original form. Personal data is replaced with persistent tags (#PERSON_1#, #PHONE_2#, #ADDRESS_1#), only the tagged text is sent to the model, and the received response is restored locally from the vault.

документ ──▶ маска ──▶ контроль утечки ──▶ модель ──▶ обратная подстановка
           локально      локально          сеть           локально

Works as a skill for Claude Code, as an MCP server for any other agent, and as a regular command-line utility.

How it works

1. Masking. The document is split into text fragments — paragraphs, cells, markup nodes. Personal data is found in each, each value receives a persistent tag. The same person gets the same tag throughout the document, including inflected forms and initials: "Ivanov Ivan Ivanovich", "Ivanovu", and "Ivanov I.I." are all one #PERSON_1#.

2. Leak check. The masked text is run through all detectors again plus a paranoid pass: any @, any sequence of seven or more digits, any phone-like string. If anything remains, sending is blocked with an exception, not a warning in the log.

3. Sending. Only the tagged text leaves the machine. The only network exit point is llm.send(), and it must trigger a check before the request. Every send is logged to ~/.pii_shield/egress.jsonl: time, provider, model, size, sha256, check status. Content is not written.

4. Reverse substitution. The model's response goes through the vault: tags are replaced with originals. For full names, the recovered nominative case is substituted — if the person in the document is only mentioned as "Kuznetsovu Ivanu Petrovichu", in the response they become "Kuznetsov Ivan Petrovich".

Related MCP server: Doc Sanitizer MCP Server

Installation

git clone https://github.com/kpshinnik/docs_masked.git ~/.docs_masked/src
cd ~/.docs_masked/src && ./install.sh

The script installs dependencies, places the skill in ~/.claude/skills/docs-masked, and prints a ready-to-use MCP configuration snippet. Details and options are in docs/INSTALL.md.

Connecting to an agent

Method

For whom

How

Skill

Claude Code, Claude.ai

./install.sh or /plugin marketplace add kpshinnik/docs_masked

MCP server

Cursor, Windsurf, Codex CLI, Continue, Zed, Cline, Claude Desktop

python3 mcp_server.py as stdio server

CLI and rule

everything else

terminal commands plus templates/AGENTS-rule.md in your project

Step-by-step instructions for each harness are in docs/HARNESSES.md.

The MCP server is written without dependencies: only python3 is needed. It provides six tools — mask_text, unmask_text, verify_text, scan_document, mask_document, unmask_document.

Usage

docs-masked scan   договор.docx                    # что будет скрыто
docs-masked mask   договор.docx                    # маска + сейф
docs-masked report договор.docx --open             # посмотреть глазами
docs-masked ask    договор.docx -p "Найди риски по срокам"
docs-masked unmask договор.masked.docx --vault договор.docx.vault.json

Commands

Command

Description

scan FILE

Shows what will be masked. File unchanged, network untouched.

mask FILE

Anonymized copy in the same format plus a vault file.

unmask FILE --vault V

Restores originals.

verify FILE

Checks that no personal data remains.

ask FILE -p "..."

Full cycle: mask → check → model → restored response.

report FILE

HTML review page: each replacement in context, values hidden.

selftest

Self-check of the cycle.

Full list of flags — skills/docs-masked/references/cli.md.

What is recognized

Full names in any case (Russian, Latin, transliteration), organizations, addresses, email, phones, passport and department code, SNILS, INN, OGRN, KPP, BIC, settlement accounts, bank cards, IBAN, compulsory medical insurance policies, driver's licenses, vehicle registration plates, IP addresses, @nicknames, dates of birth and document issue, requisition codes (OKTMO, OKPO, KBK), plus your own custom strings.

Identifiers are truly validated: SNILS checksum, INN and OGRN check digits, Luhn algorithm for cards, mod-97 for IBAN. Full table — references/coverage.md.

Formats

Format

Reading

Writing in place

.txt .md .rst .log .tex .yaml .ini

yes

yes

.docx

yes

yes, preserving formatting

.xlsx .xlsm

yes

yes

.csv .tsv

yes

yes

.json

yes

yes

.html .htm

yes

yes

.pdf

yes

with --pdf-redact flag, physically redacted

.rtf .doc .odt

yes

no (macOS only, via textutil)

DOCX is processed via XML, not through document.paragraphs: otherwise paragraphs inside content controls and captions are lost — in an actual contract, an entire column of requisition block was missing because of that. In tables, the column header is used as context: cell 500100732259 alone is indistinguishable from a random number, but in a column titled "INN" it is confidently recognized.

Python API

from pii_shield import ask_document

res = ask_document("договор.docx", "Составь резюме и найди риски",
                   provider="anthropic")
print(res.answer)          # имена уже восстановлены

Manual control of each step:

from pii_shield import mask_text, assert_clean, unmask_text

r = mask_text(raw)                 # r.text — с тегами, r.vault — сейф
assert_clean(r.text)               # LeakGuardError, если что-то осталось
answer = call_model(r.text)        # наружу уходит только маска
final, unknown = unmask_text(answer, r.vault, mode="canonical")

More details — references/api.md.

Vault

The vault is the only thing linking tags to originals. Without it, reverse substitution is impossible.

  • Written next to the document as <file>.vault.json, permissions 0600.

  • Encrypted with --pass-env flag (scrypt + Fernet).

  • Stores the canonical form, all encountered variants, and a log of occurrences in document order — thanks to the log, exact restoration returns the original word form, not the canonical one.

  • Added to .gitignore. Do not commit it.

Accuracy and limitations

The tool is designed to err on the safe side: better to mask extra than to miss something. What to keep in mind:

  • Scanned PDF without text layer is not processed — OCR is required.

  • Homonyms without initials receive separate tags, not merged into one person.

  • A bare number without context may not be recognized as an identifier — but the paranoid pass will still not let such text out.

  • Arbitrary Latin names without Slavic endings and without salutations (Mr., Dr.) are not recognized: catching any pair of capitalized words would do more harm than good.

For a critical document, it's worth visually inspecting docs-masked report once.

Development

python3 -m pytest tests/ -q          # тесты
python3 -m pii_shield.cli selftest
python3 samples/make_samples.py      # пересоздать тестовые документы

Invariants that must not be broken are listed in AGENTS.md. Everything in samples/ is synthetic; the examples/ directory is reserved for your local documents and is not included in the repository.

License

MIT.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that redacts PII/PHI from text before it ever reaches an LLM — self-hosted, fail-closed, and HIPAA-aware.
    3
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    MCP server providing on-prem PII detection and anonymization tools (scan and is_sensitive) for AI agents, ensuring data stays local.
    4
    MIT

View all related MCP servers

Related MCP Connectors

  • MCP server for AI dialogue using various LLM models via AceDataCloud

  • MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.

  • Hosted MCP server to humanize AI text: tell scans, voice fingerprints, burstiness, rewrite checks.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kpshinnik/docs_masked'

If you have feedback or need assistance with the MCP directory API, please join our Discord server