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.
Architecture
Claude never talks to your internal API directly. Every tool result is intercepted, scrubbed, audited โ and blocked if it carries a hard secret.
Why Priva-MCP
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.
First-class handling for cifNumber, accountNumber, pan, phoneNumber and more โ case-insensitive, collision-safe.
Balances and amounts keep their JSON number type so the AI can still compute. Under strict mode they are zeroed, never corrupted to "X".
A leaked password, pin, cvv or secret blocks the entire response. Claude gets a safe error; the secret value is never logged.
Every call logged to audit.log (JSON Lines). Severe breaches emit an ECS-style incident to stderr for ELK / Logstash ingestion.
Strict TypeScript, zero any, recursive traversal of untrusted JSON, and 28 unit tests via the built-in node:test runner.
Field-level redaction
What the internal API returns vs. what Claude actually receives (strict mode).
Zero-trust
If the upstream accidentally includes a pin, the whole payload is dropped.
{
"content": [{
"type": "text",
"text": "Error: response blocked by
privacy & compliance policy."
}],
"isError": true
}
{
"@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
Raw data stays on your machine. Only the masked result crosses the boundary to the model.
The masked tool output (e.g. REDACTED-CIF, XXXX-โฆ-1111) plus the prompts you type โ the model needs them to answer.
Raw PII (exists only in local RAM), blocked forbidden fields, and the audit / incident logs โ all stay on your machine.
A partial mask (last 4 digits) still goes out, and your own chat text is not scrubbed โ only tool output is.
Implementation
git clone https://github.com/adhidevara/priva-mcp.git cd priva-mcp npm install
npm run build npm test # 28 passing npm start # serve over stdio
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"
}
}
}
}
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