{"templateId":"../@theme/templates/BlogPost","sharedDataIds":{"blog-latest-posts":"blog-latest-posts"},"props":{"metadata":{"markdoc":{"tagList":[]},"redocly_category":"Blog","type":"markdown","template":"../@theme/templates/BlogPost","title":"Open-source, agent-friendly SDKs and tooling from OpenAPI description","description":"Meet redocly generate-client: one OpenAPI description becomes typed SDKs in TypeScript, Python, Go, and PHP — plus validation schemas, query hooks, test mocks, a CLI, and docs. Open source, no dependencies beyond each language's HTTP layer, and built for AI agents.","seo":{"title":"Open-source, agent-friendly SDKs and tooling from OpenAPI description","description":"Meet redocly generate-client: one OpenAPI description becomes typed SDKs in TypeScript, Python, Go, and PHP — plus validation schemas, query hooks, test mocks, a CLI, and docs. Open source, no dependencies beyond each language's HTTP layer, and built for AI agents."},"author":{"id":"roman-marshevskyi","name":"Roman Marshevskyi","authorBIO":"Director of Engineering, Redocly","image":"/assets/marshevskyi.642f924e188912716ccc91863cae9d6a4979994d1c5341d88c0bddaeffef614b.978384e4.png"},"publishedDate":"2026-09-01","categories":[{"category":{"id":"redocly","label":"Redocly"},"subcategory":{"id":"redocly-cli","label":"Redocly CLI"}},{"category":{"id":"api-specifications","label":"API specifications"},"subcategory":{"id":"openapi","label":"OpenAPI"}},{"category":{"id":"api-lifecycle","label":"API lifecycle"},"subcategory":{"id":"sdks","label":"SDKs"}}],"slug":"/blog/agent-friendly-sdks"},"seo":{"title":"Open-source, agent-friendly SDKs and tooling from OpenAPI description","description":"Meet redocly generate-client: one OpenAPI description becomes typed SDKs in TypeScript, Python, Go, and PHP — plus validation schemas, query hooks, test mocks, a CLI, and docs. Open source, no dependencies beyond each language's HTTP layer, and built for AI agents.","siteUrl":"https://redocly.com","image":"/assets/redocly-card.f670aae34a39545a5ea633a540cb3a4a333a1f23bb2ed3c4a1b17a5fbcf0ac85.db81178d.png","lang":"en-US"},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As agents become part of engineering teams, more of your API calls are written by one."," ","Agents hallucinate endpoints, invent response fields, and hand-write API code you then review line by line."," ","Generated code is the cheapest, safest code an agent can ship, so we built a generator that treats the agent as a first-class user."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/agent-friendly-sdks.3ffc9020015eaf21862af8cd43cf59f46b398b6e8fdccb3b43fd666f10e71a44.978384e4.svg","alt":"Diagram: one OpenAPI description flows through redocly generate-client into SDKs, schemas and mocks, a CLI, and docs"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Meet ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["generate-client"]},": a new command in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/Redocly/redocly-cli"},"children":["Redocly CLI"]},", powered by a new package, ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/Redocly/redocly-cli/tree/main/packages/client-generator"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["@redocly/client-generator"]}]},", that turns one OpenAPI description into typed SDKs in ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["TypeScript, Python, Go, and PHP"]},", plus validation schemas, TanStack Query and SWR hooks, test mocks, a ready-to-run ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["command-line interface"]},", and reference docs for all of it."," ","Both the command and the package are open source (MIT), and everything they generate is yours outright."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDKs are fully featured: auth, retries, middleware, pagination iterators, typed Server-Sent Events, query-string serialization, and multipart uploads, with no dependencies beyond each language's own HTTP layer."," ","The TypeScript client is built on web-standard ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fetch"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AbortController"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["URLSearchParams"]}," — ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["zero runtime dependencies"]},", emitted as code that imports nothing."," ","The API code your agent used to hallucinate becomes one deterministic command, and the compiler becomes its fact-checker: operation ids, parameters, and response fields are literal types, so a wrong call fails ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tsc"]}," with the exact operation named."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"up-and-running-in-three-steps","__idx":0},"children":["Up and running in three steps"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"1-start-from-your-api-description","__idx":1},"children":["1. Start from your API description"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The one you already have: OpenAPI 3.0, 3.1, 3.2, or Swagger 2.0."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"paths:\n  /menu-items:\n    get:\n      operationId: listMenuItems\n      parameters:\n        - name: limit\n          in: query\n          schema:\n            type: integer\n  /orders/{orderId}:\n    get:\n      operationId: getOrderById\n      security:\n        - BearerAuth: []\n      parameters:\n        - name: orderId\n          in: path\n          required: true\n          schema:\n            type: string\ncomponents:\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"2-run-one-command","__idx":2},"children":["2. Run one command"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["No account, no config file — every option has a default."," ","Customize with flags or a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["redocly.yaml"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client"]}," block when you need to."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest generate-client openapi.yaml --output src/client.ts\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3-call-your-api","__idx":3},"children":["3. Call your API"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every operation is a typed method on the generated client; every name comes from the description."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { client } from './client.js';\n\nclient.auth.bearer(token); // stored on this client; sent only with operations whose security requires it\n\nconst menu  = await client.listMenuItems({ query: { limit: 10 } });\nconst order = await client.getOrderById({ path: { orderId: 'ord_01khr…' } });\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["That's the whole client."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["One description. One command. Every time your API changes."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"the-features-youd-otherwise-hand-write","__idx":4},"children":["The features you'd otherwise hand-write"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Types are a third of the problem."," ","The behavior is what teams hand-write around generated types, and where drift starts."," ","The generated client includes it:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Auth from your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["securitySchemes"]}]},": bearer, basic, and API keys in header, query, or cookie, each sent only where an operation's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["security"]}," requires it. Credentials can be async token providers, resolved on every request, so refresh flows need no extra code. Every client instance carries its own."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pagination"]},": declare your pagination convention once (cursor, offset, page, or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Link"]}," header) and the iterators appear on the operation itself: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["listOrders.pages()"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["listOrders.items()"]},", typed, abortable, with duplicate-cursor loop detection. Delete your pagination loops."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Opt-in, abort-aware retries"]},": exponential backoff, jitter, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Retry-After"]},", idempotent-only by default, and a custom ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["retryOn"]}," predicate."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Typed Server-Sent Events"]},": an operation whose ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["text/event-stream"]}," becomes a typed async iterator with automatic reconnection, payloads typed from OpenAPI 3.2's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["itemSchema"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Composable middleware"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onRequest"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onResponse"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]},", with operation ids, paths, and tags visible to it as literal types."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The fiddly details, handled"]},": query parameters serialized exactly as the description declares, file uploads from a plain typed object, per-request timeouts, and idempotency keys that make retries safe."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Two error models"]},": exceptions by default, or a typed ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{ data, error }"]}," result if you prefer returns over throws."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["And it's strict on your behalf: a call with an argument the operation doesn't declare fails before the request leaves the process, with an error that names the operation and says where the argument belongs."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It reads OpenAPI ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["3.0, 3.1, and 3.2"]},", plus ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Swagger 2.0"]}," (normalized to 3.x before generation)."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"skills-first","__idx":5},"children":["Skills first"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every part of this tool assumes an agent will operate it, and each of those decisions helps the humans just as much:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The design ships as agent skills."]}," A skill is a short instruction file that AI agents (such as Claude Code) load before touching related code: it states what a piece of code is for, the rules it must follow, and how to change it safely. Every generator carries its own design document as a skill, and ejecting a generator drops both into your repo beside the generator source:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"redocly eject-generator zod\n\ngenerators/zod/…                           # the generator source, now yours\ngenerators/AGENTS.md                       # pointer that leads agents to the skills\n.claude/skills/client-generators/SKILL.md  # the shared authoring guide\n.claude/skills/zod-generator/SKILL.md      # why this generator is built the way it is\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An agent asked to change generated output loads the rules first and edits the generator, not the output."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A discoverable surface instead of prose."]}," The generated CLI answers ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--help"]}," with its commands and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["schema <command>"]}," with one operation's whole contract as JSON: method, path, parameters with types, request and response schemas. An agent learns a real API in two commands."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Feedback an agent can act on."]}," Strict types plus runtime unknown-argument errors name the operation and say where the argument belongs."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Deterministic ground truth."]}," The generated mocks are seeded and offline, so tests an agent writes reproduce exactly, with no live API in the loop teaching it wrong lessons."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Regeneration over hand-editing."]}," The client is machine-owned and rebuilt from the description; the generator is human-owned and ejectable. That split tells an agent exactly which file it is allowed to change."]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The whole instruction your agents need is one sentence: ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["never hand-write HTTP code for our APIs - regenerate the client and import the functions, and a wrong call fails the build."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"one-description-every-consumer","__idx":6},"children":["One description, every consumer"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The vocabulary is simple: you select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["generators"]}," in one list, each generator emits an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["artifact"]},", and each artifact serves a different consumer of your API."," ","The SDK is one kind of artifact; here is the whole list, produced from one parse of your description in one command:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Generator"},"children":["Generator"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Artifact"},"children":["Artifact"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Consumer"},"children":["Consumer"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["typescript"]}," (default), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["python"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["go"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["php"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["the full typed client, in that language"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["calling your API from any stack"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["zod"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Zod schemas + validation middleware"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["runtime contract checks"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tanstack-query"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["swr"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["query and mutation factories, hooks"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["React, Vue, Svelte, Solid data fetching"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mock"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["MSW v2 handlers + typed data factories"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["tests and demos, offline and deterministic"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transformers"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Date"]}," converters"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["ISO strings → ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Date"]},", paired with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--date-type Date"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cli"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["a bin-ready command-line interface"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["scripts, CI, agents"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["your own"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["anything"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["the long tail"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every language SDK carries the same behavior, each as a single self-contained file: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["httpx"]}," for Python, the standard library for Go, the curl extension for PHP."," ","And names resolve once: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["listOrders"]}," is the operation in the description, the function in every SDK, and the CLI command, so one identifier greps across your whole stack."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Docs are one flag: add ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--docs"]}," and every selected generator writes a reference page beside its output."," ","The docs regenerate with the code, so they cannot drift from it."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"and-if-you-disagree-with-a-built-in-take-it","__idx":7},"children":["And if you disagree with a built-in, take it"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a tool gets something wrong for you, the traditional move is to fork it, and a fork is a life sentence: you maintain the whole project from that day on, and upstream fixes stop reaching you."," ","Eject gives you the ownership without the fork:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest eject-generator python\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["That copies the built-in generator into your repository as ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["TypeScript source you own"]},": a folder with one readable file per stage (naming, types, models, operations, pagination, client)."," ","It wires your config to it, and, unmodified, it produces byte-identical output."," ","We verify that byte-identity in our test suite."," ","Later versions merge into your copy file by file with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--update"]},"."," ","The generator's design document arrives with it as an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["agent skill in your repo"]},", and the skill is yours to manage: edit it to state your house rules (naming, headers, error style, whatever the built-in got wrong for you), and your AI agent reads the skill first and changes the ejected generator to match."," ","You maintain a short design document; the agent maintains the code to it."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"yours-to-shape","__idx":8},"children":["Yours to shape"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Call style"]},": grouped inputs by default; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--args-style flat"]}," merges them into one object when an operation's inputs can't collide."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Output layout"]},": one ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["single"]}," file (default), or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["split"]}," with schema types in a sibling module."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Runtime placement"]},": inlined into the client by default for a truly single-file artifact, or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--runtime module"]}," to write the runtime as real, readable files beside it, shared between clients."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["No build step, if you want none"]},": with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--import-ext ts"]},", the generated client, the zod module, and the CLI run as they are under plain Node 22.18+, which strips the types itself."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Configuration"]},": CLI flags or a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client"]}," block in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["redocly.yaml"]},", with per-API overrides for monorepos that generate several clients from one config."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"proven-on-ourselves-first","__idx":9},"children":["Proven on ourselves first"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We didn't design this in the abstract: Redocly's own platform runs on this generator: four internal APIs, hundreds of operations, an in-house codegen deleted in the process, and much of the migration executed by an AI agent working against the generated client."," ","That migration found real bugs our tests had missed, and it's the subject of the next post."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A quick caveat: the command is still experimental, so flags and output may change between releases — pin your CLI version."," ","The code it generates is strict-TypeScript clean, exhaustively tested, and already carrying Redocly's production traffic."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"try-it","__idx":10},"children":["Try it"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["One command, no account, runs entirely on your machine:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest generate-client openapi.yaml --output src/client.ts\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Then import a function and call your API."," ","The whole client is in the file you just generated."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/cli/commands/generate-client"},"children":["Command reference"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/cli/guides/customize-client-generation"},"children":["Write a custom generator"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples"},"children":["Runnable examples"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/Redocly/redocly-cli"},"children":["GitHub"]}]}]}]},"headings":[{"value":"Up and running in three steps","id":"up-and-running-in-three-steps","depth":2},{"value":"1. Start from your API description","id":"1-start-from-your-api-description","depth":3},{"value":"2. Run one command","id":"2-run-one-command","depth":3},{"value":"3. Call your API","id":"3-call-your-api","depth":3},{"value":"The features you'd otherwise hand-write","id":"the-features-youd-otherwise-hand-write","depth":2},{"value":"Skills first","id":"skills-first","depth":2},{"value":"One description, every consumer","id":"one-description-every-consumer","depth":2},{"value":"And if you disagree with a built-in, take it","id":"and-if-you-disagree-with-a-built-in-take-it","depth":2},{"value":"Yours to shape","id":"yours-to-shape","depth":2},{"value":"Proven on ourselves first","id":"proven-on-ourselves-first","depth":2},{"value":"Try it","id":"try-it","depth":2}],"frontmatter":{"template":"../@theme/templates/BlogPost","title":"Open-source, agent-friendly SDKs and tooling from OpenAPI description","description":"Meet redocly generate-client: one OpenAPI description becomes typed SDKs in TypeScript, Python, Go, and PHP — plus validation schemas, query hooks, test mocks, a CLI, and docs. Open source, no dependencies beyond each language's HTTP layer, and built for AI agents.","seo":{"title":"Open-source, agent-friendly SDKs and tooling from OpenAPI description","description":"Meet redocly generate-client: one OpenAPI description becomes typed SDKs in TypeScript, Python, Go, and PHP — plus validation schemas, query hooks, test mocks, a CLI, and docs. Open source, no dependencies beyond each language's HTTP layer, and built for AI agents."},"author":"roman-marshevskyi","publishedDate":"2026-09-01","categories":["redocly:redocly-cli","api-specifications:openapi","api-lifecycle:sdks"]},"lastModified":"2026-09-02T01:58:19.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/blog/agent-friendly-sdks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}