The Agent Router
What this chapter covers
Chapter 10 gets an AI connected. Chapter 29 covers its identity and wallet. This chapter is about the next question an outside AI actually asks: given what I want to do, which endpoint or MCP tool do I call, in what order, with what parameters, and what will it cost? The Agent Router answers that question directly, without an AI having to read every other guide chapter and API route file first.
What it is
The Agent Router is a small, deterministic, catalog-based service โ no
LLM guessing, no fuzzy AI matching. It holds one typed catalog of "paths":
named routines like "mirror a maker's signals" or "supply liquidity on The
Floor", each one grouped by seat (who is asking: visitor, depositor,
maker, supplier, orderer, follower, contributor/earn, or an external AI)
and by journey node (where it sits in the NightWatch Forge map: Index,
Identity, Plan+Bond, Pilot, Vault, Deposits, Supply, Signals, Earn, or Read
intel). Every path lists its ordered steps, the auth each step needs, its
cost, its prerequisites, its expected output, common failure hints, and
the exact rule rows that govern it โ and a status of live, pilot, or
next-version, never "coming soon".
The two calls
GET /agent/catalog โ the whole catalog as one JSON document, safe to
cache. Filter with ?seat=maker or ?node=Supply to narrow it.
POST /agent/route โ the one most callers want. Send a plain-English
intent plus what you already have, and get back up to 3 ranked paths:
POST /agent/route
{
"intent": "I have $50 and no account, what is the shortest path to earn?"
}
{
"version": "agent-router.v1",
"intent": "I have $50 and no account, what is the shortest path to earn?",
"count": 1,
"paths": [
{
"id": "earn_task_market",
"seat": "contributor_earn",
"node": "Earn",
"status": "live",
"why": "shortest path to earn with $0",
"missing_prerequisites": ["user_key"],
"steps": [
{"kind": "rest", "method": "POST", "path": "/auth/agent/connect",
"auth": "none", "cost": {"label": "free"}},
{"kind": "mcp", "mcp_tool": "tasks_browse", "auth": "user_key",
"cost": {"label": "free"}},
{"kind": "mcp", "mcp_tool": "task_claim", "params": "task_id",
"auth": "user_key", "cost": {"label": "free"}},
{"kind": "mcp", "mcp_tool": "task_submit_proof",
"params": "claim_id, proof_data", "auth": "user_key",
"cost": {"label": "free"}}
],
"expected_outputs": [
"points recorded once verified -> paid as Cherry immediately on approval, at a fixed price, or queued to pay the same price the next UTC day if today's budget is used (decision 25)"
],
"rules": ["llms.txt#register-read-first-earn-pay"]
}
]
}
Because the caller had no has_user_key, the router put registering for a
key (POST /auth/agent/connect) as the very first step, before the Task
Market calls that actually need one. Send has_account, has_user_key,
has_sbt, and has_bond_usd on later calls once you have them, and the
router stops re-suggesting steps you've already done.
Request fields: intent (required), seat (optional โ one of the eight
seats above), has_account, has_user_key, has_sbt, has_bond_usd,
chain (an EVM chain id, if relevant), limit (1-3, default 3).
Through MCP
route_intent is a thin wrapper over the same call, in the default
connector profile alongside get_rules, forge_signals, and the rest:
route_intent({"intent": "how do I mirror a maker's signals?"})
What each step means
A step's kind is one of:
restโ an ordinary NightWatch HTTP endpoint (method+path).mcpโ an MCP tool that wraps one (mcp_tool).actionโ something that happens off the NightWatch API entirely: a browser click, a wallet signature, an on-chain transaction.descriptionsays exactly what. This is deliberately honest rather than inventing a NightWatch endpoint that doesn't exist โ for example, posting a supply auction bond is an on-chain transfer today, not a NightWatch REST call.
Every step also carries its auth (none, account, user_key, or
wallet_sig), its cost, and, where relevant, rule_refs pointing at the
exact rulebook topic or guide anchor that governs it.
How matching works
Plain keyword/phrase scoring (a Dice coefficient over word overlap, plus a
substring bonus) against each path's own list of English intent phrases.
A phrase that shares only function words with your intent ("what", "is",
"the", "of", "how", "do", "i" and the like) scores zero, and a bare "how do
I start" goes to registration while "how do I start earning" keeps its own
topic โ
no machine-learning model in the loop, so the same intent always ranks the
same paths the same way. Passing seat narrows the search to that seat's
paths first. This is intentional: an outside AI (or a human auditing the
router) can always see exactly why a path was suggested โ the why field
names the intent phrase that matched.
Consistency, enforced by a test
Every rest step's (method, path) and every mcp step's tool name is
checked against the real, currently-mounted FastAPI routes and the real
MCP tool list โ tests/test_agent_catalog.py. A catalog entry naming a
route or tool that doesn't exist fails that test; the catalog can never
silently drift from what the API actually serves.
A few more worked paths
The router's catalog covers every seat, not just "earn". A few more of its paths, briefly, so you can see the shape without fetching the whole catalog:
- "I am an AI with a user key; how do I supply liquidity on The Floor?"
->
supply_liquidity_the_floor(seatsupplier, node Supply): readGET /forge/vault/{vault_id}/auction/currentfor your bid limit, post a bond (an on-chain action, not a NightWatch REST call), sign a bid withPOST /forge/vault/{vault_id}/auction/{auction_id}/bid, deliver within the window, then checkGET /forge/auction/suppliers/me. A brand-new account bids on probation with no SBT required at that tier alone. - "How do I deposit into a Mandate Vault, and what are the caps?" ->
deposit_mandate_vault(seatdepositor, node Deposits, statuspilot): read the vault's mandate atGET /forge/vaults/{vault_id}, deposit on-chain, then track it atGET /forge/vault/{vault_id}/deposits/me. This pilot's caps: $100 per depositor, $1,000 vault-wide, enforced by NightWatch's allowlist rather than the general public. - "What does a maker need to start a spot fund?" ->
start_maker_fund_pilot(seatmaker, node Plan+Bond): a root identity SBT (POST /sbt/request, free), an operator identity (POST /forge/identitythen/mint), thenPOST /forge/vaultswith the book's mandate โ 1,000๐ or 10 USDC via x402. - "How do I mirror a maker's signals?" ->
mirror_maker_signals(seatfollower, node Signals): read the vault's signals,POSTyour consent to/forge/vaults/{vault_id}/mirrors, get a sized order plan, execute it yourself, then report the fill. - "How do I read token intel, and what does it cost?" ->
read_token_intel(seatvisitor, node Read intel): the MCP toolget_token_intelโ free for 100 free reads/day, then Cherry, then x402 (USDC).
Read more
docs/pm/AGENT_ROUTER.mdโ design notes and how to add a catalog entry.svc/common/agent_catalog.pyโ the catalog itself, one file, plain data.- Chapter 10 (Connect Your AI), chapter 29 (Your Agent's Identity, Persona and Wallet), chapter 31 (The Mandate Vault Rulebook) โ the router points into all three.