SafeHarbor MCP
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
SafeHarbor MCP
A guarded Model Context Protocol server that lets AI agents analyze healthcare claims without ever touching PHI.
Every health plan wants analysts to ask Claude "what's our lumbar MRI denial rate by plan?" Almost none can hand an agent a connection to a claims warehouse full of names, SSNs and clinical notes. SafeHarbor is the layer in between: a zero-dependency MCP server that exposes a de-identified mart and enforces privacy inside the database engine, not with SQL regexes an agent can talk its way around.
Claude / any MCP client
│ JSON-RPC over stdio
▼
┌──────────────────────────── SafeHarbor ────────────────────────────┐
│ query(sql, purpose) ──► read-only conn ──► authorizer ──► engine │
│ query_only tables, k-supp. │
│ functions, aggregates│
│ statements VM budget │
│ hash-chained audit log ◄── every call │
└─────────────────────────────────┬──────────────────────────────────┘
▼
de-identified mart (mart.db)
▲ build_mart(): pseudonymize, generalize, drop free text
raw warehouse (PHI) — never opened by the serverResults
From reports/REDTEAM.md, generated by python -m safeharbor.redteam:
Attacks neutralized | 33 / 33 (PHI access, schema recon, sandbox escape, writes/DDL, function abuse, resource exhaustion, small-cell inference, CTE shadowing, prompt-injection follow-through) |
Legitimate analyst queries answered | 14 / 14 (0 false positives) |
PHI canary scan | 0 leaks: 9,676 real identifiers from the raw warehouse checked against every byte of every allowed response |
Median query latency | ~5 ms |
Known gaps | 3, run and documented in the report rather than hidden |
Related MCP server: Claims Data MCP Server
How it works
1. Physical separation, not just views. build_mart() materializes de-identification views (pseudonymous member_token, 10-year age bands, 3-digit ZIP, service month, no names/SSN/DOB/address/contact/notes) into a separate file. The server only ever opens that file.
Why not guard views over the raw tables? That was the first design. The red-team found the bypass: SQLite resolves a CTE before a view of the same name, so
WITH v_members AS (SELECT first_name, last_name, ssn FROM members) SELECT * FROM v_membersruns with the authorizer believing every read came from the approved view. View-level column policies can't distinguish the two. Case A32 keeps that attack in the suite as a regression test.
2. Engine-level authorization. SQLite's authorizer callback is consulted for every table read, column read, function call and statement type while the query is compiled. SafeHarbor allows reads of the three mart tables only (CTE references are recognized by their null database name; virtual tables like pragma_table_info and dbstat are not), an allow-list of functions, and SELECT. Everything else (PRAGMA, ATTACH, writes, DDL, transactions, load_extension, group_concat, window functions) is denied before a single row is read. The connection is also opened mode=ro with PRAGMA query_only.
3. Small-cell suppression inside the engine. count, sum, total, avg, min and max are replaced with implementations that return NULL when computed over fewer than k = 11 rows (the CMS cell-size threshold). Because it happens at the source, derived values are covered too: count(*) + 0, sum(1), HAVING count(*) < 11 probes and single-member averages all come back empty.
4. Resource limits. A VM-instruction budget and wall-clock deadline (via the progress handler) kill runaway recursion and cartesian joins; results are capped at 500 rows.
5. Purpose + tamper-evident audit. The query tool requires a stated purpose (HIPAA minimum-necessary). Every call (client name, purpose, SQL, normalized fingerprint, decision, reason, rows, suppressed cells, latency) is appended to a SHA-256 hash-chained JSONL log. Editing, deleting or reordering any line is detected by python -m safeharbor.audit logs/audit.jsonl.
6. Prompt-injection hygiene. One synthetic provider's practice_name contains an injected instruction to dump SSNs. Responses mark free-text columns as untrusted_columns, and the injected query is blocked regardless (A33): defense doesn't depend on the model behaving.
Agent-friendly errors. Denials explain themselves ("read of sqlite_master.type is not permitted; available tables: [...]", "query exceeded the compute budget; add filters or aggregate") so an agent can correct course instead of retrying blindly.
Quick start
pip install -e ".[dev]"
python -m safeharbor.data # builds data/raw_warehouse.db and data/mart.db (synthetic, seeded)
python -m safeharbor.redteam # runs the suite, writes reports/REDTEAM.md
pytest -qUse it from Claude Desktop or Claude Code
{
"mcpServers": {
"safeharbor": {
"command": "python",
"args": ["-m", "safeharbor.server", "--mart", "data/mart.db",
"--policy", "policy.toml", "--audit", "logs/audit.jsonl"],
"cwd": "/absolute/path/to/safeharbor-mcp"
}
}
}Claude Code: claude mcp add safeharbor -- python -m safeharbor.server --mart data/mart.db from the repo directory.
Then ask: "What's the denial rate by provider specialty, and how does out-of-network allowed cost compare to in-network?"
Policy
All rules live in policy.toml: allowed tables, allowed functions, k, row cap, compute budget, untrusted text columns. Anything not listed is denied.
Known gaps
Documented in the report and kept in the suite:
Differencing (G01). Two large, allowed counts that differ by one person can be subtracted. Per-query suppression can't stop this; it needs cross-query auditing or differential privacy.
Quasi-identifiers (G02). Row-level pseudonymous records are allowed for drill-down. A rare ZIP3 + sex + age-band combination can still single someone out.
Utility cost of fail-closed suppression (G03). Per-member aggregates inside a CTE are nulled before the outer query sees them. The mitigation is the FDE one: precompute common member-level features into the mart (
members.claim_count).
Layout
safeharbor/
data.py synthetic raw warehouse (PHI) + de-identification views + build_mart()
guard.py authorizer, k-suppressed aggregates, budgets, row cap
server.py MCP stdio server (JSON-RPC 2.0, protocol 2025-06-18 / 2025-03-26 / 2024-11-05)
audit.py hash-chained audit log + verifier
redteam.py attack / utility / gap suites + PHI canary scan
policy.toml the access policyAll data is synthetic and generated from a fixed seed. Python 3.11+, no runtime dependencies.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Governed SQL access to your databases: redaction, query review and an audit log.
Guardrailed FHIR access for AI agents: PHI redaction, audit trail, step-up auth, tenant isolation
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
- FlicenseNot gradedqualityDmaintenanceEnables natural language querying of healthcare claims data by exposing a SQLite database with read-only SQL tools, allowing users to ask questions in plain English and get answers backed by real database queries.1-
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to query governed data warehouses through plain language, returning grounded answers with SQL, confidence grades, and signed receipts while enforcing access, testing, and audit controls.279 PyPI17Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to securely query a synthetic health-insurance claims database through read-only, parameterized tools for claims, member histories, coverage rules, and year-to-date summaries. Includes API-key authentication, per-tool authorization, rate limiting, untrusted-text sanitization, and fail-closed audit logging.MIT