Every team evaluating Model Context Protocol (MCP) servers reaches the same question: do we hand-maintain a second description of our API for agents, or do we generate it from the contract we already have? If you keep an OpenAPI document, the second answer is the sane one. An OpenAPI spec is almost literally a tool catalog already — each operation has a name, inputs, outputs, and a transport. Converting it to MCP is a mapping problem, not a modeling problem.

This walkthrough covers what the mapping actually is, where generic converters fail on real specs, and how to verify the result with a real agent client.

What an operation becomes

The mapping is mechanical:

OpenAPIMCP
operationIdTool name (normalized)
summary / descriptionTool description
path + query + header paramsinputSchema properties
requestBody.content["application/json"].schemainputSchema properties
responsesTool result content (JSON or text)
servers[0].urlThe upstream the server calls
securitySchemesHeaders injected at runtime

The generated MCP server is a thin adapter. When an agent calls the list_projects tool, the server validates the arguments against the schema, performs the HTTP request, and returns the response body. It does not invent endpoints, and it does not summarize anything — the contract is the behavior.

That last point is why this works better than asking an LLM to "learn our API" from docs pasted into a prompt. The model gets a structured tool list with machine-readable input schemas, and the server enforces types before a request leaves the process.

Three ways to run it

Generated code. A code generator emits a standalone MCP server in TypeScript or Python. You own the output, which means you can customize middleware, but it drifts the moment the spec changes and you now have a service to deploy.

A local proxy over the spec. A runtime reads the OpenAPI document and exposes tools directly, no codegen step. This is the model Powerduck uses: open the spec in the desktop workspace and serve it as a local MCP endpoint, or launch it headlessly with npx against the document. The spec file stays the single source, so editing a schema changes the tools on the next launch.

Hosted endpoint. The same spec published to a URL gives you a managed MCP endpoint with an access token, versioning, and access control. This is the right shape for partners or internal teams who should never see your source file.

Step 1: clean the operation identifiers

Tools are only as discoverable as their names. Before serving anything, make every operationId unique, stable, and verb-first: list_projects, get_project, create_project, not getAll, fetchOne, and projectsGet. Agents choose tools by name and description; projectsGet tells them nothing.

If your spec has no operationIds, generate them from the method and path (get /projects/{id} becomes get_project) and keep that mapping stable across releases. Renaming a tool is a breaking change for every saved agent prompt that references it.

Step 2: decide what is exposed

A 300-operation internal API should not become 300 tools. Agents handle a few dozen tools well; beyond that, discovery degrades and the wrong tool gets called. Options:

  • Serve a public subset by tagging operations (x-mcp-expose: true).
  • Split by audience: one server for billing operations, another for read-only catalog access.
  • Exclude dangerous verbs from the hosted endpoint and keep them on a local server only.

The spec already carries tags; most teams need a filter, not a rewrite.

Step 3: handle auth without leaking it

The MCP server never embeds credentials in the spec. Two patterns work:

{
  "mcpServers": {
    "powerduck-cloud": {
      "command": "npx",
      "args": ["@powerduck/openapi-to-mcp-server", "--spec", "./openapi.yaml"],
      "env": {
        "PD_API_TOKEN": "paste-locally-never-commit"
      }
    }
  }
}

For a hosted endpoint, the client holds a per-user token issued by the platform; the spec's securitySchemes stay a declaration of shape (Bearer, API key in header, basic auth), while the actual secret is injected per request. Treat read and write scopes as separate tokens from day one — an agent that can browse should not be able to delete.

Step 4: streaming and non-REST operations

This is where naive converters stop. Real APIs include SSE streams, and a conversion that drops them silently gives agents a broken picture of the product. In Powerduck's OpenAPI 3.2 documents, an SSE endpoint carries text/event-stream plus an x-protocol extension describing the event names and item schema. When that document is served as MCP, the stream operation opens the connection, collects the events the agent asked for, and returns them as a structured result instead of hanging forever.

WebSocket and gRPC operations are documented the same way in the workspace; only HTTP and SSE map to plain MCP tools today, so label the rest rather than pretending.

Step 5: verify against a real client

Serving the endpoint is not the finish line. Verify in an actual MCP client:

  1. Connect and confirm the tool count matches the exposed operation set.
  2. Call a GET tool with no arguments and with a deliberately wrong type — the second must return a schema validation error, not an upstream 500.
  3. Call a POST tool and confirm the body reaches the server with the exact content type declared in the spec.
  4. Check that an operation requiring a token fails cleanly when it is missing.
  5. Exercise one SSE tool end to end.

A common failure is double-encoding: the converter wraps the body in JSON, and the generated client does it again, and the upstream receives a string. The POST test in step 3 catches it immediately.

What usually breaks

  • Servers per environment. The spec lists staging and production; the converter picks the first one and agents quietly hit prod. Pin the server explicitly when serving.
  • Enums in descriptions. A status field with twelve enum values that only exist in prose gets called with invented values. Enums belong in the schema.
  • Free-form objects. additionalProperties: true operations become useless tools — the agent has no idea what to send. Tighten these before serving.
  • Pagination assumptions. If every list uses cursor pagination, say so in the operation description once; agents will otherwise ask the user.

Start with one document

You do not need an MCP initiative to get value. Take one spec — the one your team pastes into chat most often — serve it locally, and point an agent at it. The workflow after that is straightforward: the spec drives docs, mocks, tests, and the MCP endpoint from the same file, so there is nothing extra to maintain.

A hosted version with access control and versioning exists for the moment you want partners on it; the in-browser demo opens a sample document and shows the serve action without installing anything.

What to read next: Your API already describes the tools your agent needs goes deeper on tool discovery, and publishing API docs and an MCP endpoint from one spec covers the hosted path with custom domains.