OpenAPI Context for Cursor, Claude Code and Copilot

Stop AI coding assistants from inventing endpoints: what API context to give Cursor, Claude Code and Copilot, what to leave out, and an example prompt.

On this page

To stop Cursor, Claude Code or GitHub Copilot from inventing endpoints, give the assistant the contract for the operations it is about to call: the base URL, the auth scheme, the method and path, every parameter, the request and response schemas, and the documented error statuses. For a one-off task, paste a trimmed contract for just those operations into the prompt. For ongoing work, commit the OpenAPI file and point a project rules file (.cursor/rules, CLAUDE.md or .github/copilot-instructions.md) at it, or connect an MCP server that returns operations on demand. Leave out the rest of the spec, long prose and unrelated endpoints.

Why assistants invent endpoints and fields

A coding assistant writes the code that looks most likely given what is in its context window. When your API's contract is missing, the most likely code is whatever similar APIs looked like in the model's training data. That code compiles and reads well, which is why the mistakes tend to surface at runtime as 404s, 401s and undefined fields. The guesses follow a few familiar patterns.

  • Paths: /api/orders/:id/cancel or DELETE /orders/:id when the API serves POST /v1/orders/{orderId}/cancel.
  • Field names and casing: createdAt when the JSON says created_at, or userId when the parameter is customer_id.
  • Response envelopes: reading res.data.items when the list sits at the top level of the body.
  • Auth: an Authorization: Bearer header when the API expects an X-API-Key header or an OAuth scope the token lacks.
  • Errors: a generic catch for 404 and 500, while the API documents 409 and 422 with a structured body the UI should show.
  • Old versions: for well-known public APIs, the model may remember endpoints or fields from an earlier version than the one you use.

Two causes sit on your side of the prompt. The first is a spec with gaps. If a 200 response has no schema, or an object schema has no required list, the assistant has nothing to copy and fills the hole with a plausible shape. The response schema guide shows what a complete response looks like, and the API score flags operations with undocumented responses. The second cause is too much context. A full spec for a large API buries the twenty lines that matter under thousands that do not, and the assistant can pick up a field from the wrong operation.

The context that prevents guessing

Everything the assistant needs to write a correct call is already in your API contract. The table maps each piece to where it lives in an OpenAPI document. If you only remember one row, make it the response schema: request mistakes fail loudly with a 400, but a misread response field fails quietly as undefined in the UI.

IncludeWhat the assistant gets wrong without itWhere it lives in OpenAPI
Base URLHardcodes localhost or invents a hostservers
Auth schemeWrong header name, wrong token type, missing scopescomponents.securitySchemes plus the operation's security
Method and pathWrong verb, wrong path template, renamed path parameterspaths
ParametersMisspelled query names, missing required headers, unknown enum valuesparameters (path, query, header)
Request body schemaMissing required fields, wrong types, values outside min/maxrequestBody.content.*.schema
Success response schemaWrong field names, wrong nesting, missing null checksresponses 2xx
Error responsesNo handling for 409 or 422, wrong error body shaperesponses 4xx, 5xx and default
The taskBuilds the wrong thing in the wrong fileNot in the spec; you write it

What to leave out

These parts of a spec rarely help with a single integration task, and each one competes with your instructions for the model's attention:

  • Operations the task does not touch. Listing them invites the assistant to call a neighbor endpoint it thinks is related.
  • Long Markdown in info.description and tag descriptions: onboarding tutorials, changelogs, rate limit essays.
  • Vendor extensions such as x-codegen-* or x-internal, which are written for other tools.
  • Several examples per schema. One realistic example helps; five near-copies add length.
  • Schemas under components that the chosen operations never reference.

How much context is enough

Context windows are large, but a bigger input is slower, costs more quota, and is harder for the model to use well. In the paper "Lost in the Middle", Liu et al. found that language models used information placed at the start or end of a long input more reliably than information in the middle. A spec for a large API can be several times the size of the files the assistant is editing, so the contract you care about can end up in that middle.

OpenAI's rule of thumb is about four characters per token for English text. The single-endpoint prompt later in this guide is about 2,600 characters, roughly 650 tokens, and it contains every fact needed to call that endpoint correctly. Aim for that density: every line should be a fact the code depends on. To cut a spec down to one operation, resolve its $ref pointers first so the schemas arrive inline, then drop what the call does not need.

