Skip to main content
Doctorine
Menu

OpenAPI to MCP guide

Generate fewer MCP tools, with better semantics and safer boundaries.

OpenAPI can supply the operations and JSON shapes for an MCP server. It cannot decide which actions an agent should receive, how risky calls are approved, where credentials live, or whether the resulting tools are understandable. Those decisions are the work.

Author
Aria Shishegaran
Accountable owner
Aria Shishegaran
Review scope
MCP, OpenAPI, and security boundaries
Published
Modified
Last tested
Refresh due
Product commit
abfc80e446cf

The short answer

To turn OpenAPI into MCP, validate the contract, select a small set of agent-worthy operations, map their inputs into clear tool schemas, keep credentials outside model-visible arguments, classify side effects, normalize results, and test real tool selection as well as HTTP behavior. Release the server only when every tool can be traced to an operation and every write has a permission, retry, and recovery policy.

The OpenAPI Specification defines a language-agnostic description of HTTP APIs that documentation and code-generation tools can consume. MCP adds a client-server protocol with tools, resources, and prompts. In the official MCP architecture, tools are model-controlled actions while resources provide context managed by the application. A generator can translate syntax between them, but the owner must supply the product and security intent.

Worked mapping

One operation, one explicit agent job

Suppose the contract contains POST /v1/widgets with operation ID createWidget. The request requires a name and accepts an optional region. The API uses a bearer token and returns a widget ID plus status. The mechanical mapping is easy. The safe mapping adds the missing intent.

OpenAPI operation

operationId: createWidget
security:
  - bearerAuth: []
requestBody:
  required: true
  schema:
    required: [name]
    properties:
      name: { type: string, minLength: 1 }
      region: { type: string, enum: [eu, us] }

MCP tool contract

name: create_widget
description: >
  Create one draft widget in the chosen region.
  Use only after the user confirms the name.
  This writes data; it does not publish the widget.
inputSchema:
  required: [name]
  properties:
    name: { type: string, minLength: 1 }
    region: { type: string, enum: [eu, us] }

Notice what is absent: the bearer token. The server obtains it outside the tool arguments. The description also says that the call writes a draft and requires confirmation. OpenAPI alone does not contain enough information to infer that interaction policy reliably. Record it beside the allowlist so contract regeneration does not erase it.

Eight-stage workflow

Build the server from an allowlist, not an endpoint dump

  1. Validate the API description before translating it

    Resolve references, reject ambiguous schemas, and require stable operation identifiers. Check that request bodies, path and query parameters, response shapes, and security requirements match the API you intend to expose. Generation multiplies input mistakes: an inaccurate required field becomes an unusable tool, and an undocumented side effect becomes a safety defect.

    Required evidence: A resolved contract, validation report, and a test request for every candidate operation.

  2. Choose an agent-sized surface

    Do not turn the entire API into tools by default. Start with a coherent job such as searching records, creating a draft, or checking status. Exclude administrative, destructive, bulk, high-cost, and rarely used operations until their approval and recovery behavior is designed. Fewer well-described tools reduce selection errors and make permission review possible.

    Required evidence: An allowlist with each operation’s user job, side-effect class, permission, cost, and rollback story.

  3. Decide whether each capability is a tool or a resource

    MCP tools are model-controlled functions that can perform actions or retrieve data. Resources provide application-managed context. A stable schema, policy document, or read-only reference may work better as a resource than a callable tool. The choice affects user expectations and safety. Do not make an action look like passive context merely because both call HTTP.

    Required evidence: A primitive decision for every exposed capability, with a sentence explaining why.

  4. Translate input semantics, not only JSON shape

    Combine path, query, header, and body inputs into one understandable tool schema. Preserve required fields, enums, formats, bounds, and nullable behavior. Remove server-managed fields and credentials. Rename only when the mapping remains traceable to the operation. A short tool description should state the outcome, prerequisites, side effects, and when not to call it.

    Required evidence: A tool manifest that maps every public input back to an OpenAPI location and operation ID.

  5. Put credentials at the transport boundary

    Never expose API secrets as ordinary model arguments or include live credentials in tool descriptions, examples, logs, or generated files. A local stdio server should read credentials from its environment or a secure broker. A protected remote HTTP server needs an authorization design that follows the MCP authorization requirements and scopes tokens to the actions offered.

    Required evidence: A credential-flow diagram, least-privilege scope list, redaction tests, and an unauthenticated denial test.

  6. Make side effects visible and controllable

    Classify tools as read-only, reversible write, irreversible write, or externally consequential. Require confirmation or an application policy for risky calls. Use idempotency where the upstream API supports it, return a preview or dry run when possible, and expose the identifiers needed to inspect or reverse a change. Human approval is a product decision, not something OpenAPI can infer.

    Required evidence: A tested policy for retries, duplicates, confirmation, cancellation, and rollback for every write tool.

  7. Normalize results without hiding API evidence

    Return concise structured content that preserves the record ID, status, important fields, and actionable errors. Avoid dumping enormous responses into model context. Pagination, rate limits, partial failures, and asynchronous jobs need explicit continuation behavior. Keep a safe correlation identifier so operators can trace a failed tool call without exposing secret request data.

    Required evidence: Golden success and error results for each tool, including pagination and rate-limit cases where relevant.

  8. Test discovery, selection, execution, and release

    Schema validation alone is insufficient. Ask representative prompts, confirm the client selects the intended tool, inspect generated arguments, run against a test environment, and verify the result can support the next decision. Package the server with a version, contract digest, tool manifest, and compatibility record. Re-run the suite whenever the contract or MCP protocol target changes.

    Required evidence: A repeatable scenario suite plus a release receipt that connects source contract, generated package, and test result.

