trackmcp
Back to directory

Local, privacy-preserving PII toolkit over MCP — the model reasons on shape, deterministic local code touches substance, only masked results return.

0 stars PythonOthers Updated Aug 20, 2026

Documentation

obsify

CI
PyPI
Python
License: MIT

Let an AI assistant work on sensitive files without their raw values ever entering the model's context.

obsify is a local, deterministic MCP server. The frontier

model reasons over shape — schemas, synthetic twins, masked feedback — while deterministic

local code touches the substance and returns only masked, aggregated results. No LLM calls,

no network at runtime: detection is regex + checksums + dictionaries + Presidio's

local NER.

It ships with Australian entity support (ABN / ACN / TFN, checksum-validated),

credential/secret detection (cloud keys, API tokens, private keys, DB connection

strings), and a label-driven routing layer that makes "when should the assistant avoid

raw data" a deterministic, enforced decision rather than a judgement call.

> Honest scope: `run_on_real` executes model-written code in a *best-effort* local sandbox

> and masks its output *best-effort*. It is not a jail. Read `SECURITY.md` before

> pointing it at anything you cannot afford to leak. Return aggregates.

Why

Feeding confidential documents to a hosted LLM means the substance leaves your perimeter. The

usual answers are "don't use the LLM" or "trust the provider." obsify takes a third path —

compute-to-data: bring the code to the data, not the data to the model.

  • The model sees the schema of a spreadsheet, not its rows.
  • The model develops against a synthetic twin (faked values, real structure).
  • The model's analysis code runs locally; only masked, aggregated output returns.

The frontier model's reasoning is preserved. Only its *eyes on raw values* are removed.

Tools

ToolWhat it doesReturns
`scan_pii(path)`Scan a file/folder for PIITypes, locations, counts — never values
`make_synthetic_twin(path, out)`Faithful fake of an Excel workbookSchema summary; twin written to `out` (values faked, leak-verified)
`run_on_real(code, data_path)`Compute-to-data: run your code locally against the real file (bound to `DATA_PATH`)Only PII-masked, size-capped stdout/stderr — return aggregates
`redact_text(text)`Mask PII in a string to `` tokensThe redacted string
`verify_value_free(text, terms)`Fail-closed check that `text` leaks none of `terms` (or their variants)`{"value_free": bool}`

Supported documents: PDF (text + tables; complex-table fallback via `obsify[tables]`),

Excel `.xlsx`/`.xlsm`, and Word `.docx` (paragraphs + tables). Unreadable or unsupported files

are surfaced as explicit notes/blind spots, never silently dropped. (No OCR yet — scanned/image

pages are flagged as low-coverage, not transcribed.)

Known-entity masking (optional). Supply a local `.obsify.entities` list of names to hide;

`scan_pii` / `redact_text` deterministically catch them — and the suffix/abbreviation variants

NER misses (`BRIGHTWATER HLDGS P/L` for *Brightwater Holdings Pty Ltd*) — as `KNOWN_ENTITY`. The

list stays local and never enters the model's context. See `docs/known_entities.md`.

Demo

Poke at all five tools live against synthetic data with the official

MCP Inspector:

bash
python -m obsify.make_corpus --out ./corpus_demo
npx @modelcontextprotocol/inspector obsify-mcp

Call `scan_pii` on `./corpus_demo/ledger.xlsx` and confirm it returns types / counts /

locations only — never values. See `docs/verifying.md`.

Try it — synthetic corpus

Generate a fake-but-realistic corpus (all synthetic; ABN/ACN/TFN are checksum-valid) spanning

all three formats, then point a tool at it:

bash
pip install "obsify[demo]"                 # reportlab, for the sample PDFs
python -m obsify.make_corpus --out ./corpus_demo

It writes a multi-sheet Excel ledger (a numeric false-positive minefield), a PDF engagement

letter (prose + trial-balance table), and a DOCX audit memo (paragraphs + vendor table). Great

for kicking the tyres on `scan_pii` / `make_synthetic_twin` without touching real data.

Install & run as an MCP server

Requires Python 3.11+. obsify speaks MCP over stdio — the client launches it as a local

subprocess; nothing is hosted remotely. Register it with any MCP-capable client (Claude

Desktop, Claude Code, Cursor, VS Code, …) by adding one block to that client's config.

**Recommended — zero-install via uvx:**

