I have been thinking a lot about OpenAPI lately. Part of that came from working on APIs that are increasingly consumed through Claude, Cursor, Codex, Copilot, and other coding agents.
A developer gives the agent a docs URL or OpenAPI file, asks it to build an integration, and whatever the agent understands from that material can end up in production code.
That changes the standard I now use for a "good" OpenAPI specification.
A spec can be valid, render beautifully in your docs, generate an SDK, and still be difficult for an agent to use correctly.
There is already some evidence for this. One 2025 study looked at 50 real APIs covering 5,066 endpoints and generated MCP tools directly from their OpenAPI specifications. Only 76.5% of sampled tool calls worked without changes. After the researchers fixed recurring issues in the specs, with an average of about 19 lines changed per API, success rose to 99.9%.
A separate 2026 industrial study looked at 16 mature production APIs with roughly 600 endpoints. The APIs themselves were already stable and widely used, but agent experiments struggled with tool selection, planning, and request construction. An audit of the underlying OpenAPI files found 2,450 documentation and API-design issues across those endpoints.

OpenAPI validation tells you whether the document follows the format. It does not tell you whether an agent will choose the right operation, fill the arguments correctly, understand what a request changes, or know what to do when it gets an error.
So if I were writing an OpenAPI specification today with agents in mind, these are the things I would care about most.
This piece continues a theme from When agents use your software, your API becomes the product, where I looked at what happens when the spec and production drift apart.
Start with what the operation actually means
Take an operation like this:
/users/{id}/email:
patch:
operationId: updateUser
summary: Updates a user
There is nothing structurally wrong with it, but updateUser leaves a lot unanswered. For example:
- Can it update the user's name too?
- Is this endpoint only for email changes?
- Does changing the email send a verification message?
- Does the old address remain active until verification?
- Is there another endpoint an agent should use for profile changes?
A human developer can often fill in those gaps from the surrounding docs. An agent may be choosing between dozens of operations based largely on their names, descriptions, and schemas.
That is why I would make the operation much more explicit:
/users/{id}/email:
patch:
operationId: updateUserEmailAddress
summary: Change a user's email address
description: >
Use this operation only to change the email address
of an existing user. Do not use it to update profile
fields such as name or avatar.
Changing the address sends a verification email.
The current OpenAPI specification already gives us operationId, summary, and description for exactly this sort of information.
There is also an IETF Internet-Draft published in June 2026 specifically about HTTP APIs consumed by AI agents. It is still a work in progress, not an Internet Standard, but its reasoning is useful. One of its main observations is that operation names, descriptions, and schemas become direct inputs to an agent's tool-selection process. Similar-looking operations can therefore lead to wrong-operation or wrong-argument mistakes.
So I wouldn't worry about making descriptions sound polished. What matters is whether they help the agent choose the right operation, especially when two operations are easy to confuse.
Let the schema carry as much meaning as possible
Descriptions help, but I would rather encode a rule in the schema when OpenAPI already gives me a way to do it. Suppose an endpoint accepts:
{
"status": "active"
}
A weak schema might say:
status:
type: string
Now the agent has to infer the allowed values from somewhere else. If the real choices are known, put them in the spec:
status:
type: string
enum:
- pending
- active
- suspended
- cancelled
The same applies to ranges and formats. Instead of:
limit:
type: integer
use:
limit:
type: integer
minimum: 1
maximum: 100
default: 20
If arbitrary properties are invalid, close the object:
type: object
additionalProperties: false
required:
- email
properties:
email:
type: string
format: email
These constraints help any API client, not only agents. The difference is that an agent is much more likely to invent a reasonable-looking value when the schema leaves the choice open.
The June IETF draft makes the same recommendation. It encourages typed fields, fixed value sets, bounds, required fields, and rejecting unknown properties because those reduce the amount of guessing an agent has to do.
Make authentication and access rules visible
This is one place where I have seen spec drift cause real confusion. An endpoint may exist and the request may be perfectly valid, but whether somebody can use it depends on their authentication method, OAuth scope, role, subscription plan, or some other access rule.
If production enforces that behaviour but the OpenAPI file does not describe it, an agent gets a different version of the API from the one that actually exists.
OpenAPI supports security requirements at both the API and operation level, so operations can declare the authentication schemes they require or override a global rule.
For example:
security:
- bearerAuth: []
paths:
/reports/export:
post:
operationId: exportReport
security:
- oauth2:
- reports:export
That covers protocol-level access, but business access is often harder to express.
If an endpoint requires a paid plan, OpenAPI does not have a universal subscriptionPlan field you can fill in. I would still describe that constraint clearly where the agent can see it:
description: >
Export a report as CSV.
Requires the Pro or Business plan.
Returns 403 with error code `upgrade_required`
when the current account does not have access.
The 403 response should reflect that same rule. What matters is that the specification and the API stay in sync, because if the docs say Starter but production requires Pro, the agent is working from the wrong information.
Errors should tell the agent what changes next
An error response does more than report that something failed. For an agent, it often determines the next action. Take rate limiting:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"error": "rate_limit_exceeded",
"message": "Try again in 60 seconds."
}
That is much more useful than:
{
"error": "Request failed"
}
And OpenAPI lets us describe both the body and response headers:
responses:
'429':
description: Too many requests
headers:
Retry-After:
description: Number of seconds to wait before retrying
schema:
type: integer
content:
application/json:
schema:
type: object
required:
- error
- message
properties:
error:
type: string
enum:
- rate_limit_exceeded
message:
type: string
example: Try again in 60 seconds.
This gives generated code or an agent enough information to respond appropriately instead of treating every failure the same way.
The same principle applies to other errors.
- A 403 should make it clear whether the caller is missing a permission, needs a different plan, or is simply not allowed to perform that action.
- A 400 should point to the field that failed and explain what the API expected.
- A temporary 500 should be easy to distinguish from a validation error that will never succeed if the same request is sent again.
The IETF agent-friendly API draft makes this point directly because agents often retry. It recommends structured errors, stable error identifiers, and machine-readable retry information when retrying is appropriate.
Describe side effects before an agent discovers them
This becomes important as agents move from reading data to changing it. Consider:
/orders/{id}/cancel:
post:
operationId: cancelOrder
summary: Cancel an order
- Does that refund the customer?
- Can the cancellation be reversed?
- Does it notify someone?
- Can an order that has already shipped be cancelled?
- What happens if the agent calls the operation twice because the first response timed out?
Those are important differences when the caller is allowed to act autonomously. I would expose them:
/orders/{id}/cancel:
post:
operationId: cancelOrder
summary: Cancel an unshipped order
description: >
Cancels an order that has not yet shipped.
Side effects:
- releases reserved inventory
- starts a refund when payment has already settled
- sends a cancellation email to the customer
Calling this operation again for an already-cancelled
order returns the existing cancellation result.
That last point is really about idempotency. Agents may retry a request after a timeout or failed response, and they may also revisit a workflow and make the same call again later. If repeating a state-changing request could create duplicate charges, duplicate resources, or some other unintended effect, the API needs a safe way to handle that.
The IETF draft treats retry behaviour and repeated writes as important concerns for agent-facing APIs, and recommends idempotency mechanisms for operations where repeating the same action could be harmful.
For higher-risk actions, I would go a step further and consider a preview or confirmation step before the final write. Something like:

