Treeship
Integrations

Claude Code

Integrate Treeship with Claude Code via the official plugin (recommended), CLAUDE.md, or MCP server.

Claude Code has three integration paths. The plugin is the recommended one — it auto-records every session via hooks, no manual treeship session start to remember.

The Treeship Claude Code plugin ships every session as a portable, signed receipt with zero configuration. SessionStart / PostToolUse / SubagentStart / SubagentStop / SessionEnd hooks fire automatically; the bundled @treeship/mcp server captures any MCP-routed tool calls; built-in tools (Read, Write, Edit, Bash, Grep, Glob) are captured via the PostToolUse hook.

Install the marketplace + plugin

claude plugin marketplace add zerkerlabs/treeship
claude plugin install treeship@treeship

treeship add claude-code runs these two commands for you when the claude CLI is on PATH (it asks first, or takes --all), and prints them otherwise.

The plugin lands at ~/.claude/plugins/cache/treeship/treeship/<version>/ and auto-loads in every subsequent Claude Code session. No restart needed for a session you start after install; existing sessions pick it up next time you open them.

Make sure the project is Treeship-initialized

cd your-project
treeship init   # if you haven't already

The plugin's hooks are gated on ./.treeship/ existing in the project root. Without it, the hooks silently exit zero so they never break Claude Code in unrelated projects.

Start coding

Open Claude Code in the project. The SessionStart hook automatically opens a Treeship session named after the project + timestamp. Every Read, Write, Edit, Bash, etc. is appended to the session's event timeline. When the session ends, the SessionEnd hook seals the receipt and surfaces the shareable session report URL back into the conversation context.

Updating: when a new plugin version ships, run claude plugin marketplace update treeship && claude plugin update treeship. Restart any open Claude Code sessions to pick up the new version.

What the plugin ships

ComponentWhat it does
.mcp.jsonMounts @treeship/mcp via npx -y so MCP tool calls get signed receipts
hooks/hooks.jsonSessionStart (auto-start), SessionEnd (seal + report URL), PreToolUse (the gate: the session actor's registered agent card as a runtime boundary, see below), PostToolUse (capture built-in tools, tagged with the subagent instance when a subagent made the call), SubagentStart / SubagentStop (agent.spawned and agent.returned, so the receipt's agent graph carries the spawn)
monitors/monitors.jsonLive receipt/event counter streamed into Claude's context
skills/treeship-sessionHow to close with a real headline before SessionEnd auto-headlines

The gate (PreToolUse)

Register the session's actor with a card and the plugin enforces it before each tool runs:

treeship agent register --name claude-code \
  --tools file.read,file.write,shell.exec,net.fetch \
  --escalation "mcp__postgres__*" \
  --network api.example.com,*.internal.net

Claude Code tools map onto the card's vocabulary: Read, Grep and Glob are file.read; Write, Edit, MultiEdit and NotebookEdit are file.write; Bash is shell.exec; WebFetch and WebSearch are net.fetch; Agent and Task are agent.spawn; an MCP tool mcp__server__tool is server.tool. The card's patterns take one *, the same rule verify-capability uses.

Card saysThe gate does
a halt stands for the actor (treeship halt <actor> or *)denies every tool call, signs each refusal as blocked.v1 (operator_revocation), before any other rule
forbidden matchesdenies, and signs a blocked.v1 receipt (scope_violation) into the session
network is declared and a WebFetch host is outside itdenies, and signs a blocked.v1 receipt naming the host. A host inside the list is allowed. network takes exact hosts or *.suffix patterns (*.example.com also matches example.com); an empty list means no network scope, not "no network"
escalation_required matchesasks the operator
bounded_tools matchesallows
the card allowed it and TREESHIP_JUDGE is setasks the judge: 1 for the built-in rules (paths outside the workspace, destructive or exfiltrating shell commands, hosts outside the declared scope, amounts above TREESHIP_JUDGE_BOUND), a URL for any judge that speaks the contract. Every answer is signed as judgement.v1; an answer over TREESHIP_JUDGE_THRESHOLD (default 0.5) denies and signs blocked.v1 (policy_threshold_exceeded); ask asks the operator. A judge that cannot answer fails open with an agent.note (the card already allowed the call); TREESHIP_JUDGE_STRICT=1 escalates it to the operator instead
none of the aboveallows and leaves an agent.note ("off-card") in the timeline; with TREESHIP_GATE=enforce it denies and signs the refusal instead
no card for the actorallows everything, says nothing

The session's actor is what the gate looks a card up by. The plugin starts sessions as agent://claude-code (override with TREESHIP_ACTOR), the same actor the bridge signs as; a session started by hand under another actor has no card and the gate says nothing. The model recorded on the receipt comes from TREESHIP_MODEL or ~/.claude/settings.json at session start, not from the running session, so the two can differ if the model is switched mid-session. The network scope reaches what the harness routes through WebFetch. A curl inside Bash is a shell command to the gate, judged as shell.exec; its destination is not inspected here. The sealed receipt still records every agent.connected_network event and judges it against the declared scope (see network scope in the receipt).

