OpenAPI to AI Agent Tools: Skip the Hand-Written Wrappers

Stop hand-writing tool schemas for every endpoint. Learn how to generate AI agent tools from an OpenAPI spec, what the generator must fix, and how to keep 200 endpoints from wrecking tool selection.

Ashley Innocent

Ashley Innocent

26 August 2026

OpenAPI to AI Agent Tools: Skip the Hand-Written Wrappers

Apidog for Enterprise

On-Premises Deploy

SSO & RBAC

SOC 2 Compliant

Explore Apidog Enterprise

Most agent codebases contain a file nobody enjoys maintaining. It holds forty tool definitions, each one a hand-written JSON schema describing an endpoint that already has a schema somewhere else. The API team ships a new required field, the spec updates, the docs update, and the agent keeps sending the old payload until someone notices the 400s.

You already have a machine-readable description of every endpoint. It is the OpenAPI document. The job is to turn that into tool definitions the model can call, and to keep the two in sync automatically instead of by memory.

This guide covers how OpenAPI operations map to tool schemas, what the generator has to fix along the way, how to trim a 200-endpoint spec down to something a model can reason about, and how to test that the generated tools behave. If you are earlier in the stack, our post on whether you still need an API tool when agents write the code sets the wider context.

Apidog matters here because the spec has to be correct before anything generated from it can be. A tool definition inherits every gap in the document it came from.

The cost of hand-written tool definitions

Hand-writing tools feels fine at five endpoints. It stops being fine somewhere around twenty, for three reasons.

Definitions drift. The spec is generated from code or maintained by the API team. The tool file is maintained by whoever built the agent. Nothing connects them, so they diverge quietly, and the first symptom is an agent that “suddenly” stopped working.

Descriptions get thin. When a person writes forty schemas by hand, the last twenty get one-line descriptions. Models pick tools by reading those descriptions, so thin text directly degrades tool selection. Our post on tool schema design for agents goes deeper into why the wording carries so much weight.

Errors are invisible until runtime. A hand-written schema that says a field is a string when the API wants an integer produces a 422 the first time the agent tries it, in production, on a real task.

Generating from the spec fixes all three at once. There is one source of truth, descriptions come from the same text your docs use, and types come from the same schema the server validates against.

How an OpenAPI operation becomes a tool

The mapping is more direct than it looks. Take a single operation:

paths:
  /orders/{orderId}/refund:
    post:
      operationId: refundOrder
      summary: Refund an order
      description: >
        Issues a full or partial refund against a completed order.
        Refunds are irreversible. Partial refunds require an amount
        no greater than the remaining refundable balance.
      parameters:
        - name: orderId
          in: path
          required: true
          schema: { type: string }
          description: The order to refund.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                amount:
                  type: integer
                  description: Amount in cents. Omit for a full refund.
                reason:
                  type: string
                  enum: [duplicate, fraudulent, requested_by_customer]

The tool definition that comes out of it:

{
  "name": "refundOrder",
  "description": "Issues a full or partial refund against a completed order. Refunds are irreversible. Partial refunds require an amount no greater than the remaining refundable balance.",
  "input_schema": {
    "type": "object",
    "required": ["orderId", "reason"],
    "properties": {
      "orderId": { "type": "string", "description": "The order to refund." },
      "amount": { "type": "integer", "description": "Amount in cents. Omit for a full refund." },
      "reason": { "type": "string", "enum": ["duplicate", "fraudulent", "requested_by_customer"] }
    }
  }
}

Four rules do most of the work:

  1. operationId becomes the tool name. If an operation has no operationId, generate a stable one from method plus path, and then go add it to the spec.
  2. Path, query, and body parameters flatten into one properties object. The model does not care where a value rides on the wire. Your executor does, so keep a side table that records which parameter goes where.
  3. summary plus description becomes the tool description. Both, joined. The summary alone is usually too terse to guide selection.
  4. Required arrays merge. A required path parameter and a required body field both land in the same required list.

The executor is the other half, and it is small:

def execute(tool_name, args, spec_index, http):
    op = spec_index[tool_name]          # method, path template, param locations
    path = op.path
    query, body = {}, {}

    for name, value in args.items():
        location = op.locations[name]   # "path" | "query" | "header" | "body"
        if location == "path":
            path = path.replace("{" + name + "}", str(value))
        elif location == "query":
            query[name] = value
        elif location == "body":
            body[name] = value

    return http.request(op.method, path, params=query, json=body or None)

That is the entire bridge. Everything else is cleanup on the way through.

What the generator has to fix

A naive dump of the spec into tool schemas produces tools that models handle badly. Five adjustments matter.

Resolve $ref pointers. Most tool-calling APIs accept a subset of JSON Schema and will not follow references into a components section. Inline them. Watch for recursive schemas, which inlining will expand forever; cut the recursion at a fixed depth and describe the deeper structure in prose.

Drop unsupported keywords. oneOf, allOf, discriminator, and nullable are common in specs and poorly supported by tool schemas. Collapse allOf by merging properties. For oneOf, either pick the dominant variant or split the operation into two tools, one per shape. That second option usually produces better tool selection anyway.

Flatten deep nesting. A body three levels deep is hard for a model to fill correctly. If your create-order payload nests customer.address.postal_code, consider a flatter tool surface and reassemble the nested shape in the executor.

Prune response schemas. Tool definitions describe inputs. The full response schema does not belong in the definition, and including it wastes context. What the response looks like matters when the result comes back, and that is a separate problem covered in our post on keeping API responses inside the agent’s context window.

Carry the safety flags. Write operations should be marked so your executor can route them through an approval gate. If your spec uses an extension such as x-agent-requires-approval, read it and honor it. Pair this with the patterns in our AI agent guardrails guide.

