openapi_doc
Skill1 starsChanged 3 months agoMITView on GitHub
Author or review/retrofit OpenAPI 3.1 specs optimized for LLM-tool ingestion (code-mode/MCP toolspec registries). Use whenever the user wants to write an OpenAPI or swagger spec, document an API, review or fix an existing spec, make an API "importable as tools", author endpoint descriptors, or asks why an imported API produces bad tool names, hallucinated fields, or wrong tool selection. Trigger even if they only say "api spec", "swagger", "document these endpoints", or "make this API LLM-friendly".
In this repository18 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- clickhouseSkill
- distributed-systemsSkill
- flink-javaSkill
- flink-pythonSkill
- flink-sqlSkill
- gitSkill
- grokSkill
- java-prod-skillSkill
- javaSkill
- mermaid-skillSkill
- postgresSkill
- python-numericsSkill
- pythonSkill
- pytorchSkill
- reactSkill
- rustSkill
- sqlSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 10
Every spec you write here is read twice, by two very different consumers:
- Very specific: line 57
Every request body, parameter, and response is fully typed.
- Very specific: line 67
Every operation carries one realistic request example and one response example (3.1 examples, not the deprecated singular).
- Very specific: line 101
Every write operation that supports it declares the Idempotency-Key header parameter (shared components/parameters/IdempotencyKey).
- Very specific: line 143
Do not invent further x- fields ad hoc.
- Very specific: line 156
Run scripts/lint_spec.py <spec> — zero findings.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
OpenAPI specs for LLM-tool ingestion
The one idea: your spec has two readers
Every spec you write here is read twice, by two very different consumers:
- A machine loader parses it into tool registry rows — schemas become validation,
operationIdbecomes the tool name,serversbecomes an SSRF allowlist, security schemes become auth descriptors. - An LLM picks tools by searching names, summaries, and tags — then fills arguments from the schema and the example.
Classic API docs are written for a third reader (a human with a browser) who can tolerate vagueness. The LLM cannot: a vague summary means the wrong tool gets picked; a loose schema means hallucinated fields; a missing example means a wasted probe call. Every rule below exists to serve one of the two real readers.
Modes
Author mode — writing a spec from scratch. Follow the checklist top to bottom; start
from the closest worked example in references/worked-examples.md.
Retrofit mode — reviewing/fixing an existing spec. Run scripts/lint_spec.py <spec>
first; fix findings worst-first (bare schemas → names → examples → the rest). Preserve the
API's actual behavior — retrofit changes documentation, never invents endpoints. If the
spec contradicts the implementation, flag it, don't paper over it.
Both modes end the same way: lint passes clean, and the self-review question is answered — "could an LLM that has never seen this API pick the right operation and fill every argument correctly from this spec alone?"
The checklist
1. Identity — operationId is the tool name
Verb-first snake_case, globally unique, self-descriptive: get_contact, search_invoices,
send_campaign_batch. The loader prefixes the source name (crm_get_contact), so do not
bake the service name in. Never accept generator junk (getV2ContactsById,
ContactsController_findOne) — those become tool names the LLM must reason about.
2. Descriptions — written for search, not for humans who already know
summary: one action-first sentence, ≤ 120 chars. "Fetch one contact by id, email, or phone." Not "Contact endpoint." Not "Returns contact entity."description: 2-4 sentences, task-centric, include the words a caller would think ("lookup", "find customer", synonyms of the domain terms). This is the searchable surface.tags: domain + operation-class keywords (contacts,lookup,crm) — not org-chart groupings ("Misc", "V2").
3. Schemas — the anti-hallucination contract
- Every request body, parameter, and response is fully typed. A bare
{"type": "object"}is the #1 cause of hallucinated fields — the lint rejects it. additionalProperties: falseon request objects; listrequiredexplicitly.- Enumerate every enum. Use
format(date-time,uuid,email) wherever true. - 3.1 nullability:
type: ["string", "null"]— never the 3.0nullable: true. - Name shared shapes in
components/schemasand$refthem; the loader dereferences, and humans maintaining the spec see one definition.
4. Examples — the single biggest error-rate lever
Every operation carries one realistic request example and one response example
(3.1 examples, not the deprecated singular). Realistic means plausible values —
"sara@acme.com", not "string". One concrete example empirically halves wrong-field
errors; a "string" placeholder teaches nothing.
5. Servers — one canonical, no surprises
Exactly one entry in top-level servers. No per-operation or per-path servers overrides,
no absolute URLs inside operations — importers treat the single servers[0] as the SSRF
pin, and anything else is rejected or needs an explicit allowlist. Environment variance
belongs in server variables or the importer's config, not in scattered URLs.
6. Security — declare exactly what the wire needs
Define every scheme in components/securitySchemes and reference via security (global
default + per-operation overrides where they differ). Custom headers are apiKey with
in: header and the real header name — never prose in the description. OAuth2 flows
carry real tokenUrl/authorizationUrl and a scope map with meaningful per-scope
descriptions. Open endpoints state security: [] explicitly so openness reads as a
decision, not an omission. Full recipes (OAuth2 both flows, api-key, custom header,
multi-tenant SaaS vs standalone): references/auth-patterns.md.
7. Effect honesty — methods must not lie
GET/HEADmust be side-effect-free. A side-effecting GET is a bug to fix in the API, not to document around.- A
POSTthat only reads (search bodies, GraphQL-ish queries) declaresx-effect: read(see extension vocabulary below) — otherwise importers classify it as a write and it pays write-path costs. - Irreversible operations (
DELETE, purges, sends that cannot be recalled) arex-effect: destructiveeven when the method alone wouldn't say so.
8. Idempotency — declare it so importers can use it
Every write operation that supports it declares the Idempotency-Key header parameter
(shared components/parameters/IdempotencyKey). Importers detect this and forward their
own keys — upstream-authoritative dedup. If the API dedups by natural key instead, say so
in the operation description.
9. Pagination — one shape, spelled out
Pick cursor pagination (cursor query param + next_cursor in the response, null when
done) and use the identical shape on every list operation. LLMs write the pagination loop
from the spec; consistency means the loop written for one endpoint works on all. Document
the page-size cap. Offset pagination only when the backend truly cannot cursor.
10. Errors — typed, enumerated, matchable
One shared error schema (components/schemas/Error: error_type, message, optional
details) with error_type as a closed enum. Enumerate the status codes each operation
actually returns (401/403/404/409/422/429 as applicable) — an LLM branches on these;
"default: error" teaches it nothing. Include Retry-After on 429 responses.
11. Multi-tenant vs standalone
- SaaS multi-tenant: tenant identity derives from the credential (token claim, key
ownership) — never a caller-supplied
tenant_idparameter on tenant-scoped operations (that is confused-deputy by construction). Path-level org ids are acceptable only when the server verifies them against the credential; say that in the description. - Standalone/single-tenant: skip tenant params entirely; keep the auth scheme, note that scoping is deployment-level.
12. Versioning and deprecation
Version in the server path (/v1) or header — one mechanism, stated once. Deprecated
operations carry deprecated: true AND a description line naming the replacement
operationId. Never delete an operation from the spec while the endpoint still serves.
The x- extension vocabulary (minimal, documented, optional)
Specs remain valid vanilla OpenAPI; these carry hints where the standard has no slot:
| Extension | Values | Where | Why |
x-effect | read | write | destructive | operation | Overrides method-derived effect classification. Only needed where the method lies (POST-that-reads) or under-states (irreversible writes). Importers treat a read-downgrade as requiring human review — use sparingly and truthfully. |
Do not invent further x- fields ad hoc; if a hint feels missing, it probably belongs in
the importer's config, not the spec.
OpenAPI 3.1 specifics (house default)
Author openapi: 3.1.x, YAML. The 3.1 deltas that matter here: schemas are real JSON
Schema (type arrays for nullability, examples plural, const, full $ref siblings);
webhooks top-level section exists for callback registration APIs. Some consumers are
3.0-only — down-convert on export if needed (nullable back-conversion, single example),
don't dumb down the source of truth.
Self-review before delivering
Run scripts/lint_spec.py <spec> — zero findings. Then read three random operations cold
and ask: right tool findable from summary+tags alone? Every argument fillable from schema +
example alone? Effect classification correct if you only saw method + x-effect? Fix, rerun,
deliver.
References
references/auth-patterns.md— every securityScheme recipe: OAuth2 (client-credentials, auth-code + refresh), apiKey header/query, custom header, bearer, open, multi-tenant SaaS vs standalone patterns. Read when authoring the security section.references/worked-examples.md— two compact complete specs: SaaS multi-tenant OAuth2 CRM (with POST-search read-downgrade, bulk send + Idempotency-Key, cursor pagination) and standalone api-key inventory service (destructive delete, custom header). Start author-mode work from the closer one.scripts/lint_spec.py— structural linter (no deps beyond PyYAML). Run in retrofit mode first and before delivering in any mode.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