Trim a spec to one operation

  1. Pick the operations

    List the method and path of every call the task makes. Usually that is one, sometimes two or three.

  2. Resolve references for those operations only

    Inline the schemas they use so nothing points at a component the model cannot see.

  3. Remove noise

    Drop x- extensions, extra examples and multi-paragraph descriptions. Keep one-line descriptions; they become useful JSDoc.

  4. Keep every constraint

    required, enum, nullable, format, minLength, maxLength, minimum and maximum all change the code the assistant writes.

  5. Add the error responses

    Include each documented 4xx with its body schema, so the assistant writes a branch for each one.

The script below does the first four steps for an OpenAPI 3.x file using swagger-parser. Swagger 2.0 files keep the base URL in host and basePath instead of servers, so convert them first or adjust the script. If you only have a Swagger UI page, get the raw spec from Swagger UI first.

JavaScriptextract-operation.mjs
import SwaggerParser from "@apidevtools/swagger-parser";

// Usage: node extract-operation.mjs openapi.yaml POST /v1/orders/{orderId}/cancel
const [file, method, path] = process.argv.slice(2);
const api = await SwaggerParser.dereference(file, { dereference: { circular: "ignore" } });
const item = api.paths?.[path];
const op = item?.[method.toLowerCase()];
if (!op) {
  console.error(`No ${method} ${path} in ${file}`);
  process.exit(1);
}

// Drop vendor extensions (x-*) at every level; keep everything else.
const strip = (v) =>
  Array.isArray(v) ? v.map(strip)
  : v && typeof v === "object"
    ? Object.fromEntries(Object.entries(v).filter(([k]) => !k.startsWith("x-")).map(([k, x]) => [k, strip(x)]))
    : v;

const contract = {
  baseUrls: (api.servers ?? []).map((s) => s.url),
  auth: { required: op.security ?? api.security ?? [], schemes: api.components?.securitySchemes ?? {} },
  method: method.toUpperCase(),
  path,
  summary: op.summary,
  parameters: [...(item.parameters ?? []), ...(op.parameters ?? [])],
  requestBody: op.requestBody,
  responses: op.responses,
};

console.log(JSON.stringify(strip(contract), null, 2));
Install with npm i @apidevtools/swagger-parser. Circular schemas stay as $ref pointers, so check the output for any that remain.

One endpoint or a whole collection

Match the scope of the context to the scope of the task. A prompt that covers one form should carry one operation. A prompt that asks for a client module should carry every operation that module wraps, because the assistant needs to see the shared types and naming across them.

Choose the prompt scope

ScenarioRecommendationAlternative
You are building one form, hook, handler or test against a single call.Use an endpoint prompt with one operation's full contract.Add a second operation only when the task really calls both, such as create followed by fetch.
You are writing a client module for one resource, such as list, get, create and cancel for orders.Use a collection prompt with every operation under that tag, so shared types like Order are defined once.If the tag holds dozens of operations, split it by the screens or features you are building.
You are generating an SDK or working across most of the API over days or weeks.Stop pasting. Commit the spec, reference it from a rules file, and let the agent read operations as it needs them, or use an MCP server.Generate the types with a code generator and give the assistant the generated file instead of the raw spec.

Four ways to give the assistant your contract

MethodBest forWatch out for
Paste a trimmed contract into the promptOne-off tasks on one endpoint or one resourceGoes stale if you reuse an old prompt after the spec changes
Project rules file pointing at the specEvery session in a repo that calls the APIRules load on every request in their scope, so keep them short and link to the spec
Attach or mention the spec fileSmall specs and quick questionsAgents may read a large file in parts or search it, and miss a schema
MCP server that serves the specLarge APIs used across many tasksIt runs on your machine with your credentials, so review it before installing

Project rules files

Each assistant reads a project-level instruction file into its context. Cursor uses rule files in .cursor/rules, written as .mdc files with frontmatter that controls when they apply. Claude Code reads CLAUDE.md from the project root at the start of a session. GitHub Copilot reads .github/copilot-instructions.md, and also supports path-specific files in .github/instructions/ whose applyTo glob limits where they apply. Several agents, Cursor among them, also read an AGENTS.md file.

