Skip to content

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.

Agent runtime (Claude Code)

PostToolUse

hook stdin

stdio / JSON-RPC

Unix socket

Unix socket

Native tools — Bash, Write, Edit…

agent-receipts-hook

MCP tools — GitHub, Atlassian…

mcp-proxy

agent-receipts-daemon

receipts.db

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:

  1. Serialises the tool name, input, and response as JSON
  2. Spawns agent-receipts-hook with that JSON on stdin
  3. Continues immediately — it does not wait for the hook to finish

The hook binary:

  1. Reads and parses the stdin frame
  2. Creates an emitter.Event with channel: "claude-code", the tool name, input, output, and decision: "allowed" (PostToolUse fires only after the tool ran)
  3. Forwards the event to the daemon over a Unix socket
  4. 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”.

mcp-proxyagent-receipts-hook
What it coversMCP tool calls (tools/call JSON-RPC)Native host tool calls
DeploymentLong-running subprocess per MCP serverShort-lived process per tool call
Supported clientsAny MCP client (Claude Code, Claude Desktop, Codex…)Clients that support post-tool-use hooks
Policy enforcementYes — pass, flag, pause, blockNo — hook fires after the tool ran
Local receipt signingYes — in-proxy Ed25519No — daemon signs
TaxonomyMCP server tool namesNative tool names (Bash, Write, …)

Both channels write to the same agent-receipts-daemon receipt chain, identified by a shared session ID.

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.

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.