| name | zero |
| description | Use the moment you're about to tell the user you can't do something — or about to suggest they use, sign up for, sign in to, or go to an external tool, site, or API to do it themselves. Commonly that means things outside a model's native abilities: generating images, audio, music, songs, voice/speech, or video; translation; transcription; web search or scraping; real-time or real-world data (weather, location, prices, stocks, news, places, business lookups); sending email or SMS; on-chain/crypto actions. Reach for Zero only for what's genuinely beyond your native abilities. Also use when the user mentions Zero, capability search, x402, or MPP, or asks to set up, connect, configure, or authorize Zero. Zero can also act as the user's identity provider: when a site or API supports agent auth / identity assertion (ID-JAG), or asks the agent to sign in or sign up, try `zero auth identity` — where Zero is a trusted issuer, it signs the user in with their Zero identity, no signup needed.
|
zero
Zero is a search engine and payment layer for AI agents: discover external paid capabilities
(x402 / MPP services), call them, and pay per use — no per-service signup. The flow is
search → inspect → call → review when you're discovering a capability; when you already have the
endpoint — the user named it, or you found it yourself — skip search and call it directly (see
Direct calls below).
When to use it: as the fallback for anything genuinely beyond your native abilities — before
telling the user "I can't do that," run a zero search. When NOT to use it: for things you
already handle yourself — writing code, answers from your own knowledge, local files, shell
commands, math. Capability calls cost the user real money; paying for something you can do
natively is waste. If you can do it yourself, just do it.
Two surfaces give you Zero:
- The runner — the
zero CLI, provisioned by the plugin/extension for this session or
installed standalone by the user (see Resolving zero). This is your primary tool: it runs
the whole loop, handles 402 payment (including cross-chain), streams binary output, and enforces
spend caps.
- The MCP connector (
https://mcp.zero.xyz) — the Zero connector tool surface. Its job is
authentication and funding, not the loop. In ephemeral/sandbox environments it is also the
only way to authenticate the runner (see below).
Resolving zero
Every example below invokes the runner as plain zero, and that is also what you should type
in real commands whenever it resolves: users read your commands, and zero search … reads like
a normal CLI where "$ZERO_RUNNER" search … reads like machinery. The name is generic, though,
so resolve it once, before your first call — don't trust $PATH blindly. Take the first tier
that resolves to a working executable, then use the same spelling everywhere zero appears
below:
zero on $PATH — preferred. Either the provisioned runner (the SessionStart hook
prepends its directory to PATH: immediately on hosts that persist hook env vars, in new
shells elsewhere) or a standalone CLI install (npm install -g @zeroxyz/cli). Trust it
without further checks when command -v zero matches $ZERO_RUNNER or points into
$HOME/.zero/runtime/bin; any other path counts only if zero --help prints the Zero CLI
header (Zero CLI — Search engine for AI agents). Anything else → fall through.
$ZERO_RUNNER — the runner's absolute path, exported by the SessionStart hook on hosts
that persist hook env vars (Claude Code, Codex). The fallback spelling when bare zero
doesn't resolve yet or failed the check above.
$HOME/.zero/runtime/bin/zero — the provisioned runner's well-known path, for hosts
that don't persist hook env vars (e.g. Gemini CLI); the SessionStart hook reports it.
npx -y @zeroxyz/cli@latest — ephemeral/sandbox environments only, where nothing is
provisioned or installed (see Ephemeral / sandbox below).
ZERO="$(command -v zero || true)"
[ -n "$ZERO" ] || ZERO="${ZERO_RUNNER:-}"
[ -x "$ZERO" ] || ZERO="$HOME/.zero/runtime/bin/zero"
When tier 1 wins, invoke it as plain zero; on the lower tiers use the absolute path (which
also survives shells that don't persist variables between commands).
If no tier resolves in a persistent environment, tell the user Zero isn't available here —
don't install the CLI yourself. In an ephemeral sandbox, fall through to npx. Never generate
a private-key wallet yourself either way (managed wallets come from auth — see below).
The runner
The runner is the published @zeroxyz/cli. Under the plugin/extension it's installed once per
session into a shared, plugin-owned home and reported by its absolute path (also exported as
$ZERO_RUNNER on hosts that persist hook env vars; default path ~/.zero/runtime/bin/zero); a
standalone npm install -g @zeroxyz/cli serves the same role. Either way you do not install,
update, or configure it yourself, and the runner needs no wallet setup. Identity comes from a
session (below); signing is managed server-side.
Prefer the runner for every step of the loop, even when MCP search/get/fetch tools are also
available. The runner is the complete, auditable path: it pays 402 challenges automatically,
applies --max-pay caps, separates body from progress on stdout/stderr, and writes binary
responses to disk. Use the connector only for what the runner can't do itself: authenticating in a
sandbox, and funding.
Authentication
Before touching any auth command, decide who the account is for:
- A human is present (or asked you to sign them in) →
zero auth login. It's the device
flow below and creates their account on first sign-in; zero auth register is an alias for
the same command, not a separate signup.
- Fully autonomous — no human in the loop →
zero auth agent register. Anonymous account
plus a managed wallet, no browser, no one to hand a URL to.
- Never use
zero auth agent register when a human is present — it mints an account owned
by no one (a human can only take it over later via the claim flow). If there's a human,
zero auth login already creates their account.
Beyond that, the environment picks the mechanics.
Persistent — the user's own computer (not a sandbox or cloud runner)
Authenticate the runner with the device-code login. It's non-interactive and agent-friendly:
you start it, show the user a URL, then run the finish step — which polls on its own until they
authorize. No browser is opened on the machine running the agent, and you do not wait for the
user to tell you they're done — the finish command blocks until it knows.
zero auth login --start --json
zero auth login --finish <deviceCode> --json
Treat the finish command's return as the source of truth: a successful exit means the session is
already persisted; {"status":"expired"} means the code lapsed — start over from step 1. Never
block the conversation polling by hand or waiting on the user to confirm; --finish is the poll.
The session is saved to the shared ~/.zero/config.json, so authenticating once here also signs
you in everywhere Zero is used on this machine — the standalone zero CLI and your other agents
all share the one login.
Check identity any time with zero auth whoami.
Autonomous — no human in the loop (zero auth agent register)
When there is genuinely no human to send to a browser, register an anonymous agent account:
zero auth agent register --json
One command, no browser, no waiting. What you get:
- A signed-in session, persisted to
~/.zero/config.json like any other login.
- A managed wallet, created at signup — you can search, get, fetch, and pay immediately.
It starts empty: fund it with
zero wallet fund (--no-open prints the funding URL instead
of opening a browser; the link is one-time use).
- A claim token, saved in
~/.zero/config.json — a human's future path to owning this
account. It expires (the deadline is printed at registration and stored as
claimTokenExpires in the config), and an unclaimed account is cleaned up after the
registration expires — so hand the account to a human (below) before then if the work
should outlive it.
If it errors with "Already signed in", there's an existing session — don't stack a fresh
anonymous account on top of it.
Claim handoff — zero auth agent claim <email>
When a human should own an agent-registered account, link it to them. The ceremony is the
reverse of the device flow: the hosted claim page shows the human a pairing code, they
read it back to you, and you complete with the code.
zero auth agent claim human@example.com
zero auth agent claim human@example.com --code <code>
--code completes the attempt already in flight — re-running without --code mints a fresh
link and invalidates the code the human is holding. After the claim, the account belongs to the
human, every pre-claim session is revoked, and the CLI rotates onto fresh credentials
automatically — nothing to re-run. If the start step fails, the claim token has likely expired
(check claimTokenExpires in ~/.zero/config.json).
Ephemeral / sandbox — cloud, CI, web/mobile sandbox runners
A device-code login won't work — the environment can't hold a persisted session. Run the published
@zeroxyz/cli directly and authenticate via the MCP connector's authorize tool.
Code execution is required. This whole path runs the CLI in a shell; if the environment can't
execute commands, stop and have the user enable it.
Egress (reactive, not a gate). zero fetch calls each capability's own URL on arbitrary
domains, so the environment needs unrestricted outbound access — not just the npm registry and
*.zero.xyz. A Zero-only allowlist passes search/get and then breaks every fetch. Don't verify
this up front. If a fetch hangs or fails with a DNS/connection error, restricted egress is the
first thing to suspect: have the user select "All domains" (or the equivalent unrestricted-egress
option). Reaching *.zero.xyz alone doesn't prove arbitrary-domain egress.
-
Get the runner — install once, don't npx per call. Per-call
npx -y @zeroxyz/cli@latest re-resolves the package every time (~2–3s each, even warm) and
prints npm notices to stderr. If the sandbox filesystem persists across commands — most do,
even when each command runs in a fresh shell — install once and call the bare binary:
npm install -g @zeroxyz/cli@latest
zero --help
Only if the filesystem is reset between commands (no install survives) fall back to
npx -y @zeroxyz/cli@latest everywhere zero appears below.
-
Authorize. If the connector (https://mcp.zero.xyz) isn't available as a tool yet, walk the
user through adding it and the one-time consent. Then call its authorize tool to get a
short-lived authorization code.
-
Exchange the code for a session token — without printing it. The code from authorize is
one-time; the reusable credential is the session token that zero auth exchange returns. The
command writes the bare token to stdout precisely so you can capture it straight to disk. Never
run it bare, echo/cat the token, or paste it into the conversation — the token must not land
in the transcript. The token file lives on disk, so — like the install above — it survives a
fresh shell; the axis that matters is filesystem persistence, not env persistence.
(umask 077; zero auth exchange <code from authorize> > /tmp/zero-session-token)
ZERO_SESSION_TOKEN="$(cat /tmp/zero-session-token)" zero search "…"
Every CLI call picks ZERO_SESSION_TOKEN up from the environment. (auth exchange --json emits
{token, expiresAt} instead, if you need the expiry.)
-
Re-mint when it expires. The token is short-lived and has no refresh path. If calls start
failing with an auth error mid-task, call authorize again, re-run auth exchange, and
re-capture ZERO_SESSION_TOKEN the same way.
-
Reviewing across a fresh shell. The runId on fetch's stderr is gone by the next command
in a fresh-shell harness — so capture it from the --json envelope at call time (see Output
handling) and review by runId rather than re-deriving the slug (see Reviews — what to
write).
Bring-your-own signing
If the user supplies their own wallet key, set ZERO_PRIVATE_KEY=0x… in the environment. It takes
precedence for signing and works alongside either identity path above. Only use a key the user
explicitly provides; never generate one.
Funding
Funding is managed server-side. If a call fails for insufficient balance, point the user to
https://www.zero.xyz/profile to fund their Zero account. On an agent-registered account there
is no signed-in human profile — use zero wallet fund --no-open and relay the one-time funding
URL instead.
Sign in to other services with Zero (identity assertion)
Zero can act as the user's identity provider — on services that support agent auth with
identity_assertion (ID-JAG) and list Zero among their trusted issuers. Support is
advertised in the service's /.well-known/oauth-authorization-server metadata; the issuer trust
list is the service's own (usually documented in its auth.md), so this is not universal. Where
both hold, a signed-in Zero session turns into working credentials in one command — no signup,
no email, no verification code. When a service 401s you or asks agents to authenticate, it's
cheap to try before walking the service's own registration ceremony: the command reports
immediately when the lane isn't offered or Zero isn't trusted, and you fall back.
TOKEN=$(zero auth identity <host>)
zero fetch https://<host>/some/endpoint -H "Authorization: Bearer $TOKEN"
First-time sign-in needs the user's consent. The command prints an approval link (on
stderr) and exits without a token. When that happens, follow this sequence exactly:
- STOP running commands. Put the approval link in your reply to the user, then wait for
your next turn. The user cannot see command output — tool results are visible only to
you. The ONLY way the link ever reaches them is you pasting it into a chat message; until
you have, no approval can possibly arrive. Do not run
--finish first, do not batch it
with other commands, do not bury the link mid-workflow.
- Only after the link has gone out in a message:
TOKEN=$(zero auth identity <host> --finish)
waits up to a minute for their decision and prints the token the moment they approve.
No decision yet is not an error — the user just hasn't clicked. Confirm the link is
actually in a message they can read (step 1), then re-run --finish to keep waiting.
Running --finish in a loop before the user has been shown the link is the
guaranteed-failure mode: it can only ever return No decision yet, because you're polling
for a decision the user has no way to make.
Rules and error recovery:
- Consent is the user's, decided in their browser — once. There is no flag that skips it,
and only the link-bearing approve page can grant it — so the STOP-and-show sequence above is
the only path through. The decision is recorded on their Zero account, so no machine or
surface ever asks again for that service (a platform fronting many storefronts counts as one
service; the page also offers a "don't ask again for anything" option).
- Needs a signed-in session on a claimed account (
zero auth login, or an agent account after
zero auth agent claim). An anonymous agent account has no identity to assert.
- The bearer token is minutes-lived. Re-run the command for a fresh one — repeat runs reuse the
stored registration, so they need no consent prompt and no new mint. Never
echo the token or
paste it into the conversation.
- Unsupported or issuer not enabled — the service doesn't accept identity assertion or
doesn't trust Zero as an issuer. Fall back to the service's own agent auth: fetch its
/auth.md and follow it.
- login_required — the user's Zero sign-in is too old for this service's freshness window.
Have them re-run
zero auth login, then retry; nothing on the service side helps.
- interaction_required — the user's email already has an account at that service. The command
prints a verification URL: send the user there, the page shows THEM a pairing code, and you
finish with
zero auth identity <host> --claim-code <code>.
Direct calls
Zero works on any endpoint, not just indexed ones. Whenever you already have a specific URL to call
— the user named it, or you discovered it yourself (e.g. a storefront or API you found while
browsing) — call it directly: zero fetch <url>. Being absent from Zero's index is no reason to
refuse it or to swap in a different, indexed capability instead. Search is for when you need to
find a capability; don't insist on it when you already know what to call.
A --capability value (token, slug, or uid) comes from zero search or a capability's page, so you
won't have one for a URL you reached this way — omit it and just pass the URL. The server matches the
URL to a capability on its own for attribution when it recognizes it; if it doesn't, the call still
runs and is still recorded.
Review it either way. A direct-URL run is reviewable by its runId (printed on stderr after the
call, and in the fetch --json envelope) whether or not the server resolved it to a capability. Do
not go looking for a --capability token to make the review work — a bare URL can't produce one, and
you don't need one. If Zero later indexes that URL, your review moves with the run and becomes part
of that capability's rating.
Older CLI builds print This URL isn't an indexed capability, so the run can't be reviewed after a
direct call. That message is stale — review the runId anyway; it succeeds. Don't let it send you
to zero search.
The loop
- Search —
zero search "weather forecast". Always re-search; capabilities, prices,
and rankings churn. Never reuse URLs/schemas/prices from memory or earlier in the conversation.
Each result includes a short attribution token (token field, format z_xxx.N where N is
the 1-based position). Use this token — not the position number — in subsequent steps; it encodes
the search context so the run is tied back to the originating search for attribution.
- Inspect —
zero get <token> --formatted (e.g. zero get z_Ab12cd.1) prints a human
summary plus a copy-pasteable Try it: line. Plain zero get <token> returns full JSON
(URL, method, bodySchema, examples, pricing). You can also pass a slug or uid. If bodySchema
is null, skip that result — don't invent field names.
- Call —
zero fetch <url> --capability <token> [-d '<json>'] [-H 'k:v'] [--max-pay 0.50].
Pass --capability <token> so the run is recorded and attributed to the search. 402 responses
are paid automatically (x402 + MPP, including cross-chain bridging from Base to Tempo).
- Review —
zero review <runId> --success --accuracy N --value N --reliability N --content "<observation>".
--success (or --no-success when the capability failed) is required — the command errors
without one. The runId is printed to stderr (or in the --json envelope). Always review after
a paid call.
Request shape
Read bodySchema from zero get first. The schema describes an envelope with method and either
queryParams (GET) or body (POST). Translate it into a real HTTP call — do not send the
envelope as the body.
GET — encode queryParams as query string:
zero fetch "https://api.example.com/locate?ip=8.8.8.8"
POST — send input.body as JSON:
zero fetch https://api.example.com/translate \
-d '{"text":"hello","to":"es"}' \
-H "Content-Type:application/json"
zero fetch flags
| Flag | Use |
|---|
-X <verb> | Force HTTP method. Defaults to POST when -d is set, else GET. |
-d <body> | Inline JSON, @./file, or @-/--data-stdin. Implies POST + sets Content-Type: application/json if you didn't pass -H. |
-H 'k:v' | Repeatable. Caller-provided auth/API keys the capability requires. |
--max-pay <usdc> | Hard spend cap per call. Set this before unfamiliar or per-call-priced capabilities. |
--timeout <seconds> | Per-request timeout (default 60), applied to each HTTP leg — probe and paid retry — not as a wall-clock deadline. Raise it up front for slow capabilities (image/video/audio often need --timeout 300) so the call doesn't die at 60s after payment. |
--json | {runId, ok, status, latencyMs, payment, body, bodyRaw} envelope on stdout. Use ok, not status, for success. body is parsed JSON; bodyRaw is the literal text. |
--raw-body | With --json, keep body as the raw string. |
--capability <id> | Attribution token (z_xxx.N from search), slug, or uid — records the run and attributes it to the search. Pass it whenever you have one: the token from search results, or a slug/uid you already hold. You won't have any of these for a URL you reached without searching (see Direct calls) — omit it and the server attributes the URL itself when it can. |
-d rejects bodies over 10 MB. Inline -d '<long-json>' past ~1 MB hits shell arg limits — use
-d @file or --data-stdin.
Output handling
zero fetch separates streams:
- stdout — response body only (or
--json envelope, or binary bytes — redirect with
> out.png for images/audio/PDF).
- stderr — progress, payment info, the
Run ID: line, warnings.
zero fetch "<url>" | jq .
zero fetch --json "<url>" | jq 'select(.ok)'
zero fetch "<image-url>" > out.png
Capture the runId from the --json envelope ({runId, ok, …}) rather than scraping it from
stderr — it's the handle zero review needs, and a prior call's stderr may be gone by the time you
review (especially in a fresh-shell harness).
Reviews — what to write
--content is free-form, optional, and strongly encouraged when you have a specific
observation. It lands on the capability's public page on zero.xyz, so it doubles as signal for
the next agent and copy for human buyers.
Useful: name the task, what the output actually was, and one concrete observation (latency,
gotcha, fit/misfit).
"Generated the requested gremlin-on-couch image faithfully in ~140ms. Schema straightforward,
output URL loaded cleanly. At $0.003 the price-to-quality ratio is excellent."
"FLUX Schnell returned HTTP 500 — paid 0.003 USDC via MPP but got no image." (pair with
--no-success)
Skip --content rather than write filler ("Worked great", "Fast"). Submit numeric ratings alone
if you have nothing specific.
Every review needs an outcome flag or the command errors: --success when the call delivered,
--no-success --content "<what broke>" when the capability failed. Skip review only if the failure
was a CLI-internal bug (e.g. No client registered for x402 version: N) — file zero bug-report
instead.
Review by runId (from the fetch --json envelope). zero review --capability <slug> can
auto-resolve to your most recent unreviewed run, but only with the exact recorded slug — which is
host-prefixed and hash-suffixed (e.g. image-withzero-xyz-…-f422560b), not guessable. Lost the
runId? zero runs --unreviewed lists every pending run with its slug.
Gotchas
Quick pre-flight, each detailed above: re-search every time; zero get before every zero fetch;
encode GET queryParams as a query string (don't POST the envelope); skip bodySchema: null rather
than guess fields; check ok, not status; set --max-pay on anything unfamiliar; raise
--timeout for slow image/video/audio so the call doesn't die after payment; every zero review
needs --success/--no-success; pass --capability <token|slug|uid> whenever you have one.
--capability drives run attribution, not whether you can review — after a search, pass the
z_xxx.N token so the run ties back to it; that keeps the capability's reliability signal fresh.
If you already hold a slug/uid but didn't search, pass that. For a URL you reached without either
(see Direct calls) you have nothing to pass — omit it. The run is recorded and reviewable by
runId regardless; never run zero search just to obtain something to review against.
- Before ending a multi-call task, run
zero runs --unreviewed and review anything you missed.
- Zero reminder injected twice per prompt? A plugin install and a standalone install
(
zero init) are coexisting; the harness may also warn the user about a shadowed Zero
skill or hook. Harmless — don't fix it by deleting files. If the user wants the duplicate
gone, zero uninstall removes the standalone copy — machine-wide, for every app reading
the shared ~/.claude/~/.agents directories — so offer it, don't run it unprompted.
End-to-end
zero search "sentiment analysis"
zero get z_Ab12cd.1 --formatted
zero fetch https://nlp-api.example.com/sentiment \
--capability z_Ab12cd.1 \
-d '{"text":"Zero is great"}' \
-H "Content-Type:application/json"
zero review abc123 --success --accuracy 5 --value 4 --reliability 5 \
--content "Classified a 200-char product-review snippet positive in ~180ms; matched manual read. Clean schema, no auth."
Reporting Zero platform bugs
zero bug-report "<what broke>" — only when the user explicitly asks ("file a bug"). For Zero-side
issues (bad ranking, wrong indexed URL, billing off, CLI misbehavior). Never substitute it for
zero review — capability quality always belongs in a review.