Wraps an Express-based MCP tool server with protect-mcp policies and signed receipts. Demonstrates JSON policy format, rate limiting, and per-tool allow/deny rules.
cd express-api-gateway
npm installnpx protect-mcp --policy policy.json --enforce -- node server.jsThis starts server.js as a child process with protect-mcp sitting in front.
Every MCP tools/call request is evaluated against policy.json before
reaching the server.
In another terminal:
./test.shThis sends three tool calls over stdio and shows what happens:
get_weather-- allowed (safe, no restrictions)send_email-- allowed (rate-limited to 5 per minute)delete_account-- denied (blocked by policy)
npx protect-mcp receiptsnpx protect-mcp bundleThis creates a portable audit archive containing all receipts, the policy snapshot, and a manifest signature.
$ ./test.sh
--- get_weather ---
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Weather in London: 14C, partly cloudy"}]}}
--- send_email ---
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"Email sent to alice@example.com: subject='Meeting tomorrow'"}]}}
--- delete_account ---
{"jsonrpc":"2.0","id":3,"error":{"code":-32603,"message":"DENIED by policy: Destructive operations require human approval"}}
$ npx protect-mcp receipts --last 3
[2026-04-04T10:20:01Z] ALLOW get_weather sha256:f1e2d3... sig:OK
[2026-04-04T10:20:02Z] ALLOW send_email sha256:f1e2d3... sig:OK
[2026-04-04T10:20:03Z] DENY delete_account sha256:f1e2d3... sig:OK
All three calls produce signed receipts -- including the denied one. The receipt proves that the policy evaluation happened and what the decision was.
express-api-gateway/
package.json -- Dependencies
server.js -- MCP server with 3 tools (stdio JSON-RPC)
policy.json -- protect-mcp JSON policy
test.sh -- Sends test requests and shows results
The JSON policy format is the simplest way to configure protect-mcp. Each tool can be set to allow or deny, with optional conditions:
{
"default": "allow",
"rules": [
{ "tool": "delete_account", "decision": "deny", "reason": "..." },
{ "tool": "send_email", "decision": "allow", "conditions": { "max_per_minute": 5 } }
]
}For more complex policies (path-based rules, role checks, contextual conditions), use Cedar policies instead. See the mcp-server-signing example.
Each receipt contains:
- Decision: allow, deny, or shadow
- Policy digest: SHA-256 hash of the policy at evaluation time
- Timestamp: ISO 8601 decision time
- Tool name and input hash: what was called
- Ed25519 signature: covers all fields above
Verify offline with:
npx @veritasacta/verify .protect-mcp-receipts.jsonlThe verifier checks Ed25519 signatures without contacting any server. It is issuer-blind -- it validates cryptographic integrity without knowing or trusting the original signer.