Hook Overview
mcp-proxy covers MCP tool calls. agent-receipts-hook covers everything else: the native host tools that agent runtimes execute directly — Bash, Write, Edit, Read, WebFetch, and WebSearch in Claude Code, for example. Together they give the daemon a complete picture of agent activity across both channels.
How it works
Section titled “How it works”When an agent runtime invokes a native tool, it fires any configured PostToolUse hooks after the tool completes. agent-receipts-hook is wired up as one of those hooks. The runtime:
- Serialises the tool name, input, and response as JSON
- Spawns
agent-receipts-hookwith that JSON on stdin - Continues immediately — it does not wait for the hook to finish
The hook binary:
- Reads and parses the stdin frame
- Creates an
emitter.Eventwithchannel: "claude-code", the tool name, input, output, anddecision: "allowed"(PostToolUse fires only after the tool ran) - Forwards the event to the daemon over a Unix socket
- Exits 0 — always, regardless of success or failure
The hook never blocks the agent. Emit failures (daemon not running, socket missing, oversized payload) are silently dropped, consistent with ADR-0010 §“Failure model”.
Difference from mcp-proxy
Section titled “Difference from mcp-proxy”| mcp-proxy | agent-receipts-hook | |
|---|---|---|
| What it covers | MCP tool calls (tools/call JSON-RPC) | Native host tool calls |
| Deployment | Long-running subprocess per MCP server | Short-lived process per tool call |
| Supported clients | Any MCP client (Claude Code, Claude Desktop, Codex…) | Clients that support post-tool-use hooks |
| Policy enforcement | Yes — pass, flag, pause, block | No — hook fires after the tool ran |
| Local receipt signing | Yes — in-proxy Ed25519 | No — daemon signs |
| Taxonomy | MCP server tool names | Native tool names (Bash, Write, …) |
Both channels write to the same agent-receipts-daemon receipt chain, identified by a shared session ID.
MCP tool overlap
Section titled “MCP tool overlap”Claude Code tools with the mcp__* prefix are already captured by mcp-proxy. By default, agent-receipts-hook is registered with an empty matcher, which means it also receives mcp__* PostToolUse events. This produces a second receipt for each MCP tool call — one from the proxy, one from the hook. The two receipts are distinguishable: the proxy receipt has channel: "mcp" and the hook receipt has channel: "claude-code".
Whether this overlap is desirable depends on your use case. A future release will add a matcher-based option to exclude mcp__* tools from the hook. For now, the duplicate provides a cross-check: if the proxy and hook disagree on a tool call result, it is a signal worth investigating.
Session correlation
Section titled “Session correlation”Claude Code passes the current session ID in the session_id field of every hook frame. The hook forwards it to the daemon via WithSessionID, so all receipts within a session — from both mcp-proxy and agent-receipts-hook — carry the same session identifier and are grouped together when querying.