GUIDE · SPEC READINESS

What a generation-friendly OpenAPI document looks like.

Ajolla can work with imperfect specs — but the quality of the generated SDK, docs, and MCP server is bounded by the quality of the input. This guide is opinionated about the patterns that make Ajolla’s output materially better, and the patterns that quietly degrade it.

At a glance

Affects SDK quality
operationIds, request/response schemas, named components, deprecation flags.
Affects docs quality
Tag definitions, operation summaries and descriptions, example payloads, security schemes.
Affects MCP quality
operationIds (become tool names), descriptions (become tool prompts), method + path (drive safety classification), security.
Blocks generation
Unparseable YAML/JSON, external $refs we can’t resolve, missing paths object.

Structure and metadata

  • Provide a stable info.title and a semver info.version. Both surface in generated package names and docs.
  • List explicit servers entries with absolute URLs. The first server is used as the SDK’s default baseUrl.
  • Use product-oriented tags. Generated docs and MCP groupings inherit them; cryptic acronyms become cryptic group names.
  • Provide operation summary (one line) and description (a paragraph) on every operation. Both flow through to the docs and to the MCP tool prompt.
yaml
openapi: 3.1.0 info: title: Petstore version: 1.2.0 description: A small API for tracking pets and their owners. servers: - url: https://api.example.com/v1 description: Production

operationId is load-bearing

The operationId drives the SDK method name, the MCP tool name, and the docs anchor. Use predictable, action-led names. Without them, Ajolla synthesizes a name from the method + path, which works but produces brittle identifiers.

yaml
paths: /pets: get: operationId: petsList # → client.pets.list() summary: List pets post: operationId: petsCreate # → client.pets.create() summary: Create a pet /pets/{petId}: get: operationId: petsGet # → client.pets.get(petId) summary: Find pet by ID

Schemas and responses

  • Prefer named components.schemas over large inline objects when a shape is reused. Named schemas become reusable types in the SDK.
  • Document every status you return — including the error shape. Generated SDKs throw typed errors for any documented non-2xx response.
  • Use $ref to share request, response, and error envelopes across operations.
  • Avoid additionalProperties: true on payloads — it weakens both the SDK type and the MCP tool input schema.
yaml
components: schemas: Pet: type: object required: [id, name] properties: id: { type: string, format: uuid } name: { type: string } tag: { type: string } Error: type: object required: [code, message] properties: code: { type: string } message: { type: string } paths: /pets/{petId}: get: operationId: petsGet responses: "200": description: The pet content: application/json: schema: { $ref: "#/components/schemas/Pet" } "404": description: Not found content: application/json: schema: { $ref: "#/components/schemas/Error" }

Security schemes

  • Declare authentication explicitly. Missing security metadata degrades both the SDK’s auth helpers and the MCP safety classification.
  • Use one securityScheme per credential surface (bearer token, API key, OAuth flow) and reference it from operations that require auth.
  • Mark public operations with security: [] so the SDK can avoid sending the credential for them.
  • If your API has admin-only routes, scope them with a separate security requirement; the MCP generator uses that signal to bucket the tools as `admin`.
yaml
components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT security: - bearerAuth: [] paths: /admin/users: delete: operationId: adminUsersDelete summary: Delete a user (admin) security: - bearerAuth: [] x-ajolla-safety: admin # optional hint for the MCP classifier

Operation safety hints

Ajolla’s MCP generator bins each operation into safe, destructive, admin, or disabled. The defaults are method-based (GET / HEAD are safe; DELETE is destructive). You can override with an extension field:

yaml
paths: /pets/{petId}: delete: operationId: petsDelete x-ajolla-safety: destructive /pets: post: operationId: petsCreate x-ajolla-safety: safe # idempotent in our system

Deprecation and breaking changes

  • Mark legacy operations with deprecated: true. The endpoint inventory shows them with a warning state and the SDK adds a deprecation marker.
  • When a breaking change is shipped, prefer a new operationId over silently changing the shape. Generated SDK consumers should see a renamed method, not a runtime surprise.
  • Bump info.version with semver. The SDK’s package version reflects info.version.

Examples

  • Add an example or examples block on requestBody and 2xx responses. The docs page renders them inline.
  • Make examples illustrative and small. Avoid pasting real customer records.
  • Strip secrets out of examples before upload — treat any spec that has ever included a credential as compromised.

Operational hygiene

  • Host the spec over HTTPS and pin it by release tag or version, so a re-ingestion is reproducible.
  • Validate the spec in CI (e.g. with @apidevtools/swagger-cli) before uploading; Ajolla will degrade gracefully but a CI gate catches drift earlier.
  • Regenerate every output after a spec change. Never edit generated files by hand.
  • Track the spec revision (commit SHA, release tag) in the consuming repo’s release notes.

Common anti-patterns and the cost

Missing operationId
Method names become method+path slugs. Refactors silently rename methods.
additionalProperties: true
SDK types become Record<string, unknown>. MCP tool inputs lose validation.
Inline schemas everywhere
Repeated shapes become anonymous types; refactoring an envelope means refactoring every operation.
Undocumented errors
SDK throws untyped errors; agents using the MCP server can&rsquo;t reason about failure modes.
External $refs
Remote and file $refs are blocked at parse time (security). Inline before upload.
Wildcard security
No way to bucket admin tools in MCP. Either restrict the surface or use scope-specific security.

Related guides

OpenAPI readiness guide