The kill switch is treeship halt agent://claude-code --reason "…"; treeship halt --lift agent://claude-code ends it, and both are signed receipts on the session chain. A refusal is a signed artifact, sealed with the session like any action, so "the gate stopped it" is a claim a stranger can verify. The gate fails open on every error path: a broken Treeship never blocks a tool call by accident. It is a boundary on what the harness routes through hooks, not on what a process spawned outside them can do; the coverage level says where that boundary is.

The judge is opt-in and runs after the card, never instead of it. With TREESHIP_JUDGE=1 the built-in rules judge is deterministic and replayable, so a stranger can re-run it on the sealed state and check the answer; with a URL, any judge that speaks the contract sits in the same slot and leaves the same receipt. No model is in the decision path unless the operator points at one. | skills/treeship-verify | How to verify a receipt URL someone shares with you | | skills/treeship-report | How to publish + share a session report URL |

Method 2: CLAUDE.md instruction file (no plugin)

For users who don't want to install the plugin but still want Claude Code to use Treeship intentionally:

Install Treeship CLI

curl -fsSL https://www.treeship.dev/setup | sh

The /setup one-liner installs the CLI, runs treeship init, and asks before instrumenting any AI agents it detects. With no interactive terminal, instrumentation is skipped unless TREESHIP_SETUP_YES=1 is set.

Drop CLAUDE.md into your project

curl -o CLAUDE.md https://raw.githubusercontent.com/zerkerlabs/treeship/main/integrations/claude-code/CLAUDE.md

Claude Code reads CLAUDE.md from the project root automatically. The template instructs Claude Code to wrap every shell command with treeship wrap --, set model/cost env vars, and close sessions with meaningful summaries. It also includes the trust block (what @treeship/mcp captures, what it doesn't, when data leaves the machine) so Claude Code trusts the MCP server.

Method 3: MCP server only

If you don't want hooks but want MCP tool calls captured, add the server yourself. Claude Code reads MCP servers from a project's .mcp.json (project-scoped) or ~/.claude.json (user-scoped, under a top-level mcpServers key) -- not from ~/.claude/mcp.json, which Claude Code never reads:

// .mcp.json (project root)
{
  "mcpServers": {
    "treeship": {
      "command": "npx",
      "args": ["-y", "@treeship/mcp"],
      "env": {
        "TREESHIP_ACTOR": "agent://claude-code",
        "TREESHIP_HUB_ENDPOINT": "https://api.treeship.dev"
      }
    }
  }
}

Restart Claude Code after adding the config. This catches MCP-routed tool calls (e.g. brave_search, notion) but not Claude Code's built-in tools (Read, Write, Edit, Bash) — those bypass MCP entirely. For full coverage, use Method 1.

Start the session as the same actor the bridge signs as (treeship session start --actor agent://claude-code, which the plugin's SessionStart hook does by default, or TREESHIP_ACTOR for another name). The bridge's intents and results chain onto the open session. The actor is signed into each artifact.

Live monitoring

While Claude Code works, watch the session in real time:

treeship session status --watch

The plugin also streams live receipt/event counters into Claude's context via its background monitor, so the agent itself can see the receipt being built without polluting the chat.

What appears in the receipt

SectionPlugin (Method 1)CLAUDE.md (Method 2)MCP only (Method 3)
Built-in tool calls (Read, Write, Edit, Bash, etc.)Captured via PostToolUse [AUTO]Via treeship wrap for shell [AUTO]Not captured
MCP tool callsCaptured via @treeship/mcp [AUTO]Captured via @treeship/mcp [AUTO]Captured [AUTO]
Session lifecycleSessionStart/SessionEnd [AUTO]Manual session start/close [EXPLICIT]Manual [EXPLICIT]
Model/costVia env vars [EXPLICIT]Via env vars [EXPLICIT]Via env vars [EXPLICIT]
NarrativeAuto-headline + skill-driven manual close [MIXED]Via --headline/--summary/--review [EXPLICIT]Not captured

Claude Code's built-in tools (Read, Write, Edit, Bash) are internal to the runtime, not MCP tools. They bypass the MCP bridge entirely. The plugin's PostToolUse hook is what closes that gap — without it, only MCP-routed tool calls would show up in the receipt timeline.

Verifying a session report

The URL printed by treeship session report is the human-readable session report at treeship.dev/receipt/<id>. Anyone you share it with can verify the embedded receipt themselves:

treeship verify https://treeship.dev/receipt/<id>

Verification is pure WASM (@treeship/core-wasm), works entirely offline, doesn't phone home.