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.
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.
npm i @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.
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):
{
"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:
{
"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:
| Tool | Args | What it does |
|---|---|---|
| passport_present | action, amount, currency, url? | Signs the assertion, attaches X-Passport, calls the platform (or returns the header), and reports the decision + resolved chain. |
| passport_status | — | Returns the agent's current mandate: scopes, caps, remaining_this_period, expiry, and status — so the model knows what it may do before trying. |
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…"
// }
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 #
| Command | Description |
|---|---|
| passport agent init | Generate a keypair and register the agent public key. |
| passport agent rotate | Rotate the agent keypair. |
| passport status | Show the active mandate, caps, and remaining period budget. |
| passport present | Sign an assertion and call the platform. Flags: --action --amount --currency --url. |
| passport mcp | Run the MCP server over stdio for Claude Code. |