AN MCP EXTENSION — NO NEW TRANSPORT ENGINE-NEUTRAL ONE LOOP, ANY LANGUAGE

Schemas teach the moves. Pilotage teaches the game.

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.

learn author verify run + prove AGENT DONE guides catalog + draft validate + plan execute + trace
the problem

Agents can call. They can't compose.

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.

What the schema says
// the ENTIRE knowledge MCP can carry: { "name": "run_query", "inputSchema": { "type": "object", "properties": { "sql": { "type": "string" } ← “a string”. that's it. } } }
What the author actually needs
  • The grammar — syntax, functions, rules of the language
  • The live world — which tables / devices / capabilities exist right now
  • A checker — catch mistakes before anything runs
  • Proof — evidence afterwards that it did the right thing

And the second, quieter problem: plausible ≠ correct

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.

the idea

A competence layer on top of the interface layer.

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.

Standardize the loop, not the language.
Pilotage mandates the verbs and the report shapes — never the languages themselves. Your SQL stays SQL, your workflow language stays yours. The test for every field in the spec: “does a generic agent need it to work against ANY conformant server?” If not, it isn't mandated. This is exactly how LSP scaled to hundreds of programming languages without standardizing a single one.
separation of concerns

Guidance leaves the agent. The source becomes the teacher.

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.

Today — every pairing hand-built · N × M
agent A agent B agent C browser database workflows

Nine bespoke integrations — prompts tuned per pairing, folklore in descriptions, nothing transfers. Every new agent or tool multiplies the work.

With Pilotage — each side integrates once · N + M
agent A agent B agent C THE LOOP browser ✎ database ✎ workflows ✎

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.

A live specimen: a leading coding agent + its browser MCP.
One of the best agent–browser integrations in production works because the competence layer was built by hand, inside the client, for that one pairing: a browser-specific guide hardcoded into the agent's system prompt (“always fetch the tab context first”, “never reuse tab ids”), usage folklore packed into tool descriptions, and errors that teach at runtime. Look closely — it independently grew every Pilotage organ, ad hoc: the tab-context call is a live catalog, screenshots are the trace, instructive errors are diagnostics, the prompt section is the core guide.

Now swap in a different browser: none of it carries over. To be fair, MCP is already moving prose guidance to the surface — server instructions at initialize (“a user manual for your server”), and the Skills-over-MCP draft serving skills on-connection. The Pilotage extension takes the step prose can't: prose can teach — only Pilotage can check. A validate that runs before anything real, a plan you can gate, a trace you can audit — and the learning becomes the surface's responsibility.
anatomy

Five pieces, one cross-cutting signal.

Everything below rides ordinary MCP: resources you can read, tools you can call, plus one manifest that ties it together.

M

manifest

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.

{ "pilotage": "1.0", "extension": "io.github.jafarsa0.pilotage", "languages": [{ "name": "sql/postgres-15", "guides": "pilotage://guides", "catalog": "pilotage://catalog", "loop": { "validate": "validate_sql", "execute": "run_sql" } }] }
G

guides

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.

{ "id": "quickstart", "level": "core", "topics": ["select","joins","naming"], "tokens_estimate": 900 }
C

catalog

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.

{ "id": "public.orders", "kind": "table", "risk": "safe", "output_schema": { … } } // list · get · catalog_version on every response
V

validate + plan

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.

{ "severity": "error", "code": "unknown_column", "path": "select[1]", "hint": "close match: amount" }
T

execute + trace

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.

{ "steps": [ … ], "decisions": [{ "at": "condition after 22:00", "took": "skip", "because": "time = 14:03" }] }
R

risk — cross-cutting

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.

{ "risk": "destructive", "risk_hints": { "destructive": true, "readOnly": false } }
the loop

One loop. Any language. Any system.

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.

