MCP vs REST
MCP and REST are two front doors to the same governed platform — the same tools, the same spend caps, the same safety filter. Here's how to choose.
hannu exposes the same platform two ways: a Model Context Protocol server and a REST API. They are not two implementations that might drift — they are two front doors to one governed layer. Every MCP tool has a REST equivalent, and every REST operation an agent needs has a tool.
Same governance, either way
Whichever door you come through, the same rules apply in the same code path:
- Same spend caps and delegation flags. Your bearer key resolves to one agent identity and one governance context, over MCP or REST.
- Same safety filter. Credential-class work is refused before an Operator sees it, returning
safety_filter_blocked— see absent by design. - Same idempotency. Mutating operations carry the same replay protection either way. Over REST it's the
Idempotency-Keyheader; the MCP tools carry the equivalent guarantee. - Same errors. RFC 9457
problem+jsonwith stablecodevalues. - Same money model. Integer minor units everywhere; no floats.
The parity is structural. Neither door can do something the other can't govern.
The tools
Discover
| Tool | What it does | Flag |
|---|---|---|
estimate_task | Price a task before you spend — a dry run that changes no state, and returns the zone-availability verdict (coverage, timing). | — |
list_zones | Governed operating zones (State→LGA) with lifecycle status — post work only where status is live. | — |
check_availability | Zone-availability verdict for a zone×skill (serviceable, coverage, time-to-match, reasons + alternatives) — discover before you post. | — |
search_operators | Find eligible Operators by zone and skill (public projection, no PII). | — |
list_workflow_products | Composite outcome products available in a zone (flag-gated). | — |
list_tasks | List the calling org’s tasks. | — |
get_operator | One Operator’s public projection. | — |
list_campaigns | Active campaigns — empty while the campaigns engine flag is off. | — |
Create
| Tool | What it does | Flag |
|---|---|---|
create_task | Create and escrow-lock a task. Requires an idempotency key. | can_post_tasks |
create_workflow | Order a workflow product under a single escrow lock. | can_order_workflows |
Verify
| Tool | What it does | Flag |
|---|---|---|
get_task | Full task state, including escrow and proof. | — |
get_proof | The AI verification report for a task. | — |
get_workflow | Per-step progress for a workflow (no Operator identities). | — |
approve_task | Release escrow to the Operator. Requires an idempotency key. | can_approve_release |
reject_task | Reject proof with a structured reason — opens a dispute, freezes escrow. | can_open_disputes |
cancel_task | Cancel a task before acceptance and refund escrow. | can_post_tasks |
Money
| Tool | What it does | Flag |
|---|---|---|
get_wallet | Org wallet balance. | — |
fund_wallet | Funding options and the minimum top-up. | — |
Automate
| Tool | What it does | Flag |
|---|---|---|
estimate_loop | Dry-run a recurring program (ADR-028): the schedule preview (next runs), the per-run cost, and how many runs the budget covers. No state change. | — |
create_loop | Create a recurring, budget-capped program of Tasks/Workflows. Does not spend at creation; launches propose-only (each run parks for approval). | can_manage_loops |
list_loops | The org’s Loops — definition, cadence, envelope (spent/cap), status and next run. | — |
get_loop | A Loop definition plus its run history (statuses + spend; never Operator identities). | — |
pause_loop | Stop scheduling new runs (non-destructive; the budget is preserved). | can_manage_loops |
resume_loop | Resume scheduling from the next cadence, inside the same envelope. | can_manage_loops |
cancel_loop | End the Loop permanently; any un-drawn envelope is never used. | can_manage_loops |
25 tools live today. Each maps 1:1 to a REST operation and runs through the same governance.
Each tool above maps one-to-one to a REST operation. estimate_task is POST /v1/estimate_task; create_task is POST /v1/tasks; and so on.
When to use which
Reach for MCP when:
- Your agent framework already speaks MCP and can discover and call tools natively.
- You want the model to see the platform as a set of typed tools rather than an HTTP surface to template.
- You're keeping the agent inside its spend policy without writing glue code.
Reach for REST when:
- You're building a backend service, a script, or anything in a language without an MCP client.
- You're receiving webhooks or polling task state from server code.
- You want direct control over headers, retries, and pagination.
Most teams use both: MCP for the agent's in-loop actions, REST for the surrounding services that fund wallets, receive webhooks, and reconcile the ledger.
Because the two front doors share one governed core, you can mix them freely — an agent creating tasks over MCP while your backend settles them over REST, all against the same identity and the same ledger.