Zero-trust privacy layer for the Model Context Protocol

Stop sensitive data
before it reaches the LLM

Priva-MCP is a man-in-the-middle MCP proxy that masks sensitive PII โ€” CIF, account numbers, debit-card PANs, balances, emails, phones โ€” at the field level, and blocks any response carrying a forbidden secret.

TypeScript ยท strict @modelcontextprotocol/sdk Node 18+ 28 tests passing ELK / Logstash ready

Architecture

A proxy that sits in the middle

Claude never talks to your internal API directly. Every tool result is intercepted, scrubbed, audited โ€” and blocked if it carries a hard secret.

๐Ÿค–

Claude

MCP client
โ‡„
๐Ÿ›ก๏ธ

Priva-MCP

  • dual-layer redaction
  • zero-trust block
  • audit trail
  • SIEM incidents
โ‡„
๐Ÿฆ

Internal API

REST / DB

Why Priva-MCP

Built for compliance, not convenience

๐ŸŽฏ

Dual-layer redaction

Layer 1 masks by field key name (precise); Layer 2 is a global regex fallback for free text. No more 16-digit NIK mistaken for a credit card.

๐Ÿฆ

Field-level rules

First-class handling for cifNumber, accountNumber, pan, phoneNumber and more โ€” case-insensitive, collision-safe.

๐Ÿ”ข

Numeric safety

Balances and amounts keep their JSON number type so the AI can still compute. Under strict mode they are zeroed, never corrupted to "X".

๐Ÿšซ

Zero-trust block

A leaked password, pin, cvv or secret blocks the entire response. Claude gets a safe error; the secret value is never logged.

๐Ÿ“œ

Audit + SIEM

Every call logged to audit.log (JSON Lines). Severe breaches emit an ECS-style incident to stderr for ELK / Logstash ingestion.

๐Ÿงช

Type-safe & tested

Strict TypeScript, zero any, recursive traversal of untrusted JSON, and 28 unit tests via the built-in node:test runner.

Field-level redaction

Raw in โ†’ scrubbed out

What the internal API returns vs. what Claude actually receives (strict mode).

Field
Raw (from API)
Returned to Claude
cifNumber
CIF-7782001
REDACTED-CIF
accountNumber
0012345678901
XXXXXXXXX8901
pan
4111111111111111
XXXX-XXXX-XXXX-1111
phoneNumber
+6281234567890
+XXXXXXXXXX890
email
andi.wijaya@example.com
a***a@e***.com
balance
15750000
0  (number kept)
customerName
Andi Wijaya
Andi Wijaya  (not PII)

Zero-trust

When a hard secret leaks, nothing gets through

If the upstream accidentally includes a pin, the whole payload is dropped.

โ†’ stdout (to Claude)
{
  "content": [{
    "type": "text",
    "text": "Error: response blocked by
      privacy & compliance policy."
  }],
  "isError": true
}
โ†’ stderr (to ELK / Logstash)
{
  "@timestamp": "...",
  "log.level": "error",
  "event.action":
    "compliance.critical_violation",
  "event.outcome": "blocked",
  "violations": [
    { "path": "$.pin", "key": "pin" }
  ]
}

The incident logs the key, path and reason โ€” never the secret value itself.

Privacy & data

What reaches Anthropic?

Raw data stays on your machine. Only the masked result crosses the boundary to the model.

๐Ÿ–ฅ๏ธ Your machine โ€” local โ˜๏ธ Anthropic โ€” cloud trust boundary ๐Ÿ—„๏ธ Internal API DB / REST ๐Ÿ›ก๏ธ Priva-MCP mask + block ๐Ÿ’ป Claude Desktop MCP client ๐Ÿค– Claude model runs at Anthropic raw PII masked masked only audit.log + ELK incident stays on your machine ๐Ÿ”’
โœ…

Sent to Anthropic

The masked tool output (e.g. REDACTED-CIF, XXXX-โ€ฆ-1111) plus the prompts you type โ€” the model needs them to answer.

๐Ÿ”’

Never sent

Raw PII (exists only in local RAM), blocked forbidden fields, and the audit / incident logs โ€” all stay on your machine.

โš ๏ธ

Keep in mind

A partial mask (last 4 digits) still goes out, and your own chat text is not scrubbed โ€” only tool output is.

Implementation

Run it in under a minute

1 Clone & install

git clone https://github.com/adhidevara/priva-mcp.git
cd priva-mcp
npm install

2 Build & test

npm run build
npm test    # 28 passing
npm start   # serve over stdio

Connect to Claude Desktop

Add this to claude_desktop_config.json (use an absolute path to dist/server.js), then restart Claude Desktop.

{
  "mcpServers": {
    "priva-mcp": {
      "command": "node",
      "args": ["/abs/path/to/priva-mcp/dist/server.js"],
      "env": {
        "COMPLIANCE_STRICT": "true",
        "AUDIT_LOG_PATH": "/abs/path/to/priva-mcp/audit.log"
      }
    }
  }
}

Call a tool

Two tools ship out of the box. Mock CIFs: CIF-7782001, CIF-7782002.

// in Claude:
get_customer_profile  { cifNumber: "CIF-7782001" }
get_financial_report  { cifNumber: "CIF-7782001" }
// โ†’ returns a fully masked, audited payload