You already have working endpoints: order lookup, inventory, shipment
tracking. Now the business wants an AI assistant — "give it an order number and
it summarizes the order and where it is in transit."
The API is done. The to-do list, somehow, is not:
- define a tool name for every operation;
- hand-write the input schema and describe each field;
- translate those inputs into an HTTP request;
- shape the response back into something the model can use;
- and then maintain this second description every time a field changes.
One interface, two sources of truth, guaranteed to drift.
If your API is already described in OpenAPI, every one of those definitions
already exists. You should not be re-declaring it for agents.
The duplication nobody budgets for
Here is the wrapper people end up writing by hand for a single endpoint:
// hand-maintained, and now it must track the OpenAPI doc forever
server.tool("get_order", { orderId: z.string() }, async ({ orderId }) => {
const res = await fetch(`${BASE}/orders/${encodeURIComponent(orderId)}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
});
Multiply that by forty endpoints, add the inventory and logistics services, and
then keep the parameter names, nullable fields, enums and error codes in sync
with the HTTP API by hand. The moment the backend renames warehouseId or adds
a status, the agent tool lies.
This is pure transcription. The operation ID, the path parameters, the request
and response schemas, the auth scheme — OpenAPI already carries all of it.
Generate the tools from the contract
OpenAPI-to-MCP generation turns each operation into a discoverable tool and
reuses the contract you already maintain:
- the path and method become the tool's transport;
- parameters and the request body become the tool's input schema;
- the response schema tells the model what it will get back;
- the operation description and
securityrequirements carry over.
Conceptually, GET /orders/{orderId} becomes a tool the agent can discover:
{
"name": "getOrder",
"description": "Fetch one order by id, including status and line items.",
"inputSchema": {
"type": "object",
"properties": { "orderId": { "type": "string" } },
"required": ["orderId"]
}
}
The generated server calls your existing service. It doesn't reimplement
order logic, hold a copy of the data, or stand up a new backend. Your API stays
the system of record; MCP is just another entry point to it. Change a field in
OpenAPI, regenerate, and the human-facing docs and the agent-facing tools move
together because they are built from the same file.
Start read-only. Treat writes as a separate decision
Resist the urge to expose everything on day one. For the customer-support
assistant, open the read operations first — order lookup and shipment
tracking — and let the agent answer real questions:
"Has this order shipped?" → call
getOrder, thengetShipment, answer from
the actual responses.
This sequencing is practical, not cautious theater. It lets you verify the
things that actually break first:
- Are the tool descriptions clear enough for the model to pick the right one?
- Do parameters get passed through correctly (types, enums, required fields)?
- Does upstream auth work end to end?
- Are responses shaped so the model can cite real values instead of guessing?
Writes — cancel order, issue refund, adjust inventory — are a different risk
class. Gate them behind explicit user confirmation, least-privilege scopes, and
audit logging, and expose them only after the read path is proven.
Discoverable is not the same as authorized
This is the single most important security note in this whole setup: a tool
being discoverable does not mean the caller is allowed to execute it. MCP
solves the connection problem; it does not replace your authorization model.
- The generated server forwards credentials; your API still makes the allow/deny decision.
- Scope the exposed operations per audience. A partner integration does not need your internal bulk-adjustment endpoints.
- Never let "the agent asked nicely" bypass a permission check that the HTTP API would otherwise enforce.
A useful mental model: the OpenAPI-to-MCP layer is a typed, discoverable proxy
in front of endpoints that keep enforcing every rule they enforce today.
Local for development, hosted for partners
The same generated server fits two deployment shapes:
| Need | Shape | When you use it |
|---|---|---|
| Local agent / dev machine | stdio or local HTTP MCP server | You're wiring an agent to services on your own machine |
| Remote agents and partners | Hosted MCP endpoint over HTTP | External agents or teammates need stable access without your laptop |
| Humans, in parallel | Published API docs from the same spec | A developer wants to read, not call through an agent |
Hosting can also carry the documentation, so a partner gets a readable spec and
a callable MCP endpoint from one published version. Publish a versioned
snapshot rather than your working draft: internal, half-built operations
shouldn't become external promises the moment you save a file.
Don't confuse the two MCPs
Teams hit confusion here because "MCP for APIs" shows up in two distinct
moments, and they answer different questions:
| Development-time MCP | Runtime MCP (this article) | |
|---|---|---|
| Who calls it | Your AI coding assistant | An end-user-facing AI agent |
| What it does | Reads the contract, mocks and tests while you build | Calls the running API to get work done |
| Answers | "How should this endpoint be implemented and checked?" | "Call this endpoint and return the result" |
| Feeds on | The evolving local spec | A published, authenticated service |
You can use both against the same OpenAPI file; they just sit on opposite sides
of "the API exists."
Ship the read path this week
You don't need an agent framework rewrite to start:
- Take one reasonably complete OpenAPI spec (or generate one from existing code if the API predates its docs).
- Generate the MCP tools and expose two or three read-only operations.
- Connect an MCP-compatible client and run a real query end to end.
- Check auth and response shape, then widen the surface deliberately.
- Publish a versioned endpoint (plus matching docs) when you're ready for partners.
The consumers of an API used to be front ends, mobile apps and other services.
Agents are now on that list — and they need the same contract, not a parallel,
hand-written shadow of it.
You can generate an MCP server from an OpenAPI spec and run it locally or host
it alongside your docs in the free web app at
powerduck.com/app.
If you've already hand-rolled agent wrappers around a REST API: how did you keep
the tool schemas from drifting from the real endpoints? I'd love to hear the
approach (and the war stories) in the comments.
Top comments (0)