Local and remote servers have different credential boundaries

For a local stdio package, keep API credentials in the process environment or an operating-system secret store and pass only the minimum configuration to the server. Limit logs, redact headers, and test that tool results cannot echo a credential. The MCP authorization specification says stdio implementations should retrieve credentials from the environment rather than applying the HTTP authorization flow.

A remote Streamable HTTP server is a different product surface. The current MCP authorization specification defines protected-resource and authorization-server discovery for protected HTTP transports. You still need scopes, consent, token storage, rotation, revocation, tenant isolation, and audit evidence. Do not place a local server behind a public URL and call that remote authorization.

Tool invocation can perform arbitrary actions, so the official MCP tools specification recommends a human-in-the-loop ability to deny invocations. The exact confirmation experience belongs to the host application, but your server should supply accurate names, side-effect descriptions, permission failures, and stable identifiers so that experience can work.

Test the boundary with faults, not only successful calls

Use a test API with two similar read operations and one reversible write. Snapshot the server’s advertised tool names, descriptions, and input schemas, then run four prompt classes: a clear match, a clear non-match, an ambiguous request, and a request the active scope must deny. Capture the selected tool, generated arguments, confirmation decision, upstream request, and normalized result. Fail the run if the client invents an optional value, selects a tool for the non-match, bypasses confirmation, or exposes a credential in the schema, arguments, result, or logs.

Repeat execution while the test API returns 401, 403, 429, a malformed success body, and a second page without a terminal cursor. Also simulate a timeout after the upstream service accepts the write. Authorization failures must fail closed. Rate-limit responses should preserve bounded retry guidance. Malformed data should become a compact error rather than raw output. Pagination needs a maximum-page or repeated-cursor stop. The timed-out write must resolve through an idempotency key or status lookup before any retry, or the tool is not safe to release.

Finally, revoke or rotate the test credential and rerun discovery plus one call. A server that continues on a cached token has a transport defect. Save the redacted transcript with the tool manifest and contract digest so the same matrix can run after either the OpenAPI input or MCP protocol target changes.

Six failure modes that generation does not solve

Every endpoint becomes a tool
Symptom: The client sees hundreds of overlapping names and selects the wrong operation.
Fix: Publish a job-focused allowlist. Split broad APIs into bounded servers or capability sets.
Names are technically correct but semantically empty
Symptom: Tools such as postV1Items do not tell the model when to call them.
Fix: Use stable action-object names and descriptions that name outcome, constraints, and side effects.
Authentication is modeled as a parameter
Symptom: The model is asked to supply apiKey or Authorization on every call.
Fix: Remove secrets from public schemas and inject credentials at the server or transport boundary.
Write retries create duplicate work
Symptom: A timeout causes a second order, ticket, or deployment.
Fix: Use upstream idempotency where available and return a stable operation or job identifier.
The server returns an HTTP transcript
Symptom: Useful fields are buried in headers and a large body, consuming context and confusing the next step.
Fix: Return a compact structured result, retain a safe trace ID, and expose raw detail only when requested.
Remote transport is shipped without an authorization story
Symptom: A server that worked on one laptop becomes publicly reachable with shared credentials.
Fix: Keep it local until protected-resource discovery, OAuth flow, scopes, token handling, and denial paths are tested.

OpenAPI-to-MCP release checklist

Save the evidence beside the generated package. A check without a repeatable input or result is an opinion, not a release gate.

Contract
All references resolve; operation IDs are stable; inputs, responses, and security match test behavior.
Selection
Each tool serves a named job; destructive, high-cost, and administrative operations are excluded or gated.
Schema
Required fields, enums, formats, bounds, and nullable states survive translation; secrets do not.
Semantics
Names and descriptions distinguish similar tools and state prerequisites, side effects, and exclusions.
Authorization
Credentials stay outside model arguments; scopes are least-privilege; unauthenticated and forbidden calls fail closed.
Reliability
Retries, idempotency, pagination, rate limits, asynchronous jobs, and partial errors have tested behavior.
Agent behavior
Representative prompts select the expected tool and produce valid arguments before execution.
Provenance
The package records its contract digest, generator version, tool manifest, test result, and release version.

What Doctorine ships—and holds—today

Public, entitled Doctorine portals can publish llms.txt and llms-full.txt. Those shipped text files expose documentation context rather than typed API capabilities. An internal deterministic MCP emitter exists in the codebase, but it is not exposed through a customer CLI command or the live cloud factory. The agent-ready API product boundary shows how they relate to the human portal and generated SDKs.

Customer-accessible local generation and hosted portal MCP are both held. The portal route still lacks its complete production token, OAuth 2.1, and dynamic client onboarding path. Doctorine is also OpenAPI-first: it does not translate AsyncAPI, gRPC, or OpenRPC into equivalent MCP packages. For the source-control and review system around a future executable output, use the docs-as-code release workflow.

If you are still deciding what the human documentation must explain before any agent action is safe, apply the API documentation evaluation rubric to authentication, errors, pagination, and lifecycle behavior first.

Inspect the agent surface designed from your API.

Import one OpenAPI contract and review its human documentation, SDK, llms.txt, and executable-tool design boundary together.

Import an OpenAPI spec