← All writing

Essay

MCP is the front door, not the foundation

Good agent infrastructure needs more than a tool server. It needs one durable contract across MCP, REST, code, and the command line.

There is a test in the SproutPad repository that expects the exact public MCP operation set. Adding or removing a tool changes the baseline and fails the build. Another test asks a less flattering question: does every public tool have a complete canonical descriptor, including its input, output, effect, retry, task, approval, and transport behavior?

For a while, the first test felt like a decent measure of progress. An agent could do more things each week. The second test changed the goal. A larger catalog is not an improvement when the server and its clients disagree about what an operation means.

The baseline became mildly incriminating, which made it much more useful.

MCP solved an important problem for us. It gave agents a practical way to find and call the product. It did not solve the slower work of deciding what an operation means, how it behaves after a lost connection, or whether its result looks the same over REST and at a terminal. We had treated the tool catalog as the architecture when it was really one view of it.

The catalog grew one useful tool at a time

None of the tools looked unreasonable when it was added. Search for a domain. Estimate a launch. Apply a plan. Read a task. Verify a receipt. Each name corresponded to something an agent genuinely needed.

The trouble appears in the gaps between those tools. A domain search is an ordinary read. A launch estimate is also a read, provided it does not reserve money. Preparing a priced plan may claim budget headroom. Applying the plan can buy a domain for a year.

Those distinctions do not fit comfortably in a tool name or a persuasive description. They belong in data that the server and its clients both understand: effect, idempotency, approval behavior, task behavior, input, output, and the interfaces that expose the operation.

Large catalogs also charge the agent for discovery. Definitions occupy model context before the user has received an answer. Results add another bill. The MCP client guidance suggests progressive discovery and programmatic calling when catalogs or intermediate results get large. We intend to support both. They help a client manage the catalog, but they cannot repair an ambiguous operation.

The contract now sits underneath MCP

The operation registry is not another API. It is the shared contract from which public interfaces can be projected.

Consider domain registration. The price, approval rule, receipt, and retry behavior should remain the same whether the request comes from an MCP tool, curl, an SDK method, or sproutpad invoke launch_service. Today, enough of those paths are hand-wired that drift is possible. A hidden retry in the CLI or a missing receipt field in MCP would give the same operation different meanings.

We now describe new operations in one place. The record includes the input and output schemas, whether it reads or mutates, whether idempotency is required, whether it returns a durable task, how approval can intervene, and which bindings exist. Generators do the dull translation work. Tests check for missing routes and command collisions.

The type continues to evolve, but this reduced version shows the idea:

type AgentOperation = {
  id: string;
  effect: "read" | "mutation";
  inputSchema: unknown;
  outputSchema: unknown;
  idempotency: "none" | "required";
  task: "immediate" | "durable";
  approval: "never" | "policy_may_require" | "human_only";
  programmatic: "read" | "bounded" | "direct_only";
  bindings: {
    rest: { method: string; path: string };
    mcp?: { tool: string };
    cli?: { path: string[] };
  };
};
A shared SproutPad operation contract projects the same operations into MCP, REST, the TypeScript SDK, and the command-line interface, which all reach the same policy, budget, approval, task, and receipt controls.
MCP, REST, SDK, and CLI are different entrances to the same governed operation contract. Open full-size diagram

OpenAPI remains the description of the HTTP interface. It cannot tell us that an agent is forbidden from approving its own request, or that a one-shot secret should never pass through code mode. We have to review and record those choices ourselves.

The next unit is a job, not another call

A canonical operation contract solves disagreement about individual actions. It also gives us a safe vocabulary for composing them.

Traditional APIs assume the caller owns the workflow. An agent calling tools one at a time often builds that workflow privately inside a model turn. The server sees "create project," then "launch service," then "attach domain." It does not see the complete job before the first effect.

Our next consequential boundary is a bounded workflow plan over the same canonical operations. The agent proposes the job and its dependencies. SproutPad validates and freezes the exact plan for inspection. Applying it is a separate direct operation, and each consequential node still passes through current identity, policy, budget, state, approval, idempotency, and receipt controls.

The plan is signed to prove its exact bytes and provenance. The signature does not authorize execution. A successful preparation cannot freeze permission, price, budget, or resource state.

This is why the operation contract matters beyond transport parity. MCP, REST, SDK, and CLI are entrances. The contract also supplies the reviewed operations that an inspectable, durable job is allowed to contain.

Code mode made the boundary easier to see

Code mode kept turning up tasks that did not benefit from a model. Suppose an agent needs projects with active services, monthly cost above $20, and less than $5 left in the budget. A short program can fetch bounded records, join them, and return the three matches. Sending every intermediate row through model context costs more and adds no useful judgment.

Buying a domain at the end of that search is different. We want the paid call to remain explicit, carry a stable idempotency key, and stop if policy asks for a person. The program may prepare information for that request. It does not acquire the authority to approve it.

This is also why code runs in the agent host rather than on SproutPad servers. The host already has an isolated runtime. Each real operation still reaches our normal authorization path. Running customer code beside the control plane would saddle us with a sandbox product before we had demonstrated a need for one.

Polling provided another useful constraint. DNS and certificate work can outlive a conversation, so SproutPad returns durable tasks. A program waiting on one of those tasks receives a view capped at 1,024 bytes: identity, status, whether the task has finished, whether somebody is needed, and when to check again. It does not receive provider output, credentials, callbacks, or an approval URL. When a person is needed, the loop ends.

The command line uses the same contract

Coding agents are already comfortable with shells. That makes the CLI a useful projection of the registry, as long as we resist making it a second control plane.

The failure modes are mundane. An interactive prompt blocks CI. Prose output gets scraped. A retry concealed inside a command can repeat a write. A success message without a provider reference or receipt is hard to audit later.

The CLI therefore uses stable JSON, meaningful exit codes, explicit profiles, and idempotency keys for noninteractive mutations. Commands are derived from the same operation records as the MCP and REST bindings. We can still add a convenient command that combines a few reads; it will not get private provider credentials or a shortcut around policy.

Where the architecture stands

The canonical registry now publishes complete coverage for the public MCP operation surface. The public REST set also contains reviewed supplemental operations that are intentionally not MCP tools. Generated SDK, CLI, and code-mode projections remain narrower where their runtime or disclosure requirements differ; the coverage record names those boundaries instead of pretending every interface should expose everything.

The workflow-plan layer follows the same discipline. A generic plan format does not make every registered operation workflow-eligible. The server owns a smaller reviewed allowlist, and production availability depends on the matching schema, workers, and build capability being live together.

These distinctions are more useful than a tool count. They show whether the architecture is complete, which projections are deliberate subsets, and which capabilities remain behind a release gate.

MCP remains the front door because it is where many agents first meet SproutPad. The larger work is the room behind it: one operation contract, bounded composition, durable execution, current authority, and evidence that outlives the model turn.

Current public contracts begin at sproutpad.ai/llms.txt.