LEARN AUTHOR CONVERGE guides read the grammar catalog query live names draft write the program validate no side effects plan risk gate ▲ execute run for real trace evidence returned done · promote mission met, keep the program mission verified ✕ diagnostics — fix & retry trace says “not quite the mission” — revise
learn (knowledge) converge (verification) feedback edges ▲ risk gate — destructive plans need a yes
  1. Read the manifest — which languages, which services, what's supported.
  2. Read the core guide — small by rule; drill into topics only when needed.
  3. Query the catalog — the live names this mission needs. Note the catalog_version.
  4. Draft the program.
  5. Validate — structured diagnostics with hints. Loop here until clean. Nothing has run.
  6. Review the plan — every step's risk. Destructive? Ask the human. This is the gate.
  7. Execute with trace on — one round trip; the engine does the flow, not the model.
  8. Check the trace against the mission — branches, matches, per-step outcomes. Wrong? Back to 4.
  9. Done — and optionally promote the program so it becomes a plain tool for every future agent.
worked example · 01

SQL analytics — the typo the schema could never catch.

Mission: “monthly revenue per customer.” The server exposes one language, sql/postgres-15. Its catalog is fed by the database's own information_schema.

agent → catalog
pilotage_catalog · list kind=table q="orders"
{ "items": [{ "id": "public.orders", "kind": "table", "risk": "safe", "columns": ["id", "customer_email", "amount", "created_at"] }], "catalog_version": "cv_18422" }

The agent now knows the real table and columns — today's, not what some doc said last year.

agent → validate (first draft)
tools/call · validate_sql
{ "program": "SELECT customer_email, SUM(amout) FROM orders GROUP BY 1" }
diagnostics — nothing has executed
{ "valid": false, "diagnostics": [{ "severity": "error", "code": "unknown_column", "path": "select[1]", "message": "column 'amout' does not exist", "hint": "close match: amount" }] }

The hint is the cheapest convergence accelerator a server can offer. One fix, revalidate…

server ← validate (fixed) — now with a plan
valid + plan
{ "valid": true, "diagnostics": [], "catalog_version": "cv_18422", "plan": { "steps": [{ "calls": "public.orders", "risk": "safe" }], "max_risk": "safe" } }

max_risk: safe → the gate stays open, no human needed. Run it.

server ← execute — result with evidence
run_sql · trace_level=summary
{ "outcome": { "ok": true }, "output": { "rows": [ … 2,184 rows … ] }, "trace": { "run_id": "run_9d21", "steps": [{ "calls": "public.orders", "rows_scanned": 120540, "rows_matched": 118201, "t_ms": 412 }] } }

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.

worked example · 02

Smart home — the risk gate earns its keep.

Mission: “when the front door opens after 22:00, light the porch.” Language: automation/1. Catalog: the live device registry.

agent → draft an automation (it got creative)
tools/call · validate_automation
{ "program": { "trigger": { "entity": "sensor.front_door", "event": "opened" }, "condition": { "after": "22:00" }, "action": [ { "entity": "light.porch", "do": "turn_on" }, { "entity": "lock.front_door", "do": "unlock" } ] } }

Syntactically perfect. Semantically… it decided to also unlock the door. Nobody asked for that.

server ← valid — but look at the plan
valid + plan
{ "valid": true, "diagnostics": [], "plan": { "steps": [ { "calls": "light.porch", "risk": "write" }, { "calls": "lock.front_door", "risk": "destructive", "risk_hints": { "destructive": true } }], "max_risk": "destructive" } }

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

server ← a test run's trace — “nothing happened, and here's why”
trace
{ "outcome": { "ok": true }, "trace": { "steps": [], "decisions": [{ "at": "condition after 22:00", "took": "skip", "because": "time = 14:03" }] } }

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

for builders

Ship a Pilotage server in five steps.

If you already run an MCP server, everything here is additive — same transport, same auth, same session model.

01

Publish the manifest

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.

02

Write one small core guide

≤ ~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).

03

Mount the catalog

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.

04

Expose your validator — and derive the plan

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.

05

