Receipt verification test vectors for the Security Considerations section of draft-serra-mcp-discovery-uri-05 and draft-farley-acta-signed-receipts-00.
These fixtures demonstrate how signed decision receipts can provide verifiable proof of MCP tool-call policy enforcement, addressing threats MCP-001 (server impersonation) and MCP-002 (tool call injection).
| File | Expected Result | Scenario |
|---|---|---|
valid-receipt.json |
PASS | tools/call:read_file permitted by policy (tier: signed-known) |
tampered-receipt.json |
FAIL | Tool name changed from read_file to execute_command; original signature retained |
denied-tool-receipt.json |
PASS (signature valid, decision=deny) | tools/call:delete_everything denied — unregistered tool (MCP-002 scenario) |
expired-receipt.json |
FAIL (signature valid, but expires_at is in the past) |
Receipt issued 2025-01-01, expired 2025-01-02 |
Install the verifier CLI and check each receipt:
# Valid receipt — should PASS
npx @veritasacta/verify valid-receipt.json
# Tampered receipt — should FAIL (signature mismatch)
npx @veritasacta/verify tampered-receipt.json
# Denied tool receipt — should PASS (valid signature; decision is "deny")
npx @veritasacta/verify denied-tool-receipt.json
# Expired receipt — should FAIL (expires_at in the past)
npx @veritasacta/verify expired-receipt.jsonOr verify programmatically with the artifacts package:
import { verifyArtifact } from '@veritasacta/artifacts';
const PUBLIC_KEY = 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a';
const receipt = JSON.parse(fs.readFileSync('valid-receipt.json', 'utf8'));
const result = verifyArtifact(receipt, PUBLIC_KEY);
// { valid: true, hash: '...', version: 2 }Each receipt is a v2 signed artifact envelope:
{
"v": 2,
"type": "decision_receipt",
"algorithm": "ed25519",
"kid": "<JWK thumbprint (RFC 7638)>",
"issuer": "sb:mcp-gateway:test",
"issued_at": "2026-03-25T12:00:00.000Z",
"payload": {
"decision": "allow|deny",
"policy_digest": "sha256:...",
"scope": "mcp-server:<name>",
"tool": "tools/call:<tool_name>",
"tier": "unknown|signed-known|evidenced|privileged",
"mode": "enforce",
"reason_code": "policy_match|unregistered_tool",
"request_id": "req_..."
},
"signature": "<hex Ed25519 signature over JCS-canonicalized envelope (minus signature field)>"
}Verification steps:
- Strip the
signaturefield from the artifact - JCS-canonicalize the remaining object (sort keys lexicographically at every level)
- Verify the Ed25519 signature over the canonical UTF-8 bytes
- Check
expires_at(if present) against current time
These fixtures use the RFC 8032 Test Vector #1 keypair (do NOT use in production):
- Private key:
9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60 - Public key:
d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a - KID (JWK thumbprint):
kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k
@veritasacta/verify-- Offline receipt verification CLI@veritasacta/artifacts-- Signed artifact envelope library (Ed25519 + JCS)protect-mcp-- MCP gateway that issues these receipts- draft-serra-mcp-discovery-uri-05 -- MCP Discovery URI specification
- draft-farley-acta-signed-receipts-00 -- Signed receipts specification