Put the location of the spec and your conventions in these files, and keep the spec itself out. A rule file that inlines a 5,000-line spec pays that cost on every request it applies to. In CLAUDE.md, the @path syntax imports a file's contents into memory, so write the spec's path as plain text if you want Claude Code to open it only when needed.

markdown.cursor/rules/orders-api.mdc
---
description: Rules for code that calls the Orders API
globs: src/api/**,src/features/orders/**
alwaysApply: false
---

The Orders API contract is docs/openapi.yaml. It is the only source of truth.

- Before calling an endpoint, find its operation in docs/openapi.yaml and use
  the exact path, parameter names and schema field names it declares.
- JSON fields are snake_case. Do not convert them to camelCase.
- Never call an endpoint that is not in docs/openapi.yaml. If one seems
  missing, say so and stop.
- Handle every documented 4xx status for the operation. Error bodies follow
  the ProblemDetails schema.
- Generated types live in src/api/types.ts. Do not redefine them inline.
markdownCLAUDE.md
## Orders API

- Contract: docs/openapi.yaml (OpenAPI 3.1). Read the operation you need from it
  before writing client code. Do not import the whole file into context.
- Base URL comes from ORDERS_API_URL. Auth is a bearer token from getSession().
- Never invent endpoints or fields. If the spec lacks something, ask.
- Run `npm run test:api` after changing anything in src/api/.
markdown.github/copilot-instructions.md
# Copilot instructions

Code in src/api/ calls the Orders API. Its contract is docs/openapi.yaml.
Use only the paths, parameters and fields declared there, keep JSON fields
snake_case, and handle each documented error status for an operation.
Ask instead of guessing when the spec does not cover something.

Mentioning the spec file in chat

All three tools let you attach a file to a single request: @openapi.yaml in Cursor and Claude Code, #file:openapi.yaml in Copilot Chat in VS Code. This works well for small specs. For large ones, attach the extracted contract from the script above, so the assistant reads 150 lines instead of 15,000.

MCP servers

The Model Context Protocol lets an assistant call tools that a local or remote server provides. An OpenAPI MCP server can expose tools such as list operations or get an operation's schema, so the agent fetches the contract for the endpoint it is working on instead of loading the whole spec. Cursor reads MCP configuration from .cursor/mcp.json, Claude Code from a project .mcp.json (or claude mcp add), and VS Code from .vscode/mcp.json for Copilot agent mode. Several open-source OpenAPI MCP servers exist. Some can also send live requests, so check what one does and which credentials it can reach before you connect it.

Example: a prompt for one endpoint

Here is the same task asked two ways. The endpoint cancels an order in a fictional Shop Orders API.

AvoidA prompt that invites guessing

Text
Add a cancel order function using our orders API and wire it to the Cancel button.

The assistant has to pick a path, a verb, an auth header, a body shape and an error format. Each guess will compile, and you find out which ones are wrong when the button fails in the browser.

markdowncancel-order-prompt.md
# Task
Add a typed `cancelOrder()` function to `src/api/orders.ts` and call it from the Cancel button in `src/features/orders/OrderActions.tsx`. Use only the contract below. If you need something it does not contain, stop and ask.

## Context
- API name: Shop Orders API
- Base URL: https://api.example.com (read it from `process.env.ORDERS_API_URL`)

## Endpoint contract
- Method: POST
- Path: `/v1/orders/{orderId}/cancel`
- Summary: Cancel an order that has not shipped
- Auth: bearerAuth (http bearer). Send `Authorization: Bearer <token>` using the token from `getSession()`.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orderId` | path | string (uuid) | yes | Order to cancel |
| `Idempotency-Key` | header | string | no | Makes retries safe |

**Request body** (`application/json`, required)

- `reason`: `"customer_request" | "fraud" | "out_of_stock"`, required
- `note`: string, max 500 characters, optional

**Responses**

- `200`: the updated order (`Order` below)
- `401`: missing or expired token, no body
- `404`: order not found, `ProblemDetails` body
- `409`: order already shipped or cancelled, `ProblemDetails` body
- `422`: body failed validation, `ProblemDetails` body with `errors`

## Types
```ts
export type CancelReason = "customer_request" | "fraud" | "out_of_stock";

export interface CancelOrderBody {
  reason: CancelReason;
  /** Max 500 characters. */
  note?: string;
}

