obsify
obsify is an MCP server that lets an AI assistant work on sensitive local files using only shape, synthetic twins, and masked aggregates — never raw values.
scan_pii: Scan files/folders and get PII types, locations, and counts only — never the values.
redact_text: Replace detected PII in a string with deterministic
<TYPE>placeholders.verify_value_free: Fail-closed check that text contains none of given terms/variants, returning only a boolean.
make_synthetic_twin: Generate a synthetic Excel workbook with the same schema but all-fake values.
run_on_real: Execute your Python code locally against the real file, returning size-capped, PII-masked output — designed for aggregates.
Known-entity masking: Optionally hide names from a local
.obsify.entitiesfile, including suffix/abbreviation variants.Routing layer: Label paths in
.obsify.jsonand useobsify.guardto block direct reads and redirect to safe tools.Format support: PDF, Excel (
.xlsx/.xlsm), and Word (.docx), with unsupported files surfaced as notes.Precise detection: Checksum-validated ABN/ACN/TFN, credentials, private keys, connection strings, and more — with low false positives.
Privacy guarantees: No LLM calls and no network at runtime; detection is local and deterministic.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@obsify scan the employee contracts folder for PII"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
obsify
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_realexecutes model-written code in a best-effort local sandbox and masks its output best-effort. It is not a jail. ReadSECURITY.mdbefore 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.
Related MCP server: Conarium
Tools
Tool | What it does | Returns |
| Scan a file/folder for PII | Types, locations, counts — never values |
| Faithful fake of an Excel workbook | Schema summary; twin written to |
| Compute-to-data: run your code locally against the real file (bound to | Only PII-masked, size-capped stdout/stderr — return aggregates |
| Mask PII in a string to | The redacted string |
| Fail-closed check that |
|
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:
python -m obsify.make_corpus --out ./corpus_demo
npx @modelcontextprotocol/inspector obsify-mcpCall 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:
pip install "obsify[demo]" # reportlab, for the sample PDFs
python -m obsify.make_corpus --out ./corpus_demoIt 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:
{ "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):
pipx install obsify # isolated, on PATH (or: pip install obsify)Then point the client at the installed command:
{ "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
commandmust resolve on the PATH the client sees. A GUI client may not share your venv's PATH. Fixes: useuvx/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):
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:
.obsify.json— a label manifest classifying paths (public/confidential/restricted).obsify.guard(run aspython -m obsify.guard) — a PreToolUse guard that blocks a direct read of a labelled file (exit 2) and redirects the assistant toscan_pii/make_synthetic_twin/run_on_real.A convention (in
CLAUDE.md) so the assistant prefers obsify before it even hits the guard.
Set it up with one command:
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-mdappends 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
CREDENTIALby anchored patterns — vendor prefixes (AKIA…,ghp_…) or a keyword-gatedsecret = <value>, never entropy heuristics (which would flood on hex/base64 ledger columns). The wholeBEGIN…ENDprivate-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
pip install -e ".[dev]"
pytest tests/ # or run any file directly: python tests/test_obsify.pyThirteen 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_piireturning 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.
Related work
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:
Tool | Approach | Best for |
obsify | Detection + 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 |
Policy-driven pre-sanitization: tokenize a database into a de-identified copy the model queries freely | Known, structured schemas where you want rich analytics (joins/aggregations) on a referentially-intact clean copy | |
Reversible obfuscation proxy: the model works on consistent fakes; a proxy tool round-trips real API calls | Pentest workflows and secrets, where the model must operate on realistic data and you restore reals later | |
Enterprise redaction service: Presidio + spaCy behind REST/MCP, multi-language, scalable | A 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.
Available Tools
5 toolsmake_synthetic_twinA
Generate a SYNTHETIC TWIN of a real Excel workbook at path, written to
out. Schema (sheets, headers, column types, true row counts) is preserved;
every data value is freshly FAKED — no real value is copied. Reason and write
your analysis code against the twin; then run it on the real file with
run_on_real. Returns the schema summary (safe shape).
Inputs: out (required), path (required), cap_rows.
redact_textA
Return text with detected PII replaced by placeholders (e.g.
<AU_TFN>, ). Deterministic; checksum-validated identifiers and
context/precision rules apply so bare numbers are not over-masked.
entities is an optional PATH to a local .obsify.entities file of KNOWN names
to hide; matches (incl. variants) are masked as <KNOWN_ENTITY>. If omitted, a
nearby .obsify.entities is auto-used.
Inputs: text (required), entities. Returns: result
run_on_realA
COMPUTE-TO-DATA: execute your Python code LOCALLY against the real file at
data_path (bound to the variable DATA_PATH in your code); the returned output
is size-capped and best-effort PII-masked. The data never enters your context;
substance never leaves. Return AGGREGATES (counts/sums/summaries) via print() —
output masking is defense-in-depth, NOT a guarantee (NER can miss a name in a raw
record), so never print raw records or identifiers. The masking field carries this
caveat with the result. Network is disabled and a timeout applies.
Inputs: code (required), data_path (required), timeout.
scan_piiA
Scan a file or folder for PII and return TYPES + LOCATIONS + COUNTS only — never the detected values. Safe to surface to an LLM: it learns what PII exists and where, without the substance entering context. Recurses into subfolders; skips unreadable files and caps very large sheets, reporting both as notes.
entities is an optional PATH to a local .obsify.entities file (one name per
line) of KNOWN names to hide; matches (incl. suffix/abbreviation variants) are
reported as KNOWN_ENTITY. If omitted, a nearby .obsify.entities is auto-used.
The names are read locally and never returned.
Inputs: path (required), entities, max_cells.
verify_value_freeA
Fail-closed check that text contains NONE of terms (nor their
suffix-normalized / distinctive-token variants). Returns {"value_free": bool}
with zero detail on what matched — for verifying an artifact before it leaves
the perimeter.
Inputs: text (required), terms (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool has a clear, distinct purpose: make_synthetic_twin creates a fake dataset, run_on_real executes code against real data, scan_pii identifies PII locations, redact_text masks PII in text, and verify_value_free checks for forbidden terms. No two tools overlap in what they accomplish.
Most tools follow a verb_noun pattern (make_synthetic_twin, scan_pii, redact_text, verify_value_free), but run_on_real breaks the pattern with a prepositional phrase. The style is consistent (all snake_case, verbs first) but the deviation is noticeable.
With 5 tools, the count is well-scoped for a focused PII-handling server. Each tool covers a necessary step in the workflow, and the count is within the typical 3-15 range, though a few additional helpers could be justified (e.g., a check for twin accuracy).
The tool surface covers the core lifecycle: protect data (scan, redact, verify) and enable safe analysis (twin, run on real). Minor gaps exist, such as no tool to validate the synthetic twin's fidelity against the real file, and verify_value_free lacks a positive counterpart, but agents can work around these.
Maintenance
Related MCP Connectors
Governed access to sensitive data, secrets and APIs for AI agents. A signed proof for each decision.
Deterministic runtime safety for AI agents: scan PII, gate tool actions, verify LLM output.
300+ deterministic data utilities for AI agents: validate, normalize, parse, match, redact.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceLet LLMs analyze sensitive data safely by querying a tokenized, join-preserving copy of the database, with fail-closed PII scanning and provable numeric equivalence.MIT
- AlicenseAqualityBmaintenanceSelf-hosted governance layer between an AI assistant and your data: allow/deny policy, deterministic PII masking, row caps, and a hash-chained audit log with an Ed25519-signed receipt for every access, verifiable offline.4276 npm3MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to access only task-relevant data with secrets hidden and personal details pseudonymized, while enforcing destination policies to prevent data leakage even under prompt injection.2AGPL 3.0
- FlicenseNot gradedqualityNot gradedmaintenanceEnables agents to encrypt and sanitize sensitive data such as credentials, credit card numbers, and addresses locally before dispatching prompts to cloud LLMs, then rehydrate the masked values client-side. It runs deterministically with zero external dependencies over MCP JSON-RPC stdio, so no plaintext PII leaves the user's machine.-