MCP action chains — how an AI navigates the surface [Flow]
From the Meridians Wiki · Public · Maintained · joint
The MCP surface shares one bounded static catalog and materialises its first-class domain capabilities from the daemon manifest (built from the same QUERY_SPECS / OPERATION_SPECS / ACTION_META the docs use). Every result carries a uniform envelope { ok, working_set, result, delta?, affordances? } — the affordances are the spec's declared nextMoves, the hypermedia that teaches the practice so an agent follows links rather than re-scanning the full generated tool list.
The chain enforces orient-first → escalate-at-boundaries → stage-before-durable-action:
flowchart TD
orient["**orient** — describe / list_documents<br/>where am I"]
orient --> whats["read_loop · whats_changed<br/>what's running, what moved"]
orient --> when["read_hotspots → window_context<br/>WHERE in time it moved"]
whats -->|quiet| run["run_domain_loop / research_run<br/>produce the next feed"]
run -.->|"affordance: orient FIRST"| whats
whats -->|Position proposal awaits| stage["**stage_loop_directive**<br/>arm a Position — does NOT apply"]
stage -.->|affordance| apply["run_loop_project<br/>establishes the Position on next run"]
whats -->|steer a fold| designate["**stage_loop_directive** (merge)<br/>designate a Stream's outcome BEFORE the window"]
designate -.->|affordance| merge["run_loop_tutor<br/>autonomous group merge — folds now, no approval"]
whats -->|question callable| project["create_position<br/>stake a forward read (POS id)"]
project -.->|affordance| checks["research_run applies Checks →<br/>read_positions: the revision trail"]
The central-plane chain — discover → drill down → adopt → share
The chain above moves work inside one record. The other half of the surface is central (Supabase behind the gateway): the shared shelf of expert models, and the account/admin planes. It has its own shape — shelf altitude first, deep read second, and never guess at what a collection contains.
flowchart TD
browse["**exchange_browse**<br/>shelf cards · one page of 24"]
browse -.->|"nextCursor ≠ null"| browse
browse --> got["exchange_get_listing<br/>versions · ratings · full metadata"]
browse -->|"kind: collection"| mem["**exchange_collection_members**<br/>the ROSTER + which are separately listed"]
mem -->|"the listed subset"| sums["exchange_listing_summaries<br/>name them all in ONE call"]
mem -.->|"listingId: null =<br/>only inside the bundle"| adopt
sums --> got
got --> adopt["**exchange_adopt**<br/>fork → verify → NEW records"]
adopt -.->|affordance| focus["focus / list_documents<br/>→ rejoins the chain above"]
got --> vis["exchange_set_visibility<br/>snapshot → granted → public"]
vis --> grant["exchange_grant → list_grants → revoke<br/>the sharing progression"]
Two traps the affordances exist to prevent: a nextCursor ignored silently truncates the shelf at 24, and members: [] on a four-domain collection reads as empty when it actually means none separately adoptable — which is why collection-members returns the roster and the listed subset together.
The account and admin planes hang off the same tier, gated by a declared scope (director → your own account and listings; admin → the cross-Director views; owner → revoking an admin, and the SQL console). A mis-scoped call is refused locally, before it leaves the process, so the agent gets the reason instead of a bare remote 403. See platform model.
nextMovesare static per-spec, hand-written — on queries, ops, and control actions alike (a move names its destination byquery/op/actionid and the tool name is derived, so a rename can't strand it). A bare read/op with none falls back to a generic reconcile pair. The affordance says when ("IF belief moved…"); the agent judges.- The generated manual audits this graph:
knowledge-base/generated/MCP_MANUAL.mdreports hubs (most-pointed-at tools) and isolated tools — a real read/op nothing routes to, i.e. a discovery gap. Regenerate and read that section after adding a spec; it is the cheapest way to find a capability an agent will never discover. - The whole surface is also reachable generically via
meridians_query/meridians_run_op/meridians_ops/meridians_manifest— planned (unbuilt) ops are reachable only this way. - The practice this encodes (machine attends → human judges → model remembers → reality grades) is the same one PRODUCT_BEHAVIOURS.md documents for humans — MCP is a 1:1 clone, not a second mechanism.
Driving a Scenario game headless
One capability has its own headless shape distinct from the read→act→refine chain above: running a live Scenario game over MCP. The Scenario-group ops split the studio's one-click advance() into an on-rails player half (game-play-round), a synchronous non-generative driver (game-command), and the generative world half (game-resolve) — with a needsStudio boundary that stops a headless caller from running a generative step wrong. The full loop is mapped in scenario-loop → Driving a game headless (MCP).