← All articles Platform

The deploy step was where the human stood

OBTO Team · Insights from the Glass Box

What reviews a tool that did not exist ten seconds ago?

An agent working through a task decides it needs a capability nobody built. On most platforms the next few hours look like this: someone writes a handler, opens a pull request, a second person reads it, it merges, CI builds, the MCP server redeploys, and the tool exists. The agent waits. More often the run simply ends and a person picks the thread back up tomorrow.

On OBTO an MCP tool is a database record. Creating one is a single call, and there is no build, no redeploy and no server restart between that call and the tool appearing in the tenant's catalog.

That is a real speed difference, and it is the least interesting thing about it. The deploy step was carrying a passenger.

What the deploy step was actually for

Look at a normal tool-shipping pipeline and the deploy is the last mechanical stage in a chain that began with a human reading code. The review is not really attached to the deploy. It is attached to the fact that shipping was slow enough, and public enough, that somebody had to look.

Take the chain out and you get the speed. You also quietly delete the one guaranteed moment when a person read what the new tool was allowed to do. Nobody decides to remove that review. It leaves with the pipeline it was riding on.

So the question a platform like ours has to answer is narrow and slightly uncomfortable. If a tool can exist one second after an agent decides it wants one, what does that tool have to say for itself first?

The declaration moved into the call

Our creation call refuses a tool that has not described its own blast radius. Two arguments are required, and neither one is optional documentation.

annotations is the machine-readable part. readOnlyHint, destructiveHint and openWorldHint are all required booleans. Does this tool change state? Can it destroy something? Does its effect leave the platform?

annotationJustifications is the part that is harder to fake. For each of those three claims the caller has to write a reviewer-facing sentence explaining why the value is accurate. That sentence is stored on the record.

Here is a real one, from a tool live in our own catalog. bridge_post appends a message to a thread so that people can see what an agent is doing. It declares itself non-destructive, and it has to say why:

// annotationJustifications.destructiveHint
"It is append-only. It adds a new message and never edits,
overwrites, deletes, or deactivates existing threads or
messages, so no prior data can be lost."

A person reads that in about four seconds and can tell whether the handler underneath is behaving. That is the job the pull request used to do, relocated into the API that creates the thing.

What creating one looks like

The whole surface is one call: a Zod schema for the arguments, a handler written as a JavaScript function expression, and the declarations.

obto_create_mcp_tool({
  name: "flag_overdue_invoice",
  zodSchemaString: 'z.object({ invoiceId: z.string() })',
  handlerFunction: 'async (args) => { ... }',
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
  annotationJustifications: { /* one sentence per claim, required */ }
})

There is no repository for this tool, no image to rebuild and no process to restart. It is a row in the tenant's catalog, scoped to that tenant, and the next session that connects will see it.

The boundary we did not remove

One constraint survives, and it comes from MCP rather than from us. A client caches the tool catalog when it connects, so an agent that creates a tool cannot then call it. Its own catalog was fixed at connect time. The new tool becomes callable from the next session.

We did not design that as a safety feature and it would be dishonest to sell it as one. It is a protocol detail. Its effect, though, is a quarantine: every tool an agent mints sits in the catalog, readable, across at least one session boundary before anything can invoke it. Whatever inspection you want to run in that window, automated or human, has somewhere to stand.

What this does not solve

A justification is a claim, and an agent can write a confident, well-formed sentence that is wrong. Nothing about a required string field makes the handler beneath it honest. A tool can declare destructiveHint: false and then drop a collection, and the record will hold a fluent explanation of why that could never happen.

A pull request, where a person reads the actual code before it can run, is stronger than this for anything high-stakes. That is worth saying plainly. Managed MCP platforms that keep a build step keep that review along with it, and for a small set of carefully written tools they are right to.

What the structured declaration buys is narrower. The claim is stored as data on every tool in the tenant, so "show me every tool here that says it can destroy something" is a query rather than an afternoon of reading source. So is "show me every tool whose effect leaves the platform." You are trading a guaranteed read of a few tools for a queryable index over all of them.

Which one you want depends on how many tools you have and how fast they arrive. If the answer is a handful, written by people, keep the pull request. Catalogs stop being readable at a size that shows up sooner than most teams expect.

What we would tell you to steal

The generalizable rule has nothing to do with MCP. When you remove a human step because it is slow, name the second job it was doing before you take it out. Shipping was the visible job; the review was riding along with it.

Then put that second job somewhere structural. Make it an argument the creation API refuses to run without, so a caller in a hurry cannot skip it. A doc comment, a wiki page, or a line in a prompt asking the agent to be careful are all things a system will happily proceed without. A required field is not.

We use the same move elsewhere. A patch carries an anchor and is refused when that anchor no longer matches what is live. An upload is refused when its bytes do not hash to what was promised. Guardrails that live in the prompt are the version of this that does not hold.

If you want to try it, building a tool is the shortest way in, and the Builder tier is free.

Frequently asked questions

Can an AI agent create its own MCP tools at runtime?

It can on a platform where the tool definition is data rather than deployed code. On OBTO a tool is a record in the tenant's catalog, so creation is a single API call with no build step and no server restart. The tool becomes callable from the next MCP session, because clients cache the tool catalog when they connect.

What stops an agent from creating a tool that does something dangerous?

The creation call requires the tool to declare its own behaviour before it can exist: whether it is read-only, whether it can destroy data, and whether its effect reaches outside the platform. Each of those three claims needs a written justification stored on the record. That makes the claim auditable and queryable. It does not verify the handler underneath, which is why a high-stakes tool still deserves a person reading the code.

Why can an agent not use the tool it just created?

MCP clients cache the tool catalog at connect time, so the list of available tools is fixed when a session starts. A tool created mid-session is absent from that cached list. Smoke-test it from a fresh session.

Do MCP tool annotations enforce anything?

Annotations are declarations rather than sandbox constraints. They describe intended behaviour so that clients and reviewers can reason about a tool before calling it. Enforcement comes from elsewhere: tenancy scoping on data access, collection allowlists, and refusals built into the platform's own write paths.

How is this different from a managed MCP hosting platform?

Managed platforms deploy MCP servers you have written, which keeps the build and the review that travels with it. Storing the tool as a record removes that step, which is faster and moves the burden onto the declaration required at creation time. Which trade is right depends on how many tools you have and how quickly they need to appear.

More from the OBTO blog