Article IV Enumerated powers
4.1
The face
One POST route, /mcp, behind a bearer check. The transport is stateless by design:
sessionIdGenerator is left undefined, and a fresh McpServer instance is
constructed for each request and closed when the response closes. The source marks this as a
deliberate stage decision — “no session store; federation is S3.”
Every tool returns the same envelope: a single text content block containing the result serialised
as indented JSON. Advisor writes, which can collide on a version race, are wrapped so that a rare
collision surfaces as {ok:false, retry:true} rather than a rejected call — the write
itself is atomic and rolls back cleanly.
4.2
The surface, by power conferred
Twenty-four tools fall into seven groups. Two groups are read-only. Three mutate the board or the record. One is the intent path — the only route toward the outside world. One is confirmation-gated: it demands evidence that a person said the thing being written down.
4.3
Full schedule
| Tool | Class | Effect |
|---|---|---|
| operator_context_packet | read | The orientation packet for a day. |
| operator_state | read | Full payload: tasks, decisions, audit, intents, runs. |
| operator_user_world_model | read | The reviewable, evidence-backed user model snapshot. |
| operator_tick | write | Runs a tick in mode morning, start, check-in, night or status. |
| operator_update_task | write | Patches status, priority, owner, blocker, detail, lane or title. Returns changed. |
| operator_update_decision | write | Flips a decision to pending, rejected or parked. Cannot approve. |
| operator_record_instruction | write | Captures a human instruction or comment into state. |
| operator_set_directive | write | Sets a short-lived voice directive: quiet, topic_mute or focus. |
| operator_clear_directive | write | Lifts one directive, one kind, or all of them. |
| operator_list_directives | read | Lists the directives currently shaping outbound contact. |
| operator_update_world_model | gated | Adds, updates or retires typed beliefs. High-stakes kinds need confirmation evidence. |
| operator_revert_world_model | write | Undoes the last world-model change, optionally to a named version. |
| operator_apply_goal_change | gated | Pause, resume, add, remove, mark under review, or re-rank by importance. |
| operator_reconcile_goal_model | gated | Atomic reconciliation across goals, priorities and projects. Fails closed on ambiguity. |
| operator_revert_goal_model | write | Undoes the last goal reconfiguration. |
| operator_log_decision | write | Appends a strategic decision to a durable, human-readable log. |
| operator_record_progress | write | What this tick did, its outcome, what was tried, what failed. |
| operator_record_observation | write | One cheap world or self delta. Tainted at the sink, always. |
| operator_record_action_intent | gate | Records a typed action intent; the gate stamps the verdict. |
| operator_update_action_intent | write | Lifecycle transitions only. Never approval, never out of a terminal status. |
| operator_history | read | Recent operator days. |
| operator_run_start | write | Starts an autonomous run under a single-running lock, with stale recovery. |
| operator_run_heartbeat | write | The dead-man’s switch for a running run. |
| operator_run_finish | write | Closes a run as ok, error, skipped, cancelled or stale. |
4.4
Confirmation evidence
Three tools can change what the system believes a person is and wants. Those are treated as more dangerous than most external sends, because a durable belief injects itself into every subsequent turn as authoritative truth.
The rule is graduated. Low-stakes belief kinds — preference, note, project, risk, constraint —
apply freely, because they are reversible and audited. High-stakes kinds require two things at once:
the flag confirmedInConversation, and a confirmationQuote copied from a
recent human-authored instruction. The advisor validates the quote against the stored comment rows
rather than trusting the flag.
- high-stakes kinds
identity,goal,behavior_policy,constraint— the beliefs that describe who the person is, their direction, their rules and their hard limits. - lineageEach write records the exact version it superseded, so an undo restores the true pre-change state even if a batch worker bumped the version in between — “not the naive highest-superseded, which an interleaving worker bump poisons.”
- notificationEvery high-stakes write notifies the person, “so a silent (e.g. prompt-injected) edit can’t misdirect the system unseen — it becomes a loud, one-tap-revert notice.”
- fail closedReconciliation refuses ambiguous or missing targets and writes no version at all rather than guessing which goal was meant.
None of these tools sends, spends or publishes. The advisor module states the boundary explicitly: “this is INTERNAL state only … the deterministic action gate for external effects is untouched.”
4.5
A call, end to end
The transport speaks JSON-RPC over streamable HTTP and answers as a server-sent event. The client
library in this repository parses the data: line and then parses the text content
inside it — the tool result is JSON nested one level inside the MCP envelope.
$ curl -s http://127.0.0.1:3401/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H "Authorization: Bearer $IAN_OPERATOR_TOKEN" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{ "name":"operator_record_action_intent", "arguments":{"action":{"type":"spend","title":"Annual plan renewal", "amount_cents":4900}}}}' event: message data: {"result":{"content":[{"type":"text","text":"…"}]},"jsonrpc":"2.0","id":1} # the text block, decoded: { "id": 41, "action_type": "spend", "title": "Annual plan renewal", "status": "pending_approval", "policy_decision": "requires_approval", "policy_reasons": [ "Spending money, paid subscriptions, purchases, and paid vendor commitments require human approval before execution." ], "approval_decision_id": null, "idempotency_key": "<sha1(type|title|recipient|amount|body)[0:32]>", "affordances": ["accept", "edit", "respond", "ignore"], "created": true }
Field names and the reason string are those emitted by
actionIntentDto and rule 1 of the gate. Re-issuing the identical call the same day returns
the same row with "created": false, and the runners suppress the duplicate notification.
4.6
Downstream allowlists
The service is also a client. Where it reaches outward, it does so through short, enumerated lists rather than general access — and each list is enforced by a throw, not by convention.
| Caller | May call | Enforcement |
|---|---|---|
| Executor handlers | files_write · files_search · memory_recall · actions_workspace_run | “executor refused: … is not in the enumerated ian-brain tool allowlist”, thrown before any request is built. |
| Observe tick | actions_shell_run · machines_command_exec · actions_workspace_run | Never the search tools; a test asserts the runner imports no model, vector or embedding module at all. |
| Autonomous runners | operator_record_action_intent · operator_update_task · operator_update_decision · operator_record_instruction · operator_context_packet | Two separate allowlists, one per runner; two tests assert the intent-update tool is in neither. |
| Onboarding | read-only context tools | A test asserts it “refuses write/send/action Brain tools”. |
The downstream call is also bounded in time: an abort controller trips at a configurable timeout, defaulting to sixty seconds, “so a hung remote can never pin an executor claim open indefinitely.”