Skip to content

Instantly share code, notes, and snippets.

@tomjwxf
Created March 31, 2026 07:05
Show Gist options
  • Select an option

  • Save tomjwxf/98dd6766cfdc455911b67d3e4649d353 to your computer and use it in GitHub Desktop.

Select an option

Save tomjwxf/98dd6766cfdc455911b67d3e4649d353 to your computer and use it in GitHub Desktop.
MCP Security Test Fixtures — Receipt verification vectors for draft-serra-mcp-discovery-uri-05 Security Considerations

MCP Security Test Fixtures

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).

Fixtures

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

Verification

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.json

Or 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 }

Envelope Format (v2)

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:

  1. Strip the signature field from the artifact
  2. JCS-canonicalize the remaining object (sort keys lexicographically at every level)
  3. Verify the Ed25519 signature over the canonical UTF-8 bytes
  4. Check expires_at (if present) against current time

Keypair

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

Related

{
"v": 2,
"type": "decision_receipt",
"algorithm": "ed25519",
"kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
"issuer": "sb:mcp-gateway:test",
"issued_at": "2026-03-25T12:00:01.000Z",
"payload": {
"decision": "deny",
"policy_digest": "sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
"scope": "mcp-server:filesystem",
"tool": "tools/call:delete_everything",
"tier": "unknown",
"mode": "enforce",
"reason_code": "unregistered_tool",
"request_id": "req_mcp_fixture_002"
},
"signature": "ae099b4785c320ba4a211b446bbf1f1f2f97c6a68be99dad5cb2e2a1e559dc9cf8279b1da7c5baf2aacfe3ffbe7ed0503c5cdac199513892c5cbf1c92d679906"
}
{
"v": 2,
"type": "decision_receipt",
"algorithm": "ed25519",
"kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
"issuer": "sb:mcp-gateway:test",
"issued_at": "2025-01-01T00:00:00.000Z",
"expires_at": "2025-01-02T00:00:00.000Z",
"payload": {
"decision": "allow",
"policy_digest": "sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
"scope": "mcp-server:database",
"tool": "tools/call:query_db",
"tier": "signed-known",
"mode": "enforce",
"reason_code": "policy_match",
"request_id": "req_mcp_fixture_003"
},
"signature": "e9850aade8ff1c6efc0de2cca4f8fb8b9160e78d16c2459963ff268d04be258d05463ad29aeac8ebc84ea5418069947c93644f4af0cac68aad3302e14f33170b"
}
{
"v": 2,
"type": "decision_receipt",
"algorithm": "ed25519",
"kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
"issuer": "sb:mcp-gateway:test",
"issued_at": "2026-03-25T12:00:00.000Z",
"payload": {
"decision": "allow",
"policy_digest": "sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
"scope": "mcp-server:filesystem",
"tool": "tools/call:execute_command",
"tier": "signed-known",
"mode": "enforce",
"reason_code": "policy_match",
"request_id": "req_mcp_fixture_001"
},
"signature": "cac24462916fc5294bd0fa95a6ca6c8fe069310339c61e53b0e1f442a72f9f9cbe7f34f5e476429b39770fb6b06710094dae0ea757140fde41a987199b9f7301"
}
{
"v": 2,
"type": "decision_receipt",
"algorithm": "ed25519",
"kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
"issuer": "sb:mcp-gateway:test",
"issued_at": "2026-03-25T12:00:00.000Z",
"payload": {
"decision": "allow",
"policy_digest": "sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
"scope": "mcp-server:filesystem",
"tool": "tools/call:read_file",
"tier": "signed-known",
"mode": "enforce",
"reason_code": "policy_match",
"request_id": "req_mcp_fixture_001"
},
"signature": "cac24462916fc5294bd0fa95a6ca6c8fe069310339c61e53b0e1f442a72f9f9cbe7f34f5e476429b39770fb6b06710094dae0ea757140fde41a987199b9f7301"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment