OpenAPI TypeScript SDK generator
Generate a production-quality TypeScript SDK from an OpenAPI specification. Behave like a real SDK generator, not a thin OpenAPI wrapper.
Scope and guarantees
- Analyze the OpenAPI spec first to propose intelligent defaults based on its contents
- Propose sensible defaults derived from the spec (title, servers, security schemes, operations, etc.)
- Ask required questions and require explicit confirmation
- Derive human-friendly method names from context, not OpenAPI identifiers
- Produce deterministic output
- Support OpenAPI 3.0 and 3.1
- Generate a typed ApiError and runtime features (retries, rate limiting, caching)
- Explain spec issues and tolerate non-blocking drift
Inputs (prefill only)
If the user provides JSON, treat it as prefilled answers. Still show defaults and require explicit confirmation before generating.
Example prefill:
{
"openapi_url": "https://api.example.com/openapi.yaml",
"openapi_path": "C:/specs/openapi.yaml",
"output_mode": "standalone_package | folder_only",
"output_path": "sdk",
"package_name": "@example/api-sdk",
"client_name": "ExampleClient",
"runtime_target": "node_and_browser",
"node_version": "22",
"auth_method": "bearer"
}
Either openapiurl or openapipath must be provided or collected.
Spec analysis (mandatory first step)
Before asking any questions, the agent must:
- Load and parse the OpenAPI spec (from URL or file path)
- Analyze the spec to extract:
- info.title → for package/client naming - info.version → for package version suggestion - servers → for base URL defaults - components.securitySchemes → for auth method defaults - security (global and per-operation) → for auth method analysis - Operation patterns → for naming style suggestions - Response headers → for rate limiting/caching hints - Operation methods → for runtime target suggestions
- Propose defaults based on this analysis
- Show reasoning for each proposed default
Mandatory interaction flow
- Parse and analyze the OpenAPI spec first (if provided or collected)
- Load the spec from URL or file path - Analyze the spec to propose intelligent defaults - Extract relevant information (title, servers, security schemes, operations, etc.)
- Ask all required intake questions in one concise block.
- Show defaults for every question, proposing values derived from the OpenAPI spec analysis
- Require explicit confirmation of answers or defaults.
- Show a naming preview and allow overrides.
- Ask for a final confirmation gate before any code generation.
Do not generate code until the user explicitly confirms.
Required intake questions (always ask)
Ask these questions in a single block and show defaults proposed from the OpenAPI spec analysis.
- SDK identity
- Package name Default: derive from OpenAPI info.title (sanitized, lowercased, kebab-case), or openapi-sdk if unavailable Example: "Pet Store API" → @pet-store/api-sdk or pet-store-api-sdk - Exported client name Default: derive from OpenAPI info.title (sanitized, PascalCase + "Client"), or ApiClient if unavailable Example: "Pet Store API" → PetStoreClient
- Output
- Output mode: folderonly or standalonepackage Default: folder_only - Output path (folder) Default: sdk/
- Spec source
- OpenAPI URL or local file path Default: use provided value; otherwise ask - If URL: does it require auth headers? Default: no
- Base URL
- Default: first entry in OpenAPI servers - If no servers exist, default to empty string and warn
- Naming style
- Verb-first (listPlants, getPlant) - Resource-first (plantsList, plantsGet) Default: verb-first
- Runtime target
- nodeonly, browseronly, or nodeandbrowser Default: nodeandbrowser - Node version (if Node is included) Default: 22
- Primary authentication method
- API key - Bearer token - OAuth2 access token (passthrough only) - Basic auth - None Default: analyze OpenAPI security and components.securitySchemes to propose the most common/primary auth method, or none if none exist - If multiple schemes exist, prefer: Bearer > API Key > OAuth2 > Basic > None - Show which operations use which auth methods if they differ
- Integration tests
- Yes, generate real integration tests (env-based, disabled by default) - No, generate mock-only tests Default: no
Naming preview step (mandatory)
Before generation:
- Show a preview of representative endpoints with proposed names.
- Allow user overrides.
- Proceed only after confirmation.
- Record overrides in
sdk-decisions.md.
Confirmation gate (mandatory)
After collecting answers and naming overrides, present a final summary and ask for explicit confirmation.
Example:
Here is what I am going to generate:
- Package name:
openapi-sdk
- Client name:
ApiClient
- Output mode:
folder_only
- Output path:
sdk/
- Base URL:
https://api.example.com
- Naming style: verb-first
- Runtime target: nodeandbrowser (Node 22)
- Auth method: bearer token
- Integration tests: disabled
Reply with:
confirm to proceed
- or list any changes you want
Do not generate code until the user confirms.
Decision recording (mandatory)
After confirmation, write:
sdk-decisions.md
Record all final decisions and whether each one was explicit or default.
Example:
- package_name: openapi-sdk (default, confirmed)
- client_name: ApiClient (default, confirmed)
- output_mode: folder_only (explicit)
- output_path: sdk/ (default, confirmed)
- naming_style: verb-first (default, confirmed)
- runtime_target: node_and_browser (default, confirmed)
- node_version: 22 (default, confirmed)
- integration_tests: false (default, confirmed)
Output modes
folder_only
Generate a minimal SDK folder suitable for copying into an existing codebase.
sdk/
├── index.ts
├── client.ts
├── operations.ts
├── errors.ts
├── types/
├── utils/
├── openapi-report.md
└── sdk-decisions.md
standalone_package
Generate a full, publishable SDK project.
<package>/
├── package.json
├── tsconfig.json
├── tsdown.config.ts
├── README.md
├── src/
│ ├── index.ts
│ ├── client.ts
│ ├── operations.ts
│ ├── errors.ts
│ ├── types/
│ └── utils/
├── examples/
├── tests/
├── openapi-report.md
└── sdk-decisions.md
Naming strategy (critical)
Do not use raw OpenAPI operationId values.
Derive names deterministically from context.
Naming goals
- Human-friendly
- Stable across regenerations
- Flat function surface
- Predictable
- Collision-free
Naming algorithm
- Extract the primary resource from the path
Ignore version prefixes such as /v1 or /api.
Examples:
/plants → plants
/plants/{id}/events → primary plants, secondary events
- Infer the action from HTTP method and path shape
| Method |
Path shape |
Action |
| GET |
collection |
list |
| GET |
item |
get |
| POST |
collection |
create |
| PUT |
item |
update |
| PATCH |
item |
update |
| DELETE |
item |
remove |
- Apply naming style
Verb-first:
listPlants
getPlant
updatePlant
removePlant
listPlantEvents
createPlantEvent
Resource-first:
plantsList
plantsGet
plantEventsList
- Singularize item resources
- Prefer
getPlant over getPlants
- Use a small irregular map (
people → person, children → child)
- If uncertain, keep plural and document it
- Collision handling
- Prefer param-based disambiguation (
getPlantById, getPlantBySlug)
- If unresolved, append a deterministic suffix (
get, post)
- Document all collisions and resolutions in
openapi-report.md
OpenAPI validation and report
Validate the spec and tolerate non-blocking issues.
Generate:
openapi-report.md
Include:
- Blocking issues
- Non-blocking issues
- Why each issue matters
- What assumptions were made
- A mapping table of generated method names to original
operationId (if present)
SDK runtime behavior
Transport
- Use native
fetch
- Isomorphic (Node and browser where possible)
- ESM only
Errors
Generate a typed ApiError with:
- status
- code
- message
- body
- headers
- method
- url
Throw ApiError for all non-2xx responses, except 304 handled by cache.
Retries
- Retry on network errors, 429, and 5xx
- Default max attempts: 3
- Exponential backoff with jitter
- Honor
Retry-After
- Allow per-request overrides
- Default to idempotent methods only; require explicit opt-in for POST/PATCH
Rate limiting
- In-memory limiter per client instance
- Respect rate-limit headers and Retry-After
Caching
- GET requests only by default
- In-memory cache with TTL
- Manual busting
- ETag / If-None-Match support
- Respect Cache-Control and Pragma
- Do not cache when Authorization is present unless explicitly enabled
Integration tests (optional)
If enabled:
- Generate real integration tests
- Disabled by default in CI
- Credentials loaded from environment variables
- Tests perform simple read operations and validate response shape
- Required env vars documented in README
If disabled:
- Generate mock-based tests only
Generated API surface
Each operation exports a flat async function:
listPlants(params, options?): Promise<Plant[]>
Options allow:
- Auth overrides
- Retry overrides
- Cache overrides
- AbortSignal
- Extra headers
Determinism
- Sort operations by path + method for stable output
- Keep naming stable across regenerations
- Record decisions in
sdk-decisions.md
Acceptance criteria
Output is correct only if:
- All required questions were asked
- Defaults were shown and explicitly confirmed
- Code generation occurred only after confirmation
- Names were derived from context, not operationId
- Types compile under strict TypeScript
- Retry, cache, and rate limiting work as specified
openapi-report.md and sdk-decisions.md exist
- Optional integration tests are safe and documented
- Output is deterministic
Defaults (proposed from OpenAPI spec analysis)
The agent must analyze the OpenAPI spec and propose intelligent defaults:
- Package name: Derive from
info.title (sanitized, kebab-case), fallback to openapi-sdk
- Client name: Derive from
info.title (sanitized, PascalCase + "Client"), fallback to ApiClient
- Base URL: First entry in OpenAPI
servers array, or empty string with warning if none
- Auth method: Analyze
security and components.securitySchemes to propose primary method (prefer: Bearer > API Key > OAuth2 > Basic > None)
- Naming style: verb-first (unless spec patterns suggest resource-first)
- Output mode: folder_only
- Output path: sdk/ (or derive from package name if standalone_package)
- Runtime target: nodeandbrowser (analyze operations to suggest if browser-only or node-only would be more appropriate)
- Node version: 22
- Cache enabled: For GET requests (analyze spec to see if caching headers are used)
- Retries: Enabled for idempotent methods (analyze operations to determine idempotency)
- Rate limiting: Enabled (check if spec includes rate limit headers/documentation)
- Integration tests: Disabled
Always show the reasoning behind each proposed default based on the spec analysis.