MCP server
The agent's install — @veto-protocol/mcp adds the buyer-side checkout flow as MCP tools so an agent in Claude, Cursor, or Continue can transact against any protocol-speaking merchant.
@veto-protocol/mcp is the agent's install. It adds the buyer-side checkout flow as
MCP tools so an agent in Claude / Cursor / Continue can transact against any merchant
that speaks the Veto Checkout protocol — reading nothing but self-describing, reason-coded
responses. Hand-rolled stdio JSON-RPC 2.0, zero runtime dependencies, no build step.
This is the merchant-protocol MCP server: seven checkout_* tools that drive a
merchant's HTTP endpoints. It is distinct from the buyer-governance Veto MCP (veto_authorize,
veto_policy_*, …) that gates an agent's spend before it pays. The two compose: a governed
agent gets a veto mandate from its governor, then presents it via checkout_quote.
Install
Add the server to your MCP client config (or run veto mcp install --client claude):
{
"mcpServers": {
"veto-checkout": {
"command": "npx",
"args": ["-y", "@veto-protocol/mcp"],
"env": { "VETO_API_BASE": "https://api.veto-ai.com" }
}
}
}Tools
| Tool | What it does |
|---|---|
checkout_discover({ url }) | Fetch a merchant's manifest. Start here. |
checkout_catalog({ url }) | List a merchant's products. |
checkout_quote({ url, items, … }) | Create a session → self-describing payment_required. |
checkout_pay({ url, session_id, payment }) | Settle → run the acceptance gate. |
checkout_status({ url, session_id }) | Poll a session to its resolution. |
checkout_mint_mandate({ agent_id, merchant, max_amount, … }) | Sign a veto mandate (self-governed/test agents; needs VETO_ISSUER_SEED). |
checkout_verify_receipt({ jws, url? }) | Verify a merchant receipt offline against its JWKS. |
Each tool result is the merchant's reason-coded body verbatim, wrapped as MCP text content;
isError mirrors the HTTP ok, so the model sees the reason codes on any non-2xx.
The buyer flow, tool by tool
An agent never needs to read this page at runtime — every response carries a next field —
but the happy path is:
Discover. checkout_discover({ url }) → the manifest (catalog URL, rails, pay_to,
accepted mandate types, policy summary).
Quote. checkout_quote({ url, items: [{ sku, qty }], agent_id, buyer_intent?, mandate? })
→ a session_id and a self-describing payment_required.
Pay. checkout_pay({ url, session_id, payment }). The payment is rail-specific —
{ "kind": "mock", "token": "..." } for the offline mock rail, or the EIP-3009
transferWithAuthorization envelope for x402.
Verify. checkout_verify_receipt({ jws: <receipt>, url }) fetches the merchant's
/.well-known/jwks.json and checks the Ed25519 signature offline. Cross-check
claims.mandateRef against the mandate subject you presented.
Environment
The server holds no merchant state — every tool takes the merchant url as an argument
and calls it over fetch. Only the two mandate-minting vars are read at runtime:
| Var | Purpose |
|---|---|
VETO_ISSUER_SEED | base64 Ed25519 32-byte seed — required only for checkout_mint_mandate. |
VETO_ISSUER_KID | kid stamped on minted mandates (default veto-issuer-1). |
veto mcp install writes a VETO_API_BASE into the config block for forward-compatibility,
but the current server ignores it — pass the merchant URL to each tool instead.
Run locally
node --experimental-strip-types packages/mcp/src/bin.tsEach tool mirrors a wire endpoint and returns the same self-describing,
reason-coded payloads — so the agent follows the next field through the whole flow.
CLI
The veto CLI — login, scaffold a merchant, manage policy, and drive any protocol-speaking merchant (discover, catalog, quote, pay, status, receipt verify). Zero dependencies, runs on Node ≥ 22.6.
Reference
Look-it-up tables — the full reason-code vocabulary, every policy field, and the shared type contract.