agentleFS
base58edbase58ed/claude_code_files/skills/openapi_doc/SKILL.md

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".

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
  1. Distinctive:

    Every spec you write here is read twice, by two very different consumers:

    line 10
  2. Very specific:

    Every request body, parameter, and response is fully typed.

    line 57
  3. Very specific:

    Every operation carries one realistic request example and one response example (3.1 examples, not the deprecated singular).

    line 67
  4. Very specific:

    Every write operation that supports it declares the Idempotency-Key header parameter (shared components/parameters/IdempotencyKey).

    line 101
  5. Very specific:

    Do not invent further x- fields ad hoc.

    line 143
  6. Very specific:

    Run scripts/lint_spec.py <spec> — zero findings.

    line 156

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

  1. A machine loader parses it into tool registry rows — schemas become validation, operationId becomes the tool name, servers becomes an SSRF allowlist, security schemes become auth descriptors.
  2. 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: false on request objects; list required explicitly.
  • Enumerate every enum. Use format (date-time, uuid, email) wherever true.
  • 3.1 nullability: type: ["string", "null"] — never the 3.0 nullable: true.
  • Name shared shapes in components/schemas and $ref them; 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/HEAD must be side-effect-free. A side-effecting GET is a bug to fix in the API, not to document around.
  • A POST that only reads (search bodies, GraphQL-ish queries) declares x-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) are x-effect: destructive even 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_id parameter 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:

ExtensionValuesWhereWhy
x-effectread | write | destructiveoperationOverrides 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.