Generating MCP tools from the OpenAPI schema
An AI agent reaches Giant Context through an MCP server, the standard interface that hands a model a set of tools and calls them on its behalf. The usual way to have one is to build one. Somebody writes a tool per operation, with a name, an input schema, a description and the code that calls the real API behind it.
I wrote no tools. The MCP server builds them from the API's own OpenAPI schema.
The line from route to tool
A route declares its contract inline and the API assembles those declarations into an OpenAPI schema at boot. That schema already existed to feed the SDK and the console client. The tool surface is one more reader of it, and it reads a filtered copy served at its own endpoint:
From a route to a tool a model can call
route schema operationId, TypeBox params and body, x-tool-* → the API assembles the OpenAPI schema at boot → GET /docs/mcp/json only tool-enabled, mcp-sourced operations → the MCP server fetches that → filter, normalize, register one tool per operation → a tool call is proxied back to the API over HTTPThat filter is the opt-in. An operation that has not marked itself never appears in /docs/mcp/json.
What the route declares for the tool
One operation, as the route declares it
// updateBlock in the OpenAPI schema, request and response schemas omitted"operationId": "updateBlock","summary": "Update a block by id","tags": ["Content"],"x-tool-enabled": true,"x-tool-sources": ["console", "mcp"],"x-tool-instructions": "Send ONLY the fields you are changing.","x-tool-read-only-hint": false,"x-tool-destructive-hint": true,"x-permissions": ["resourcemanager:projects:get"],"x-sdk-enabled": truex-tool-enabled decides whether the operation becomes a tool. x-tool-sources decides which surface gets it, the assistant inside the console or the external MCP server, from the same line. x-tool-instructions is prose written for a model rather than for a person browsing documentation. The two hints are MCP's own annotation vocabulary for whether a call changes state and whether the change can overwrite. The permission and the SDK flag were already there for the other readers of the same schema.
x-tool-enabled
Whether it becomes a tool at all
x-tool-sources
Which surface gets it, the console assistant or the MCP server
x-tool-instructions
The description the model reads before choosing it
The two hints
Whether the call changes state, and whether the change can overwrite
What arrives at the model
The server takes the tool name from operationId, the description from x-tool-instructions with summary as a fallback, and the input schema by flattening path parameters, query parameters and the JSON body into one object. The permission the route requires is appended to the description. What the model sees is this:
The same operation, as the model receives it
updateBlock
Update a block by id. Send ONLY the fields you are changing. Omittedfields keep their current value.
Requires: resourcemanager:projects:get.
readOnlyHint: falsedestructiveHint: trueA model choosing between tools reads that and nothing else. updateBlock replaces a content locale in full. Send four paragraphs to a block that held five and the fifth is gone, and the sentence warning about that is on the route.
The check that runs before the generators
A contract check runs ahead of the generators and fails the build on a schema that breaks its rules, so a missing or wrong declaration stops the build instead of reaching the tool list.
One rule does most of the work here. Every tool that is not a GET must declare x-tool-destructive-hint: true. The rule asks nobody to decide. Any write is flagged whether or not it removes anything, and a route that stays silent does not build.
An API description is already a closed loop
A program calling an API on someone's behalf needs a short list of things. The path and the method. The parameters, and the shape of the request and the response. How the caller authenticates. The OpenAPI schema states every one of them.
So nothing is left over for me to supply. The MCP server resolves the endpoint from the path, the arguments from the parameter list, and the validation from the request schema.
The MCP server knows nothing about this platform
Read its source and Giant Context does not appear in it. It fetches an OpenAPI schema, filters it, turns each operation into a tool, and forwards calls back over HTTP. Every piece of product knowledge came out of the schema. Point the same server at a different API's schema and it serves that API's tools.
There is no file in the repository that lists the tools. The MCP process assembles them from /docs/mcp/json and holds them in memory.
What this is worth
Building an MCP server for a product is a second implementation of its API surface, written by hand and maintained beside the first. It lags the API it wraps, and every operation it has not caught up to is a capability the customer's agent cannot use.
I have never updated the MCP service. A route says on itself that it wants to be a tool, and the tool arrives complete with its own name, schema, instructions and warnings. The API stays the single source of truth for the console, the SDK and the tool surface, and for a mobile client when there is one.
Building something like this
I'm Jesse. I build platforms end to end, and I'm open to work. If this is the kind of engineering you need, get in touch.