Somewhere between Kyoto temples I shipped a signing proxy for Agent Receipts — an open protocol that gives every AI agent action a cryptographically signed audit trail. The idea is simple: when an agent acts on your behalf, you should be able to prove what happened. Not just logs. Proof.

This week I used it to audit itself.
When you give Claude access to GitHub via the MCP server, it can create issues, push
files, open pull requests. It acts on your behalf. And when it’s done, it tells you what
it did.
But that’s a claim. Not evidence.
Imagine an agent silently force-pushes to main, or opens a PR to a repo you didn’t
authorise. The logs say it happened — but those logs are self-reported. There’s no
independent witness to what payload was sent, which API was called, or what the server
actually responded. If you’re operating agents in any context where accountability
matters — a shared codebase, a production system, a compliance environment — “the
agent said so” isn’t enough.
Cryptographic receipts are the mechanism that closes this gap. A certifying proxy sits
between the MCP client and the server. Every tool call passes through it. Every call
gets a signed receipt. The receipt is hash-chained to the previous one, so the audit
trail is tamper-evident: you can’t remove a receipt, reorder them, or insert a fake one
without breaking the chain.
Routing the GitHub MCP server through the proxy
The Agent Receipts proxy is a transparent stdin/stdout wrapper. It doesn’t modify the
GitHub MCP server or Claude Desktop. It just intercepts the JSON-RPC messages, signs a
receipt for each tool call, and forwards everything through.
The setup is a few commands and a config change.
Install the proxy (tested with v0.3.3):
go install github.com/agent-receipts/ar/mcp-proxy/cmd/mcp-proxy@v0.3.3
Generate a persistent signing key:
mkdir -p ~/.agent-receipts
openssl genpkey -algorithm Ed25519 -out ~/.agent-receipts/github-proxy.pem
chmod 600 ~/.agent-receipts/github-proxy.pem
openssl pkey -in ~/.agent-receipts/github-proxy.pem -pubout \
-out ~/.agent-receipts/github-proxy-pub.pem
Update claude_desktop_config.json:
⚠️ This file contains your GitHub PAT — don’t commit it to version control.
Use a least-privilege token scoped to only the repos you need.
{
"mcpServers": {
"github-audited": {
"command": "/Users/YOU/go/bin/mcp-proxy",
"args": [
"-name", "github",
"-key", "/Users/YOU/.agent-receipts/github-proxy.pem",
"-receipt-db", "/Users/YOU/.agent-receipts/receipts.db",
"/opt/homebrew/bin/mcp-server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_TOKEN"
}
}
}
}
Restart Claude Desktop. The GitHub MCP server now runs behind the proxy — every tool
call goes through it transparently.
What a receipt looks like
After asking Claude to list open issues on the repo, I checked the receipt store:
mcp-proxy list -receipt-db ~/.agent-receipts/receipts.db
ID ACTION RISK STATUS TIMESTAMP
---
urn:receipt:fc72deca-e3b7-49c8-a2c5-8… read low success 2026-04-12T06:32:02Z
urn:receipt:4f123102-8c0a-4c46-abf8-0… read low success 2026-04-12T06:32:07Z
2 receipts
Action: read, Risk: low — the proxy recognised list_issues as a read operation
and scored it accordingly.
Each row is a W3C Verifiable Credential stored on disk. Here’s the core of one — the
credentialSubject that records what happened, and the proof that signs it:
{
"credentialSubject": {
"action": {
"type": "read",
"tool_name": "list_issues",
"risk_level": "low",
"target": { "system": "github" },
"parameters_hash": "sha256:3846c4a1f3b2e9d7c0581a4f6e2b8d3c9a7f1e4bc2d5a8f9e6b3c1d7a4e2f8b6",
"timestamp": "2026-04-12T06:32:02Z"
},
"chain": {
"sequence": 1,
"previous_receipt_hash": null
}
},
"proof": {
"type": "Ed25519Signature2020",
"verificationMethod": "did:agent:mcp-proxy#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "uX3bbrW7YoSWlqZzBvAr5iUjDOA05Na0aPb1efc72de..."
}
}
(The full receipt also includes W3C @context, issuer and principal DIDs — currently
placeholders while the DID method strategy
is finalised — and standard VC envelope fields.)
The parameters_hash is a SHA-256 of the RFC 8785 canonical JSON of the tool call
arguments — the actual payload sent to the GitHub API, not a summary. The proof is
an Ed25519 signature over the canonical receipt. The chain links each receipt to the
previous one by hash, making the sequence tamper-evident.
Then I verified the chain:
mcp-proxy verify -key ~/.agent-receipts/github-proxy-pub.pem \
-receipt-db ~/.agent-receipts/receipts.db \
fc72deca-e3b7-49c8-a2c5-8b3f1a2c9d4e
Chain fc72deca-e3b7-49c8-a2c5-8b3f1a2c9d4e: VALID (2 receipts)
Every tool call adds another receipt. The chain grows with the session — tamper-evident
from the first call to the last.
Here’s the part I enjoyed: once writes were working, I used the audited connection to
file bug reports discovered during the session itself.
The proxy wasn’t storing the tool name in receipts at all — every call showed
Action: unknown. The classifier code was correct, but the tool name was never wired
through from interception to storage. And the CLI reference docs were showing
double-dash flags (--db) when the binary uses single-dash Go flags (-db).
So I filed both bugs via the proxy:
Those GitHub API calls — create_issue, update_issue — have signed receipts from
the GitHub MCP server running through the proxy. The bug reports about Agent Receipts
were filed using Agent Receipts, via the GitHub MCP server, and the act of filing them
is itself receipted.
That’s the dogfooding loop closing.
What the chain gives you
Each receipt is an Ed25519-signed W3C Verifiable Credential. The chain property means:
- Tamper-evidence: remove or reorder a receipt and the chain verification fails
- Gap detection: a missing sequence number is immediately visible
- Independent verification: anyone with the public key can verify the chain — no
trust in the proxy required
- Cross-session continuity: because the key is persistent, receipts from different
sessions can be verified against the same public key
The proxy also handles sensitive data automatically — GitHub PATs, API keys, and other
secrets matching known patterns are redacted before storage. Your token isn’t in the
audit log.
What’s still rough
This is early days — proxy v0.3.3, SDKs v0.3.0. A few things I hit during the walkthrough:
Placeholder DIDs. The issuer shows as did:agent:mcp-proxy and principal as
did:user:unknown. These are placeholders — the DID method strategy is being worked
out in ADR-0007. For now they’re
labels, not resolvable identifiers.
Fine-grained PATs don’t work for org write access. GitHub’s org-level policy for
fine-grained PATs creates friction even when permissions look correct. Use a classic
PAT with repo scope for org-owned repos for now.
Try it
The proxy is open source, ships as a single Go binary, and wraps any MCP server —
not just GitHub.
go install github.com/agent-receipts/ar/mcp-proxy/cmd/mcp-proxy@v0.3.3
Check the releases page for the
latest version.
Full walkthrough: Auditing Your GitHub MCP Server with Agent Receipts
The spec, SDKs, and proxy are all in the monorepo at
github.com/agent-receipts/ar.
Where to contribute
This is an open protocol and we’re building it in the open. Here are some concrete
areas where input would be especially valuable:
Security:
Protocol design:
Developer experience:
If you’re working on MCP tooling, agent governance, or verifiable credentials — or if
you just want to poke holes in the design — pick an issue and jump in.