Do not give the model all 200 endpoints

The biggest practical problem is not conversion. It is volume. A mature API has hundreds of operations, and pasting all of them into the tool list produces two failures at once: the context fills with schemas before the task starts, and selection accuracy drops because the model is choosing among near-identical options.

Three ways to cut it down, roughly in order of how well they work.

Filter by tag. OpenAPI operations carry tags, and tags usually map to product areas. An agent that handles refunds needs the orders and payments tags, not admin or analytics. This is a one-line filter and it typically removes most of the surface.

Curate an allowlist. Write down the operations this agent is allowed to call, by operationId, and generate only those. This doubles as a security control, since an agent that has no tool for an endpoint cannot call it by accident. Our post on stopping agents from nuking your API argues for exactly this kind of narrow surface.

Retrieve tools on demand. For very large APIs, index the operations and select a handful per turn based on the task. This adds a retrieval step and its own failure modes, so reach for it only after filtering and curation stop being enough.

There is also the protocol route. The Model Context Protocol standardizes how a server exposes tools to a client, and an MCP server backed by your OpenAPI document gives you one integration point instead of one per framework. Our explainer on what MCP is covers the model, and building an MCP server with Apidog covers the build.

The spec has to be right first

Generation moves the quality problem upstream. A vague description in your OpenAPI document becomes a vague tool description, and the model picks the wrong endpoint. An optional field that the server actually requires becomes a tool the agent calls incorrectly on the first try.

So audit the spec through an agent’s eyes before you generate anything:

This is ordinary spec hygiene, and it pays off twice, because the same text drives your published docs. In Apidog the spec, the docs, the mock server, and the tests come from one project, so tightening a description improves all of them at once. Our guide on managing API versioning in Apidog covers the other half of keeping generated tools honest over time.

Share the tool set, do not copy it

A generated tool set is configuration, and configuration that lives in one developer’s checkout drifts the same way hand-written schemas do. The filter list, the allowlist, and the pinned spec version should be shared artifacts, versioned next to the spec they came from.

Some platforms make this the default unit. In Sharkly, an Agent is a saved working configuration rather than a one-off prompt: its instructions, Runtime, Skills, repositories, and run settings travel with it and can be shared across a Space, so a working tool setup becomes something a team reuses instead of something each person rebuilds. The runtime underneath is still Claude Code, Codex, or whatever you already run. What changes is that the configuration around it stops being local.

Testing generated tools

Generated tools fail in ways hand-written ones do not, so test the generation as well as the calls.

Start with a schema round-trip check. For every generated tool, build a valid example from the schema and send it. Anything that returns 400 or 422 means the tool schema and the server disagree, and the spec is the thing to fix.

Then test selection. Write a small set of task prompts with a known correct tool, run them, and record which tool the model picked. This is a cheap regression suite that catches the day someone renames an operation or shortens a description. Because the output is non-deterministic, assert on the tool name rather than on exact arguments, along the lines of our guide to testing non-deterministic agents.

Finally, run the agent against mocks before anything live. A mock server generated from the same spec gives you realistic responses without side effects, and it lets you inject the 500s and timeouts that your retry logic is supposed to handle.

Where this leaves you

The spec is the contract, and the tool list should be a projection of it, not a parallel copy maintained by hand. Generate the tools, filter them hard, keep the descriptions honest, and test both the shapes and the selection.

Start by exporting your OpenAPI document and counting the operations that have no description. That number is how much work stands between you and agent tools you can trust. Download Apidog if you want the spec, mocks, and tests in one place while you fix it.

Frequently asked questions

Can I generate tools from a Swagger 2.0 document? Yes, but convert it to OpenAPI 3.x first. The 2.0 body model differs enough that generators handle it inconsistently, and 3.x is what current tooling targets. The OpenAPI Specification repository documents the differences.

How many tools can a model handle at once? Accuracy starts degrading well before the technical limit, and the practical ceiling is usually a few dozen. Treat any list past that as a signal to filter by tag or curate an allowlist rather than as a limit to test.

Should tool names match operationId exactly? Yes, when the operationId is readable. It gives you a direct lookup from tool call back to spec operation, which makes tracing and debugging much easier. Rename in the spec if the name is bad, not in the generator.

What about GraphQL APIs? The same idea applies with a different source: introspect the schema and generate a tool per query or mutation. The volume problem is worse because a GraphQL schema exposes more surface, so filtering matters even more.

Do I still need to write any tools by hand? A few. Composite tools that chain several calls into one action, and tools that wrap something other than HTTP, still get written manually. The point is that the routine one-endpoint wrappers stop being handwork.

How do I stop the agent from calling write endpoints during testing? Generate a read-only tool set for test runs by filtering on HTTP method, and point the agent at a mock for anything that writes. Our post on why agents should hit mocks, not production covers the setup.

Explore more

AI Agent Tool Call Tracing: What to Log on Every Request

AI Agent Tool Call Tracing: What to Log on Every Request

"Called tool, got 200" explains nothing. Learn what to record on every agent tool call, what to redact, and how to turn failed traces into regression tests.

26 August 2026

AI Agent Idempotency: Stop Retries From Double-Charging

AI Agent Idempotency: Stop Retries From Double-Charging

Agent retries create duplicate charges and duplicate orders. Learn how idempotency keys work, how to generate them per task step, and how to test that the second call changes nothing.

26 August 2026

AI Agent Context Window: Trimming Bloated API Responses

AI Agent Context Window: Trimming Bloated API Responses

Fat JSON responses eat the agent's context window and its budget. Learn field selection, hard list caps, tool-layer projection, and server-side summaries that keep tool results small.

26 August 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs

OpenAPI to AI Agent Tools: Skip the Hand-Written Wrappers