Importing Your API Into MCP Is Usually the Wrong Move Importing an OpenAPI document directly into a Model Context Protocol (MCP) server is usually the wrong move, according to a new analysis, because the resulting tool catalog inherits the API's size and shape, burdening AI agents with excessive tokens and resource-oriented tools instead of job-oriented ones. Anthropic measured that agents connected to thousands of tools must process hundreds of thousands of tokens before reading a request, and Cloudflare's engineering team advises MCP server designers to present greatly simplified APIs. The article argues that selective import, while reducing the count, still fails to produce the job-level tools agents need, such as a fused 'schedule_event' tool rather than separate list and create endpoints. There is a command for it now. Point a generator at an OpenAPI document, get an MCP server, connect an agent. Stainless, Speakeasy, APIMatic and Mintlify all ship it; runtime translators do the same without codegen. It takes under a minute and it demos beautifully. It is also, most of the time, the wrong artifact — and the reason is one sentence: a projection that faithfully mirrors an API inherits the API’s size and shape. The import itself is the correct instinct. Your OpenAPI document is the most accurate machine-readable description of your system that exists, and re-typing it by hand would be absurd. It is the right input . The mistake is treating it as the output . Size is the complaint everyone makes A 200-operation API becomes 200 tools, all loaded before the model reads a word of the request. Anthropic measured it: “In cases where agents are connected to thousands of tools, they’ll need to process hundreds of thousands of tokens before reading a request.” And it is only the first bill — definitions are re-sent every loop, so an oversized catalog compounds quadratically with the number of turns /blog/after-compression-shaping-and-relocation/ . Cloudflare’s engineering team put the consequence plainly: “MCP server designers are encouraged to present greatly simplified APIs as compared to the more traditional API they might expose to developers.” That is the argument against one-click import, stated by the people building the runtime. The recommendation is to simplify; the importer’s whole value proposition is that it doesn’t . Shape is the one that breaks deployments Cut the catalog to twenty tools and size is manageable. Shape is untouched, because it was never about the count. An OpenAPI document describes resources . An agent needs jobs . Anthropic states the target almost as a recipe: “Instead of implementing a list users , list events , and create event tools, consider implementing a schedule event tool which finds availability and schedules an event.” Read that as a spec for a generator and it becomes obvious why none can satisfy it. schedule event is not a transformation of three endpoints. It is a decision — that these three, in this order, with this conflict rule, are one unit of work worth exposing. That lives in your product knowledge, not in the document. “But we only import the twelve we need” Nobody defends importing all 200. The mature version is selective import : hand-pick the dozen operations the agent plausibly needs. Twelve tools, and the context bill drops by an order of magnitude. It is a real improvement, and the right thing to do with the upstream half. It also leaves the problem untouched: Selection is a subtraction operator. The tools an agent needs are not a subset of your endpoints. You cannot select your way to get customer context , because it is not in the list. It is not endpoint 7 you forgot to tick — it is three endpoints and the decision to fuse them . Subtraction gets you a shorter list of the same kind of thing; the agent needed a different kind of thing. So: - The unit is still a resource, not a job. The agent still plans the sequence, holds intermediate IDs and joins results. Fewer tools does not mean fewer turns, and turns are the expensive axis. - Payloads are untouched. You selected which endpoints to expose, not what they return. The sixty-field response is still sixty fields. - Cross-system jobs stay impossible. Selection operates inside one document; the job spans three. - The selection has no home. It lives as a CLI flag or ticked boxes. Upstream renames an operation and nothing tells you — and the valuable part, why these twelve, is discarded. Choosing which upstream operations to bring in is a decision about consumes — what you may reach. The agent-facing surface is exposes , and selection has no opinion about it. Selective import answers the upstream question well, then presents its answer as though it were the downstream one. “Then we’ll annotate the document” The next move is cleverer: if the document lacks agent metadata, add it. OpenAPI sanctions this — specification extensions, any x- field — so teams attach a better description, a readOnly hint, a friendlier name, and generate from that. For metadata this genuinely works. But an extension hangs off an operation , so it can describe an endpoint better or flag it read-only. It cannot say “these three, in this order, returning only these five fields.” There is no operation to hang that on: it is a statement about a relationship between operations, and the document has no place to put one. The OpenAPI Initiative reached that conclusion itself. Rather than extend OpenAPI to express call sequences, it published Arazzo — a separate specification for defining “sequences of calls and their dependencies… in the context of delivering a particular outcome.” The people who own the extension mechanism looked at composition and concluded it needed its own document. The composition an agent needs is not annotation-shaped. It is a second artifact — and once you are writing one, the only question is whether it also carries the credential scope, the response shaping and the exposed surface, or whether you maintain those in three more places. Two costs come free with the approach. Extensions are advisory , so tooling that does not know your prefix ignores them and the same document yields different tools per generator. And they put agent concerns inside the API’s contract , reviewed on the API’s cycle — where a spec regenerated from code annotations silently drops every hand-written block. The escape hatches Dynamic tool discovery helps the token bill, but selection still happens at runtime inside a probabilistic model, and search needs a query — so the agent must already know your vocabulary. Retrieval doesn’t remove curation, it relocates it /blog/dynamic-tool-discovery-and-the-limits-of-finding-the-right-tool/ . Code execution makes an oversized, badly shaped surface cheaper to traverse ; it does not make it a good surface. The joins still happen in model-authored code, written fresh each run. Ask which systems can this reach? and the answer is still “whatever it writes next time.” What a designed surface looks like This is where the analysts land from the architecture side too — scope the surface to the agent’s task, not the product, as argued in Applied Capabilities /blog/applied-capabilities-bff-for-context-engineering/ . An Ikanos capability /glossary/capability/ is that surface written down as one declarative file: consumes which systems, under which credentials , aggregates how calls compose into one unit of work, off the model’s critical path , exposes the agent-facing surface, as MCP, REST or Agent Skill , and binds credentials, separated from logic . aggregates is the section a generator cannot write, and it is the entire point. Because output parameters /glossary/output-parameters/ are declared per operation, the sixty-field payload becomes the five the task needs — projected at the boundary rather than compressed after arrival. Concretely, the get customer context that no importer could produce: capability: consumes: - import: crm from: ./shared/crm.yml generated by ikanos import openapi - import: billing from: ./shared/billing.yml a different document entirely aggregates: - display: "Support Desk" namespace: support flows: get-customer-context: description: "Everything an agent needs before answering a billing question" semantics: safe: true idempotent: true inputParameters: customer-id: type: string required: true steps: three upstream calls, one tool call get-customer: type: call call: crm.get-customer with: id: "{{customer-id}}" list-invoices: type: call call: billing.list-invoices with: customer: "{{customer-id}}" list-tickets: type: call call: crm.list-open-tickets with: customer: "{{customer-id}}" mappings: five fields, not sixty - target: name value: "$.get-customer.display name" - target: plan value: "$.get-customer.subscription.plan code" - target: unpaid-invoices value: "$.list-invoices.data ? @.status=='open' " - target: open-tickets value: "$.list-tickets.data.length" - target: account-owner value: "$.get-customer.owner.email" exposes: - type: mcp port: 3001 namespace: support-copilot tools: get-customer-context: description: "Fetch a customer's plan, unpaid invoices and open tickets" ref: support.get-customer-context Three endpoints across two OpenAPI documents become one tool call , returning five fields. The consumes half was imported; everything below it is the decision an importer cannot make. There is no control flow in the file, which is what makes it reviewable: a reader can enumerate every operation it can reach before anything runs. What Ikanos’s importer will not do Ikanos ships ikanos import openapi . Its own description is “Import an OpenAPI specification into an Ikanos consumes YAML file.” It writes consumes — the upstream half. It does not emit tools, it does not emit exposes , and it will not hand you an MCP server. That is not a missing feature; it is the position. Import saves you from re-typing a contract that already exists, then stops exactly where judgement begins. When the mirror is right Exploration in a sandbox, to learn which operations matter. Genuinely small APIs whose endpoints already correspond to jobs. Exhaustive coverage as the real requirement, like an internal typed SDK — a generator is right there and Ikanos is wrong. None of the three is “we have an agent in production and this was the fastest way to give it tools.” The rule The document is the input. The capability is the output. Import is where the work starts, not where it finishes — and every minute it appears to save is repaid in tokens, retries, and the audit conversation you cannot have because the surface only exists at runtime. Start here: the Ikanos documentation https://shipyard.naftiko.io/ikanos/1.0.0-beta5/ covers consumes , aggregates and exposes in full, and the playground https://shipyard.naftiko.io/playground/ runs a capability in the browser with nothing to install. Related reading: Applied Capabilities /blog/applied-capabilities-bff-for-context-engineering/ on scoping a surface per agent, Agents Undid Twenty Years of API Curation /blog/agents-undid-twenty-years-of-api-curation/ on why the design-time step disappeared, and Beyond iPaaS /blog/beyond-ipaas-why-capabilities-are-the-right-unit/ on the four questions to ask any integration layer.