A destructive operation is much easier to trust when the agent can inspect what happens before it commits the change.
More description is not automatically better
It is easy to take all of this and conclude that every operation needs a long, detailed description, but I do not think that is the right approach.
A 2026 study of 856 tools across 103 MCP servers found quality issues in 97.1% of the descriptions under the researchers' evaluation criteria. Improving those descriptions raised median task success by 5.85 percentage points, but the richer descriptions also increased execution steps by 67.46%, and performance actually got worse in 16.67% of the tested cases.
Another 2026 benchmark found a similar trade-off from a different angle. When agents had to work with more realistic API complexity, irrelevant information caused the largest performance drop among the scenarios tested, reducing strong-model performance by 27.3%.
So the goal is not to document everything everywhere.
I would aim for descriptions that are explicit enough to remove ambiguity, but concise enough that the agent can quickly understand what matters.
A large OpenAPI file can become its own problem
This is another area I think will matter more as APIs get larger. A mature product can easily expose hundreds of endpoints, and if each one becomes a separate tool, the agent suddenly has to choose from a long list of operations that may look very similar.
That makes naming and descriptions more important, but it also creates a context problem. The agent has to spend part of its available context just understanding the tool surface before it can even start the actual task.
The June IETF draft recommends curating the operations agents can see instead of exposing a large low-level API surface by default. It also suggests using composite operations when a common workflow can be completed safely in one bounded call.
For example, an API may expose:
create_order
add_order_item
set_shipping_address
apply_discount
calculate_tax
finalize_order
That makes sense as a resource API, but if nearly every agent workflow performs those operations together, an agent-facing layer might expose a higher-level operation:
create_checkout
with the underlying API still doing the real work.
This is where I would separate two concerns. OpenAPI should describe the API as accurately as possible, while the surface exposed to an agent may still need to be curated for clarity and safety.
Keep responses small and easy to continue
Request schemas get most of the attention, but responses matter too. An endpoint that returns thousands of fields or an unbounded list can use up a large part of an agent's context before it gets to the next step.
The agent-friendly API draft recommends bounded responses, field selection where appropriate, stable ordering, and cursor-based pagination with continuation values that the client can use directly.
So instead of:
GET /transactions
returning everything, I would prefer something that supports:
GET /transactions?limit=50&status=pending
and responds with:
{
"items": [],
"next_cursor": "eyJpZCI6MTIzNH0="
}
The agent should not need to calculate the next offset or infer how pagination works from prose.
Test the spec without helping the agent
Before calling an API agent-ready, give the agent the same material a developer would have access to, such as your public docs, the OpenAPI specification, and an API key if the task requires one, then ask it to complete a few real integration tasks.
For example:
Using only this API specification:
1. Fetch a customer's current subscription.
2. Upgrade them to the Pro plan.
3. Handle rate limits correctly.
4. Stop if the current account does not have permission.
Then inspect what it does.
- Did it choose the right operation?
- Did it invent a parameter?
- Did it understand the authentication scheme?
- Did it send a value outside the allowed range?
- Did it know what to do with a 429?
- Did it retry a write that should not be repeated?
- Did it misunderstand a side effect?
That test tells you something a schema validator cannot. It shows how the API is actually understood by the agent, which is ultimately what matters if agents are going to build against it.
I would run the same task against more than one model as well, because tool-selection behaviour can vary.
Some of this can also be automated. CI can check for drift between routes and the OpenAPI specification, exercise examples against a test environment, validate known error responses against their schemas, and compare documented security requirements with the middleware that actually protects the route.
You could even have an agent periodically build a small integration from the published specification and flag anything it cannot resolve cleanly.
The important part is to test the OpenAPI file as an interface, not just as valid YAML.
What I would check before calling a spec agent-ready
For each operation:
- I want to know whether the name makes its intent obvious and whether similar operations are easy to distinguish.
- The schema should contain the constraints the API already knows, including allowed values, bounds, required fields, and formats.
- Authentication and access rules should match production.
- Errors should be structured enough for the caller to know what failed and whether anything useful can happen next.
- State-changing operations should document their side effects and have clear retry or idempotency behaviour.
Then I want to give the spec to an agent and see what it actually does with it.




