16.8 Explain, then run — the plan tool and its endpoints
The MCP tool plan is offered wherever kg_query is:
plan({goal | question, inputs?, run?: false})→ {"outcome": "planned", "reads", "steps": [{from, kind, realm, adds, requires, reaches, runs, cost}], "compiled", "inputs": [{input, parameter}], "quote": {modelCalls, apiCalls, wouldRefuse, plan, unpriced}, "diagnostics"?} + {"rows", "warnings", "error"?} when run is true→ {"outcome": "unreachable", "missing", "detail"}→ {"outcome": "tie", "plans", "cost"}→ {"outcome": "refused", "reasons"} with a question: + {"question", "goal", "worldOnly"}, or "needsInput" / "declined" (§16.9)Exactly one of goal and question. inputs is an object (or its JSON text) of typed inputs (§16.4).
Explain first. With run: false, the default, plan introspects, plans, compiles and quotes: it runs
no step, reads no source and calls no model. A caller reads the steps and the quote, then calls again with
run: true, which plans again and executes what that compiled; rows and warnings come back unchanged. A
goal that reads only stored data needs no plan — it plans to no steps, and kg_query answers it directly.
The same answer, field for field, over REST:
POST /api/v1/admin/kg/plan { "goal" | "question", "inputs"? } → the tool's answer; never executesPOST /api/v1/admin/kg/plan/run { "goal" | "question", "inputs"? } → the answer, + rows and warningsExplaining and running are two routes, not one route with a flag: a studio that plans as its author
types calls a route that cannot execute whatever the body says, and a client allow-list can grant it without
granting the run. The caller is the authenticated principal. Neither or both of goal and question is a
400; every planner outcome — unreachable, tie and refused included — is a 200, because each is the
answer.