json
{ "mcpServers": { "obsify": { "command": "uvx", "args": ["--from", "obsify", "obsify-mcp"] } } }

`uvx` fetches obsify from PyPI and runs it on demand — no permanent install. On first run,

obsify downloads the spaCy NER model (`en_core_web_lg`, ~560 MB) once and caches it; this

fetches a public model and sends no user data (set `OBSIFY_AUTO_DOWNLOAD=0` to forbid it and

install the model yourself). Later runs are instant and fully offline.

Or install it (pip / pipx):

bash
pipx install obsify        # isolated, on PATH  (or: pip install obsify)

Then point the client at the installed command:

json
{ "mcpServers": { "obsify": { "command": "obsify-mcp" } } }

Restart the client and the tools appear. Optional extras: `obsify[tables]` (complex-table PDF

fallback via camelot + Ghostscript), `obsify[compute]` (pandas, handy inside `run_on_real` code).

> PATH gotcha (the #1 cause of "server won't connect"): the `command` must resolve on the

> PATH the *client* sees. A GUI client may not share your venv's PATH. Fixes: use `uvx`/`pipx`

> (globally resolvable), or give an absolute path — `"/path/to/.venv/bin/obsify-mcp"` (macOS/Linux)

> or `"C:\\path\\to\\.venv\\Scripts\\obsify-mcp.exe"` (Windows).

From this repo (before it's on PyPI):

bash
pip install "git+https://github.com/Formative-Sum41/obsify.git"   # gets `obsify-mcp` + `obsify`

The routing layer — deterministic, not a judgement call

The hard part of "help me, but don't read the confidential file" is *deciding when to protect*.

obsify moves that decision out of the model and into the environment:

1. `.obsify.json` — a label manifest classifying paths (`public` / `confidential` / `restricted`).

2. `obsify.guard` (run as `python -m obsify.guard`) — a PreToolUse guard that blocks a direct read of a labelled file

(exit 2) and redirects the assistant to `scan_pii` / `make_synthetic_twin` / `run_on_real`.

3. A convention (in `CLAUDE.md`) so the assistant *prefers* obsify before it even hits the guard.

Set it up with one command:

bash
obsify init [--dir PATH] [--with-claude-md]

`obsify init` is non-destructive by design — it owns exactly one file and hands you snippets

for the rest:

  • `.obsify.json` — obsify owns this; init writes it (never overwritten without `--force`).
  • `.claude/settings.json` — *your* file: init prints the PreToolUse hook block to paste,

never edits it (it runs code, so registering it is your call).

  • `CLAUDE.md` — *your* file: the convention is opt-in. Default prints it; `--with-claude-md`

appends a marker-wrapped, idempotent block that never clobbers your content.

Full convention: `docs/obsify_routing.md`.

How detection stays precise

  • Checksum-validated identifiers. ABN/ACN/TFN candidates are proposed by regex and confirmed

by their official checksums, so a random number is never reported as an identifier.

  • Context-required IDs. A bare number is only accepted as an ABN/ACN/TFN when a label word

("TFN", "ABN", "BSB", …) is nearby — this kills the sequential-journal-ID false-positive flood

on numeric ledgers.

  • Letterless / NER-with-digit suppression. Pure numbers, amounts, dates and alnum codes are

not flagged as names/orgs; real names, emails and addresses (which carry letters) are unaffected.

Validated letterless PII stays exempt: checksum IDs (ABN/ACN/TFN/Medicare), Luhn cards, valid

IPs, BSB-adjacent accounts, and phones (via context or phone shape) — while a decimal point

still marks an amount, not a phone.

  • Credentials, not just PII. Cloud keys (AWS/GitHub/Google/Slack/Stripe), JWTs, private-key

blocks and DB connection strings are flagged as `CREDENTIAL` by *anchored* patterns — vendor

prefixes (`AKIA…`, `ghp_…`) or a keyword-gated `secret = `, never entropy heuristics

(which would flood on hex/base64 ledger columns). The whole `BEGIN…END` private-key block is

masked, not just its header, so no key body is left behind.

Measured accuracy

obsify ships a scored evaluation harness (`eval/` — labelled synthetic corpus + answer key +

scorer against the *shipping* detector, plus an independent third-party cross-check). Headline

on the synthetic corpus: 100% recall on expected-detect items, 0 false positives on a

numeric FP-torture sheet (with a grouped-number guard), bare context-gated IDs correctly

suppressed. Independent cross-check vs Microsoft `presidio-research`: EMAIL/IBAN 100%, PERSON 94%.

The harness earned its keep — it found real defects, which were then fixed: credit cards and

phone numbers were being silently suppressed by the numeric-noise filter (now exempt via checksum

validation / phone shape), and Medicare, IP, date-of-birth, AU passport and driver-licence had no

recognizer (now added, checksum- or context-gated). Full method, numbers, and remaining documented

gaps (SWIFT/BIC, non-DOB dates): `eval/README.md`.

Tests

bash
pip install -e ".[dev]"
pytest tests/            # or run any file directly: python tests/test_obsify.py

Thirteen suites (88 tests), run in CI on Linux + Windows / Python 3.11 + 3.12:

  • mcp-protocol — launches the real server over stdio and speaks MCP to it (the same path a

client like Claude uses): confirms all five tools register with valid schemas and that calls

round-trip through JSON-RPC — including `scan_pii` returning shape only, end to end.

  • checksums — anchored to externally-published ABN/ACN/TFN worked examples (valid and

corrupted), which breaks the generator↔validator circularity.

  • obsify / twin / redaction — the privacy invariants: shape-only output, leak-free twins,

and a fail-closed self-check.

  • precision — the false-positive suppressors kill numeric-ledger noise while keeping real names.
  • credentials — the anchored secret patterns catch cloud keys / tokens / JWTs / private-key

blocks / connection strings, while keyword-anchored generics stay precise on prose (no entropy).

  • routing — the guard's block/allow classification and `obsify init`'s non-destructive contract.
  • corpus — the synthetic PDF+Excel+DOCX corpus end to end: per-format detection, DOCX

paragraph+table extraction, and shape-only output across every format.

  • evaluation — the scored harness as a regression gate (recall, suppression, FP-torture, gaps).
  • robustness — graceful degradation: corrupt/oversized/empty/nested/unsupported inputs never

crash and are always surfaced as notes.

  • model / variants — first-run model auto-download logic; variant normalization behind `verify_value_free`.

For interactive verification (MCP Inspector) and the live-client last-mile check, see

`docs/verifying.md`.

obsify is one of several MCP servers tackling "let an AI touch sensitive data safely" — they're

mostly complementary, solving the same problem from different ends. Worth knowing where each fits:

ToolApproachBest for
obsifyDetection + shape-isolation: the model sees only shape, synthetic twins and masked aggregates — never the values (real *or* faked)Messy, unstructured docs (PDF/Excel/DOCX) where you can't enumerate PII up front; strict value isolation; enforcement of *when* to protect
cloakboxPolicy-driven pre-sanitization: tokenize a database into a de-identified copy the model queries freelyKnown, structured schemas where you want rich analytics (joins/aggregations) on a referentially-intact clean copy
redact-mcpReversible obfuscation proxy: the model works on consistent fakes; a proxy tool round-trips real API callsPentest workflows and secrets, where the model must operate on realistic data and you restore reals later
cms-aiEnterprise redaction service: Presidio + spaCy behind REST/MCP, multi-language, scalableA hosted, multi-language redaction API with a UI and horizontal scale

Where obsify is distinct: it's the only one of these where the model gets *neither* raw values *nor*

a full mirror to operate on — just shape + masked aggregates — combined with checksum-validated

identifiers, credential detection, a deterministic routing guard, and a hard no-network / no-LLM

guarantee. That's the strictest-isolation end of the spectrum, tuned for confidential financial documents.

Honest trade: obsify optimizes *isolation of the values* over *utility on the data*. If you need

referentially-intact analytics on a clean copy (cloakbox), reversible round-tripping (redact-mcp), or a

multi-language hosted service (cms-ai), those are the better fit — and pair well with obsify rather than

competing with it.

Contributing

PRs welcome — see `CONTRIBUTING.md` for setup, the merge bar, and the

non-negotiable invariants (no LLM calls in the library, no runtime network, no real data,

shape-not-substance). Security issues: `SECURITY.md` (report privately).

License

MIT — see `LICENSE`.

Frequently asked questions

What is obsify?

obsify is Local, privacy-preserving PII toolkit over MCP — the model reasons on shape, deterministic local code touches substance, only masked results return.

How do I install obsify?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is obsify open source?

Yes — it is hosted on GitHub at https://github.com/Formative-Sum41/obsify.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP