Skip to content

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 executes
POST /api/v1/admin/kg/plan/run { "goal" | "question", "inputs"? } → the answer, + rows and warnings

Explaining 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.