export interface Order {
  id: string;
  status: "pending" | "paid" | "shipped" | "cancelled";
  total_cents: number;
  currency: string;
  /** ISO 8601 date-time. Null until the order is cancelled. */
  cancelled_at: string | null;
}

export interface ProblemDetails {
  type: string;
  title: string;
  status: number;
  detail?: string;
  errors?: { field: string; message: string }[];
}
```

## Requirements
- Use the exact path, header and field names above. JSON fields are snake_case.
- Call no other endpoint and send no field that is not listed.
- Disable the Cancel button while the request is in flight.
- On 409, show `detail` and refetch the order. On 401, redirect to sign-in.
- Throw an `ApiError` with `status` and the parsed body for every non-2xx response.

## Acceptance criteria
- [ ] Sends POST {base}/v1/orders/{orderId}/cancel with `orderId` URL-encoded
- [ ] Body has `reason`, and `note` only when the user typed one
- [ ] Success is typed as `Order`, with `cancelled_at` as `string | null`
- [ ] 401, 404, 409 and 422 each have a handled branch
- [ ] No field outside the contract is read or sent
About 2,600 characters. Every line is either a fact from the spec or a decision the developer made.

A few details make this prompt work. The Task names the files, which the spec cannot know. The contract spells out the path template and the header name, so there is nothing to infer. The Types block marks cancelled_at as string | null, which forces a null check the assistant would otherwise skip. The 409 line tells the assistant an already-shipped order is a normal outcome with its own UI, and the error handling guide covers the ProblemDetails shape it relies on. The acceptance criteria double as your review checklist for the diff.

Generate the prompt from your spec in APIForge

Writing that contract by hand is slow, and hand-copied field names bring back the typos you are trying to avoid. APIForge builds the prompt from your OpenAPI or Swagger spec. Paste a spec URL in the workspace, select an operation, open the AI prompt tab and choose a scope: Endpoint for the selected operation, or Collection for every operation that shares its first tag.

The output is a short Markdown prompt with five sections: Task, API, Endpoint (or Endpoints for a collection), Types and Rules. Everything except Types is built directly from the spec: the base URL from servers, auth resolved from the security schemes, each parameter with its location, type and required flag, and the success and error status codes. Shapes are not repeated as schema JSON; the endpoint lines point to type names such as GetPetByPetIdResponse, and the model writes those types in the Types block under rules that forbid invented fields. Pick one endpoint and the prompt covers only that endpoint. Pick a collection and it covers every operation in it, with up to ten described in full and the rest listed by method and path. Copy it, add your file paths to the Task line, and paste it into Cursor, Claude Code or Copilot.

Lines the spec has nothing for are left out instead of padded. The one exception is a missing base URL: the prompt then says "not in the spec, ask me", so the assistant asks you instead of guessing a host. If auth or error responses are missing from the prompt, the spec does not document them. A fix there removes the guess for every developer and every tool; the OpenAPI best practices guide covers what to add.

Frequently asked questions

Yes, and for ongoing work in agent modes (Claude Code, Cursor's agent, Copilot agent mode) it is a good default. Reference the file from your rules file so every session knows where it is. Two limits apply: an agent may read a large spec in parts or search it, so it can miss a schema defined far from the path, and it will faithfully copy a stale spec. For a specific task, still give it the trimmed contract.

For a large API used across many tasks, often yes, because the agent fetches only the operations it asks for. For one endpoint, pasting is faster and you can see exactly what the model was given, which makes a wrong answer easier to debug.

Check the spec first. Responses without schemas, objects with no properties, and missing required lists all leave room to guess. Then check the prompt: say explicitly that it must not use fields outside the contract and should ask when something is missing. Running the spec through the API score shows the operations with gaps.

No. Describe the scheme and where the code reads the credential, such as an environment variable. If you want to confirm the endpoint behaves as documented, send a test request yourself from an API client and paste the response shape, with any personal data removed.

Sources

  1. RulesCursor documentation
  2. Manage Claude's memory (CLAUDE.md)Anthropic documentation
  3. Adding repository custom instructions for GitHub CopilotGitHub Docs
  4. Model Context Protocol: introductionmodelcontextprotocol.io
  5. Lost in the Middle: How Language Models Use Long ContextsarXiv
  6. What are tokens and how to count them?OpenAI Help Center