Skip to main content
Two families of MCP tools cover the parts of the platform an agent has to reason about rather than merely call: the runtime its team actually runs on, and the queue in which a proposed belief waits for a decision. Both are listed per tenant. A tool you cannot see is one this tenant’s Account Kit does not enable — that is a product decision, and no call will change it. A tool you can see may still refuse a specific call; that is authority, not scope, and the refusal tells you what would change it.
The tool list is resolved once, when the session opens. An Account Kit edit takes effect immediately at the gate but does not retract a tool already listed, so a long-lived session can show a tool whose primitive was switched off minutes ago. That drift is only ever permissive — the execution gate is re-evaluated on every call and denies — so the failure mode is a tool that refuses, never a tool that runs when it should not. Reconnect to refresh the list.

Runtime tools

Every tool addresses one tenant user’s runtime over one control wire — the same wire the REST routes already drive, resolved to whichever runtime is registered for that tenant. There is no carrier branch anywhere in these tools, and nothing you write should introduce one. Requires the tasks primitive.
Writes are queued, not sent. naive_teams_comment and naive_teams_unblock return when the command is durably held, which is not the same as the runtime having seen it. If the runtime is unreachable you receive a wait_for_delivery recovery step — do not send the command again. A second send is a second comment, or a second unblock.

Brain governance tools

The twenty-one naive_brain_* content tools cover what the brains know. These five cover how something comes to be known: the writeback envelope a claim arrives on, the proposal it becomes, and the decision that resolves it. Requires the brain primitive, which is opt-in — an Account Kit with no brain entry is a refusal, not a default.
decision: "accept" canonizes a claim into the company’s source of truth, so an agent call parks for a human. You get an approval id, not a result. decision: "reject" applies immediately, with you recorded as the reviewer.

How a refusal reaches you

A refused call does not return a bare error. It returns a verdict envelope, carried in both structuredContent and the text block:
Read three fields, in this order:
  1. retryabletrue only when the envelope also carries a retry_after recovery step, which happens only for verdict: "unavailable". If it is false, calling again cannot help: the decision is a function of a policy snapshot your calls do not change.
  2. recovery[] — the one thing that would change the outcome. It names a tool or a human act, never a shell command.
  3. alternatives[] — other tools that would work right now.

Verdicts

attest is a member of the verdict union and nothing produces it today: measured across the API, the platform packages and the governor, the only occurrence of the string is the type declaration itself. Handle it if you are writing an exhaustive switch — the union is closed and will not grow silently — but do not build a flow that waits for one.
Three vocabularies, and they are not the same set. The closed decision engine answers allow | deny | freeze. POST /v1/policy/explain answers allow | deny | park. This envelope adds attest and unavailable on top, because a model needs to tell an outage from a decision. Do not map one onto another by name alone.
A park gives you wait_for_approval with an approval_id, an expires_at, and on_expiry: "deny" — an approval window that elapses becomes a refusal. Poll naive_approvals_get. You cannot approve your own request. If you refuse the same call three times, the envelope stops offering choices: you get exactly one escalate_to_human step and no alternatives. That is the signal to stop and tell the user.

Connections enumeration is filtered

naive_connections_tools now filters its output through the same Account Kit rule naive_connections_execute obeys. A toolkit the kit does not permit is refused with a request_toolkit recovery naming it, rather than enumerated; per-tool enable/disable filters are applied to the result, and withheld_by_policy reports how many names were removed. Calls that worked before still work — every kit ships permissive by default, so this bites only where somebody deliberately narrowed one.