Pilotage is a small extension of MCP that lets AI agents write and run programs — SQL, automations, workflows — against any live system, check them before anything runs, and prove after that the result is really right.
MCP solved discovery and calling: an agent connects, lists the tools, reads each tool's JSON Schema, and calls them. That works perfectly — as long as every argument is a simple record: an id, an email, a number.
But the most powerful tools don't take records. They take programs: a SQL query. A home-automation rule. A multi-step workflow with data flowing between steps. The tool's schema can only say “this argument is a string” or “this is an object” — it cannot teach the language the program is written in.
Even a perfectly valid program can be logically wrong — and the output can hide it. A filter that matched zero rows plus a default fallthrough produces a result that looks completely fine. Human developers catch this with compilers, debuggers and traces. Agents today get only the return value. If we want agents that finish a mission fully and correctly, they need the same instruments we have — machine-readable, on the same connection.
MCP standardized the interface: what exists and how to call it. Pilotage standardizes competence: what an agent must know to use it well — and how it converges on a correct result.
No new transport. No WebSockets. Pilotage is an extension —
a set of conventions expressed entirely with MCP's own primitives (tools, resources,
one manifest), declared under the extension id
io.github.jafarsa0.pilotage
in MCP's extensions
capability negotiation at initialize. If your system speaks MCP today, the Pilotage
extension is additive.
Today, the knowledge of how to use a tool well lives inside the agent — hardcoded prompts, tuned integrations, folklore. Pilotage moves that knowledge into the surface itself. The provider of the tool becomes the teacher — and that one move changes the scaling law.
Nine bespoke integrations — prompts tuned per pairing, folklore in descriptions, nothing transfers. Every new agent or tool multiplies the work.
Agents implement the standard loop once. Surfaces publish their own guidance once (✎ = guides, catalog, diagnostics, trace). Every pairing just works — including the ones that don't exist yet.
Everything below rides ordinary MCP: resources you can read, tools you can call, plus one manifest that ties it together.
The front door. One small document that says: which languages this server accepts programs in, where the guides and catalog live, and which loop services exist.
The grammar, made navigable. Not one giant manual — a list of guides with topics, levels and token sizes, so a small agent fetches the right 2 KB instead of 120 KB. Rule: at least one core guide, kept small.
The live world the program refers to — tables, devices, capabilities — queryable and versioned, never a dump. Guides teach the grammar; the catalog names the nouns. A language needs one exactly when its programs reference live names.
The compiler that teaches. Zero side effects. Mistakes come back as structured diagnostics with hints — the agent fixes and retries. When the program is valid, the response includes a plan: what would run, and each step's risk — the go / no-go moment before anything touches the world.
Results come back with evidence: an ordered trace of every step — what it called, what went in, what came out, and which way every branch went and why. The trace is how an agent tells correct from plausible.
Every catalog entry and plan step carries a comparable danger class, aligned with MCP's own tool annotations (readOnlyHint, destructiveHint). Purpose: a generic gate — human approval, a policy engine — can say no without understanding your domain. Compulsory pilotage, in effect: in dangerous waters, nobody proceeds without a pilot.
This is Pilotage's real product: a single algorithm an agent implements once — and it works against every conformant server, whether the programs are SQL, automations or workflows. In pilotage terms: the catalog is the chart, validate + plan is the harbor pilot's pre-departure check, and the trace is the voyage log.
Mission: “monthly revenue per customer.” The server exposes one language, sql/postgres-15. Its catalog is fed by the database's own information_schema.
The agent now knows the real table and columns — today's, not what some doc said last year.
The hint is the cheapest convergence accelerator a server can offer. One fix, revalidate…
max_risk: safe → the gate stays open, no human needed. Run it.
Why the trace matters: if rows_matched had said 0, the output would still “look fine” — an empty-but-valid result. The trace is what lets the agent tell correct from plausible.
Mission: “when the front door opens after 22:00, light the porch.” Language: automation/1. Catalog: the live device registry.
Syntactically perfect. Semantically… it decided to also unlock the door. Nobody asked for that.
▲ max_risk: destructive The agent's loop — which knows nothing about smart homes — hits its generic gate: destructive plans need a yes. It asks the human, the human says “absolutely not”, the unlock step is dropped. The mistake died before it ran.
Without decisions, a daytime test looks like a silent success and the agent ships a broken automation. With it, the agent reads: “it skipped because it's 14:03 — the logic is right, test again after ten.”
If you already run an MCP server, everything here is additive — same transport, same auth, same session model.
One document: your languages, where guides and the catalog live, and the names of your loop tools. Declare the extension in the extensions capability at initialize, point to the manifest from _meta.pilotage.manifest, and tag every program-valued tool with _meta.pilotage.language. Examples use the short pilotage key; the registered form is the reverse-DNS extension id io.github.jafarsa0.pilotage.
≤ ~2k tokens, honest topics list, versioned. Add a reference guide that enumerates your diagnostic codes — the errors themselves must be learnable. Expose guides as resources and a pilotage_get_guide tool (some clients never show resources to the model).
List (paged, filterable) + get, over whatever inventory you already have — information_schema, a device registry, a capability store. Stamp every response with a catalog_version so drift is detectable at execute time.
You almost certainly have one (a parser, a linter, a schema checker). Wrap it: structured diagnostics with code · path · message · hint, strictly side-effect free. When valid, also return the plan: ordered steps + per-step risk + max_risk.
The envelope is standard — run_id, ordered steps with input/output/outcome, and decisions for every branch. The contents of each step's payload stay yours: any JSON. That's the whole deal — standard loop, your language.
| Piece | Status | Note |
|---|---|---|
| manifest + language tags | MANDATED | the front door — without it, agents can't even find the rest |
| guides (1 small core + codes reference) | MANDATED | topics, levels, token estimates, versions |
| catalog + catalog_version | MANDATED* | *when programs reference live names; empty otherwise |
| validate (side-effect free, structured diagnostics) | MANDATED | the one service every server must have |
| trace envelope (if you execute) | MANDATED | envelope standard; step payloads engine-opaque |
| plan on valid | OPTIONAL | declared in conformance; strongly recommended |
| explain / narration | OPTIONAL | facts are the contract; prose is a bonus |
| catalog search / changed-since | OPTIONAL | conformance flags |
| assertions (agent-declared postconditions) | v2 | the formal answer to “fully” |
Pilotage invents almost nothing. It names a layer the world keeps rebuilding ad-hoc, and gives it one standard shape.
InitializeResult.instructions — officially pitched as “a user manual for your server” — but a manual is free text: nothing checks that it was read, followed, or still matches the live system. Pilotage turns the user manual into a checkable contract — versioned catalog, side-effect-free validate with closed diagnostic codes, a plan you can gate, a trace you can audit — using MCP's own primitives.max_risk (destructive ⇒ ask a human); and engines keep their own authz — Pilotage's risk signal is for generic gates, it never replaces your permissions.decisions expose logic errors after. Fully: only the agent knows its mission, so it verifies the trace against it — and v2 adds assertions: agent-declared postconditions the engine checks mechanically against the trace.