Home/Docs/For agents

Give your agent a passport

Hold a keypair, sign a short assertion on every request, and present it as an X-Passport header. Three ways to do it: the SDK, the MCP server for Claude Code, or the CLI.

Keys & the model #

An agent (agt_…) is an Ed25519 keypair plus self-declared runtime metadata — name, model, framework. The private key stays in the agent runtime; only the public key is registered with the passport. A principal then issues your agent a mandate that scopes exactly what it may do.

You never send the key

The agent signs each request locally. The platform and the passport only ever see a signature over a small, single-use payload — never your private key, and never the raw mandate.

Agent SDK #

The SDK holds the keypair, signs assertions, and attaches headers.

terminal
npm i @passport/agent
agent.ts@passport/agent
import { Passport } from "@passport/agent";

const passport = await Passport.load({
  agent: "agt_9f…",
  keyFile: "~/.passport/treasury-bot.key", // Ed25519 private key
  mandate: "mnd_tr7…",
});

// signs the assertion & calls the platform for you
const res = await passport.request("https://northbank.sandbox/v1/account/refill", {
  action: "account.refill",
  amount: 50000, currency: "USD",
  body: { amount: 50000, currency: "USD" },
});
// res.ok === true · res.passport.decision === "allow"

Register & rotate #

Register the public key once; rotate it any time without re-issuing mandates.

terminalCLI
passport agent init --name treasury-bot --runtime claude-code
# generates a keypair, registers the public key → agt_9f…

passport agent rotate --agent agt_9f…
# issues a new keypair; old signatures stop verifying

Under the hood these are POST /v1/agents and POST /v1/agents/{id}/rotate.

What gets signed #

Each request signs this exact payload with the agent key, as a JWS, and sends it in the X-Passport header. Nonce + timestamp make it single-use (see replay rules):

assertion payloadsigned per request
{
  "mandate_id":  "mnd_tr7…",
  "action":      "account.refill",
  "amount":      50000,
  "currency":    "USD",
  "platform_id": "plt_northbank",
  "nonce":       "3f9a…",
  "ts":          "2026-07-22T18:04:11Z"
}

MCP server (Claude Code) #

The agent ships an MCP server so Claude Code — or any MCP-capable runtime — can transact with a passport today, no glue code. Register it in your MCP config:

.mcp.jsonClaude Code
{
  "mcpServers": {
    "passport": {
      "command": "npx",
      "args": ["@passport/agent", "mcp"],
      "env": {
        "PASSPORT_AGENT":  "agt_9f…",
        "PASSPORT_MANDATE": "mnd_tr7…",
        "PASSPORT_KEYFILE": "~/.passport/treasury-bot.key"
      }
    }
  }
}

MCP tools #

The server exposes two tools the model can call directly:

ToolArgsWhat it does
passport_presentaction, amount, currency, url?Signs the assertion, attaches X-Passport, calls the platform (or returns the header), and reports the decision + resolved chain.
passport_statusReturns the agent's current mandate: scopes, caps, remaining_this_period, expiry, and status — so the model knows what it may do before trying.
Claude Code · tool callpassport_present
passport_present({ action: "account.refill", amount: 50000, currency: "USD" })

// → {
//     decision: "allow", reason: "ok",
//     chain: { principal, agent, mandate: { remaining_this_period: 150000 } },
//     verification_id: "vrf_9c1…"
//   }
Check before you spend

Have the model call passport_status first to read remaining_this_period, then size the transaction to fit the mandate. It turns a blind per_tx_cap / period_cap deny into a decision the agent makes on purpose.

CLI reference #

CommandDescription
passport agent initGenerate a keypair and register the agent public key.
passport agent rotateRotate the agent keypair.
passport statusShow the active mandate, caps, and remaining period budget.
passport presentSign an assertion and call the platform. Flags: --action --amount --currency --url.
passport mcpRun the MCP server over stdio for Claude Code.