Return the trace with every execution

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.

What's mandated vs optional

PieceStatusNote
manifest + language tagsMANDATEDthe front door — without it, agents can't even find the rest
guides (1 small core + codes reference)MANDATEDtopics, levels, token estimates, versions
catalog + catalog_versionMANDATED**when programs reference live names; empty otherwise
validate (side-effect free, structured diagnostics)MANDATEDthe one service every server must have
trace envelope (if you execute)MANDATEDenvelope standard; step payloads engine-opaque
plan on validOPTIONALdeclared in conformance; strongly recommended
explain / narrationOPTIONALfacts are the contract; prose is a bonus
catalog search / changed-sinceOPTIONALconformance flags
assertions (agent-declared postconditions)v2the formal answer to “fully”
why it holds

Every piece of Pilotage already won somewhere else.

Pilotage invents almost nothing. It names a layer the world keeps rebuilding ad-hoc, and gives it one standard shape.

information_schema
databases have shipped a queryable catalog of their own live world for decades — it's where the word “catalog” comes from.
LSP
standardized diagnostics + language help for hundreds of languages by standardizing verbs and report shapes — never the languages. Pilotage's blueprint.
terraform plan
preview what would change, gate on it, detect drift before apply — Pilotage's plan + catalog_version, proven at scale.
EXPLAIN ANALYZE
execution evidence beside the result, because output alone can't prove the query was right. Pilotage's trace.
GraphQL introspection
one endpoint + a language + a machine-readable self-description — the vocabulary/language split, solved once already.
MCP destructiveHint · SEP-1303 · SEP-1862
the interface layer keeps conceding schemas aren't enough: destructive/read-only hints; validation errors fed back to the model (SEP-1303 — post-hoc, free-text); preflight per-op metadata (SEP-1862 — single call, no plan). Pilotage aligns with all three and adds the pre-run validate, the multi-step plan, the trace.

Fair questions

How is this different from Agent Skills?
Different territory. Agent Skills teach agents how to do tasks — procedural know-how shipped as prose and files. Pilotage lives where an argument is a program: an agent authoring SQL, automations or workflows in a server's own language and proving the run was right. Skills are the textbook; Pilotage is the chart, the pre-flight check and the voyage log. And they compose rather than compete: Pilotage guides can travel as Skills-format guides over the Skills-over-MCP draft (SEP-2640) — Pilotage adopts that channel for distribution. The durable difference isn't where guidance lives but what kind it is: prose can teach; only validate + plan + trace can be checked.
Why isn't plain MCP enough?
MCP's schemas describe records. A program is a sentence in a language — its grammar, semantic rules and live references can't fit in JSON Schema. MCP's own answer is 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.
Why an extension instead of a brand-new protocol?
Adoption economics. MCP already won the interface layer: transports, auth, clients all exist. Everything Pilotage needs is expressible as MCP tools + resources + one manifest. A standalone protocol would pay a huge cold-start cost for zero expressive gain. If the Pilotage extension earns gravity, it can graduate later — the GraphQL path. The adoption route is MCP's own Extensions Track of the SEP process.
Does it need WebSockets or a new connection model?
No. The “live loop” is logical, not physical: validate → fix → validate → run → trace are ordinary request/response calls. MCP's Streamable HTTP (sessions via a header, SSE only for streaming) carries all of it unchanged.
My engine's outputs don't look like anyone else's. How can this be standard?
That's the core rule: standardize the loop, not the language. Pilotage mandates envelopes — diagnostic shape, plan shape, trace shape — and leaves every payload inside them as engine-opaque JSON. The generic agent needs the envelope to run its loop; the domain reasoning happens on your payloads.
What stops an agent from running something dangerous?
Three fences, in order: validate can reject it; the plan exposes per-step risk before execution and conformant agents must gate on 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.
What does “fully and correctly” actually mean here?
Correctly: diagnostics kill syntax/semantic errors before running; the trace's 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.