Treeship
SDK

@treeship/mcp

Drop-in MCP client replacement. Every tool call receipted automatically.

Install

npm install @treeship/mcp

Usage

One import change. Zero other code changes.

// Before (no attestation):
import { Client } from '@modelcontextprotocol/sdk';

// After (every tool call attested):
import { Client } from '@treeship/mcp';

Everything else stays the same. The wrapped Client class extends the original MCP Client and re-exports all other symbols unchanged.

import { Client } from '@treeship/mcp';

const client = new Client(
  { name: 'my-agent', version: '1.0' },
  { capabilities: {} },
);
await client.connect(transport);

const result = await client.callTool({
  name: 'search',
  arguments: { q: 'treeship' },
});

// Result now carries attestation metadata:
// result._treeship = { intent: "art_xxx", tool: "search", actor: "agent://mcp-my-agent" }

What gets attested

For every callTool() invocation, the bridge produces two attestations:

  1. Intent (before the call, awaited) -- proves what was about to happen. The tool name and a SHA-256 digest of the arguments are recorded.
  2. Receipt (after the call, fire-and-forget) -- proves what came back. Records elapsed time, exit status, and a digest of the output. The receipt is never awaited, so it never blocks the response.

Arguments and results are never stored directly. Only their SHA-256 digests enter the artifact. The digest proves which data was involved without exposing the data itself.

If attestation fails for any reason (CLI not installed, network issue, signing error), the tool call still completes normally. Treeship never breaks your MCP workflow.

Result metadata

When attestation succeeds, the result object includes a _treeship field with the full ToolReceipt shape:

interface ToolReceipt {
  intent?: string;                        // artifact ID of the intent attestation
  receipt?: string;                       // artifact ID of the receipt (undefined until async attestation completes)
  receiptReady?: Promise<string | undefined>;  // resolves when receipt attestation finishes
  tool: string;                           // tool name, e.g. "search"
  actor: string;                          // actor URI used for both attestations
}

Async receipt contract

The receipt field starts as undefined because receipt attestation happens off the critical path (fire-and-forget). This means the tool call returns immediately without waiting for the receipt to be signed and stored.

If you need the receipt artifact ID -- for example, to chain it as a parent in a subsequent attestation -- await the receiptReady promise:

const result = await client.callTool({ name: "search", arguments: {} });

// receipt is undefined right after the call returns
console.log(result._treeship?.receipt); // undefined

// await receiptReady to get the receipt artifact ID
const receiptId = await result._treeship?.receiptReady;
console.log(receiptId); // "art_xyz..." or undefined if attestation failed

If receipt attestation fails (CLI not installed, signing error, network issue), receiptReady resolves to undefined rather than rejecting. This keeps the error-handling contract simple: the promise never throws.

Environment variables

VariableEffect
TREESHIP_DISABLE=1Disable all attestation. Pure passthrough to the upstream MCP client.
TREESHIP_ACTOROverride the default actor URI (default: agent://mcp-{clientName})
TREESHIP_APPROVAL_NONCEAttach an approval nonce to intent attestations
TREESHIP_DEBUG=1Log all attestation activity to stderr

Actor naming

By default, the actor URI is derived from the name field in your Implementation object:

new Client({ name: 'my-agent', version: '1.0' }, { capabilities: {} });
// Default actor: agent://mcp-my-agent

Override it with the TREESHIP_ACTOR environment variable when you need a specific identity.

Approval flow

To attest that a human approved a tool call before it happened, set the TREESHIP_APPROVAL_NONCE environment variable. The nonce is included in the intent attestation and links back to the approval artifact.

export TREESHIP_APPROVAL_NONCE=abc123

This is useful when a human reviews and approves a planned action before the agent executes it.