@kubb/plugin-mcp 5.0.0-beta.4 → 5.0.0-beta.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/extension.yaml CHANGED
@@ -2,18 +2,18 @@ $schema: https://kubb.dev/schemas/extension.json
2
2
  kind: plugin
3
3
  id: plugin-mcp
4
4
  name: MCP
5
- description: Generate Model Context Protocol (MCP) servers from OpenAPI specifications for AI assistants to call your API.
5
+ description: Generate a Model Context Protocol (MCP) server from OpenAPI so AI assistants like Claude can call your API as tools.
6
6
  category: ai
7
7
  type: official
8
- npmPackage: "@kubb/plugin-mcp"
8
+ npmPackage: '@kubb/plugin-mcp'
9
9
  docsPath: /plugins/plugin-mcp
10
10
  repo: https://github.com/kubb-labs/plugins
11
11
  maintainers:
12
12
  - name: Stijn Van Hulle
13
13
  github: stijnvanhulle
14
14
  compatibility:
15
- kubb: ">=5.0.0"
16
- node: ">=22"
15
+ kubb: '>=5.0.0'
16
+ node: '>=22'
17
17
  tags:
18
18
  - mcp
19
19
  - model-context-protocol
@@ -39,243 +39,496 @@ icon:
39
39
  intro: |
40
40
  # @kubb/plugin-mcp
41
41
 
42
- Generate [Model Context Protocol](https://modelcontextprotocol.io/introduction) servers from your OpenAPI schema. AI assistants like Claude can now call your API through conversational interfaces.
42
+ Generate a [Model Context Protocol](https://modelcontextprotocol.io/introduction) server straight from your OpenAPI spec. Every operation becomes a typed MCP tool that AI assistants (Claude Desktop, Claude Code, MCP-compatible clients) can call directly.
43
+
44
+ The plugin emits the tool definitions, the request handlers, and the Zod schemas they validate against. Plug the generated server into Claude with one config file.
43
45
 
44
46
  ```mermaid
45
47
  graph TD
46
- A[Kubb<br/>Generates code from OpenAPI] --> B[MCP Server<br/>Handles tool calls]
47
- B --> C[Claude<br/>Conversational AI]
48
- C -->|Sends tool request| B
49
- B -->|Uses generated code| A
48
+ A[OpenAPI spec] --> B[Kubb<br/>code generation]
49
+ B --> C[MCP server<br/>tool registry]
50
+ C --> D[Claude / MCP client]
51
+ D -->|Tool call| C
52
+ C -->|HTTP request| E[Your API]
50
53
  ```
51
54
  notes:
52
55
  - type: tip
53
56
  body: |
54
- See [Setup Claude with Kubb](https://modelcontextprotocol.io/docs/tools/claude-desktop) for configuration instructions.
57
+ See [Connect Claude to a remote MCP server](https://modelcontextprotocol.io/docs/tools/claude-desktop) for connecting the generated server.
55
58
  options:
56
59
  - name: output
57
60
  type: Output
58
61
  required: false
59
- default: "{ path: 'mcp', barrelType: 'named' }"
60
- description: Specify the export location for the files and define the behavior of the output.
62
+ default: "{ path: 'mcp', barrel: { type: 'named' } }"
63
+ description: Where the generated MCP tool handlers are written and how they are exported.
61
64
  properties:
62
65
  - name: path
63
66
  type: string
64
67
  required: true
65
- description: Output directory or file for the generated code, relative to the global `output.path`.
68
+ description: |
69
+ Folder (or single file) where the plugin writes its generated code. The path is resolved against the global `output.path` set on `defineConfig`.
70
+
71
+ Use a folder to keep each generator's output isolated (`'types'`, `'clients'`, `'hooks'`). Use a single file when you want everything in one place, for example `'api.ts'`.
66
72
  tip: |
67
- if `output.path` is a file, `group` cannot be used.
73
+ When `output.path` points to a single file, the `group` option cannot be used because every operation ends up in the same file.
74
+ examples:
75
+ - name: kubb.config.ts
76
+ files:
77
+ - lang: typescript
78
+ twoslash: false
79
+ code: |
80
+ import { defineConfig } from 'kubb'
81
+ import { pluginTs } from '@kubb/plugin-ts'
82
+
83
+ export default defineConfig({
84
+ input: { path: './petStore.yaml' },
85
+ output: { path: './src/gen' },
86
+ plugins: [
87
+ pluginTs({
88
+ output: { path: './types' },
89
+ }),
90
+ ],
91
+ })
92
+ - name: Resulting tree
93
+ files:
94
+ - lang: text
95
+ twoslash: false
96
+ code: |
97
+ src/
98
+ └── gen/
99
+ └── types/
100
+ ├── Pet.ts
101
+ └── Store.ts
68
102
  default: "'mcp'"
69
- - name: barrelType
70
- type: "'all' | 'named' | 'propagate' | false"
103
+ - name: barrel
104
+ type: "{ type: 'named' | 'all', nested?: boolean } | false"
71
105
  required: false
72
- default: "'named'"
73
- description: Specify what to export and optionally disable barrel-file generation.
106
+ default: "{ type: 'named' }"
107
+ description: |
108
+ Controls how the generated `index.ts` (barrel) file re-exports the plugin's output.
109
+
110
+ - `{ type: 'named' }` re-exports each symbol by name. Best for tree-shaking and explicit imports.
111
+ - `{ type: 'all' }` uses `export *`. Smaller barrel file, but exports everything.
112
+ - `{ nested: true }` creates a barrel in every subdirectory, so callers can import from any depth.
113
+ - `false` skips the barrel entirely. The plugin's files are also excluded from the root `index.ts`.
74
114
  tip: |
75
- Using `propagate` will prevent a plugin from creating a barrel file, but it will still propagate, allowing [`output.barrelType`](https://kubb.dev/docs/5.x/configuration#output-barreltype) to export the specific function or type.
115
+ Pick `'named'` when consumers care about which symbols they import (better tree-shaking, friendlier auto-import). Pick `'all'` when the file count is small and you want a one-line barrel.
76
116
  examples:
77
- - name: all
117
+ - name: "'named' (default)"
78
118
  files:
79
- - lang: typescript
119
+ - name: kubb.config.ts
120
+ lang: typescript
121
+ twoslash: false
80
122
  code: |
81
- export * from './gen/petService.ts'
123
+ import { defineConfig } from 'kubb'
124
+ import { pluginTs } from '@kubb/plugin-ts'
125
+
126
+ export default defineConfig({
127
+ input: { path: './petStore.yaml' },
128
+ output: { path: './src/gen' },
129
+ plugins: [
130
+ pluginTs({
131
+ output: { barrel: { type: 'named' } },
132
+ }),
133
+ ],
134
+ })
135
+ - name: src/gen/types/index.ts
136
+ lang: typescript
82
137
  twoslash: false
83
- - name: named
138
+ code: |
139
+ export { Pet, PetStatus } from './Pet'
140
+ export { Store } from './Store'
141
+ - name: "'all'"
84
142
  files:
85
- - lang: typescript
143
+ - name: kubb.config.ts
144
+ lang: typescript
145
+ twoslash: false
86
146
  code: |
87
- export { PetService } from './gen/petService.ts'
147
+ import { defineConfig } from 'kubb'
148
+ import { pluginTs } from '@kubb/plugin-ts'
149
+
150
+ export default defineConfig({
151
+ input: { path: './petStore.yaml' },
152
+ output: { path: './src/gen' },
153
+ plugins: [
154
+ pluginTs({
155
+ output: { barrel: { type: 'all' } },
156
+ }),
157
+ ],
158
+ })
159
+ - name: src/gen/types/index.ts
160
+ lang: typescript
88
161
  twoslash: false
89
- - name: propagate
162
+ code: |
163
+ export * from './Pet'
164
+ export * from './Store'
165
+ - name: nested
90
166
  files:
91
- - lang: typescript
92
- code: ""
167
+ - name: kubb.config.ts
168
+ lang: typescript
93
169
  twoslash: false
94
- - name: "false"
170
+ code: |
171
+ import { defineConfig } from 'kubb'
172
+ import { pluginTs } from '@kubb/plugin-ts'
173
+
174
+ export default defineConfig({
175
+ input: { path: './petStore.yaml' },
176
+ output: { path: './src/gen' },
177
+ plugins: [
178
+ pluginTs({
179
+ output: { barrel: { type: 'named', nested: true } },
180
+ }),
181
+ ],
182
+ })
183
+ - name: Generated tree
184
+ lang: text
185
+ twoslash: false
186
+ code: |
187
+ src/gen/types/
188
+ ├── index.ts # re-exports ./petController and ./storeController
189
+ ├── petController/
190
+ │ ├── index.ts # re-exports Pet, Store, ...
191
+ │ └── Pet.ts
192
+ └── storeController/
193
+ ├── index.ts
194
+ └── Store.ts
195
+ - name: 'false'
95
196
  files:
96
- - lang: typescript
97
- code: ""
197
+ - name: kubb.config.ts
198
+ lang: typescript
98
199
  twoslash: false
200
+ code: |
201
+ import { defineConfig } from 'kubb'
202
+ import { pluginTs } from '@kubb/plugin-ts'
203
+
204
+ export default defineConfig({
205
+ input: { path: './petStore.yaml' },
206
+ output: { path: './src/gen' },
207
+ plugins: [
208
+ pluginTs({
209
+ output: { barrel: false },
210
+ }),
211
+ ],
212
+ })
213
+ - name: Result
214
+ lang: text
215
+ twoslash: false
216
+ code: |
217
+ # No index.ts is generated for this plugin.
218
+ # Its files are also excluded from the root index.ts.
99
219
  - name: banner
100
- type: "string | ((node: RootNode) => string)"
220
+ type: 'string | ((node: RootNode) => string)'
101
221
  required: false
102
- description: Add a banner comment at the top of every generated file. Accepts a static string or a function that receives the `RootNode` and returns a string.
222
+ description: |
223
+ Text prepended to every generated file. Useful for license headers, lint disables, or `@ts-nocheck` directives.
224
+
225
+ Pass a string for a static banner. Pass a function to compute the banner from each file's `RootNode` (the AST root containing path, schema, and operation context).
226
+ examples:
227
+ - name: Static banner
228
+ files:
229
+ - name: kubb.config.ts
230
+ lang: typescript
231
+ twoslash: false
232
+ code: |
233
+ import { defineConfig } from 'kubb'
234
+ import { pluginTs } from '@kubb/plugin-ts'
235
+
236
+ export default defineConfig({
237
+ input: { path: './petStore.yaml' },
238
+ output: { path: './src/gen' },
239
+ plugins: [
240
+ pluginTs({
241
+ output: {
242
+ banner: '/* eslint-disable */\n// @ts-nocheck',
243
+ },
244
+ }),
245
+ ],
246
+ })
247
+ - name: Generated file
248
+ lang: typescript
249
+ twoslash: false
250
+ code: |
251
+ /* eslint-disable */
252
+ // @ts-nocheck
253
+ export type Pet = {
254
+ id: number
255
+ name: string
256
+ }
257
+ - name: Dynamic banner
258
+ files:
259
+ - name: kubb.config.ts
260
+ lang: typescript
261
+ twoslash: false
262
+ code: |
263
+ import { defineConfig } from 'kubb'
264
+ import { pluginTs } from '@kubb/plugin-ts'
265
+
266
+ export default defineConfig({
267
+ input: { path: './petStore.yaml' },
268
+ output: { path: './src/gen' },
269
+ plugins: [
270
+ pluginTs({
271
+ output: {
272
+ banner: (node) => `// Source: ${node.path}\n// Generated at ${new Date().toISOString()}`,
273
+ },
274
+ }),
275
+ ],
276
+ })
103
277
  - name: footer
104
- type: "string | ((node: RootNode) => string)"
278
+ type: 'string | ((node: RootNode) => string)'
105
279
  required: false
106
- description: Add a footer comment at the end of every generated file. Accepts a static string or a function that receives the `RootNode` and returns a string.
280
+ description: |
281
+ Text appended at the end of every generated file. The mirror of `banner` — use it for closing comments, re-enabling lint rules, or marker lines.
282
+
283
+ Pass a string for a static footer, or a function that receives the file's `RootNode` and returns the footer text.
284
+ examples:
285
+ - name: Re-enable lint after a banner disable
286
+ files:
287
+ - name: kubb.config.ts
288
+ lang: typescript
289
+ twoslash: false
290
+ code: |
291
+ import { defineConfig } from 'kubb'
292
+ import { pluginTs } from '@kubb/plugin-ts'
293
+
294
+ export default defineConfig({
295
+ input: { path: './petStore.yaml' },
296
+ output: { path: './src/gen' },
297
+ plugins: [
298
+ pluginTs({
299
+ output: {
300
+ banner: '/* eslint-disable */',
301
+ footer: '/* eslint-enable */',
302
+ },
303
+ }),
304
+ ],
305
+ })
107
306
  - name: override
108
307
  type: boolean
109
308
  required: false
110
- default: "false"
111
- description: Whether Kubb overrides existing external files that can be generated if they already exist.
309
+ default: 'false'
310
+ description: |
311
+ Allows the plugin to overwrite hand-written files that share a name with a generated file.
312
+
313
+ - `false` (default): Kubb skips a file if it already exists and is not marked as generated. This protects manual edits.
314
+ - `true`: Kubb overwrites any file at the target path, including hand-written ones.
315
+ warning: |
316
+ Enable this only when you are sure the target folder contains nothing you need to keep. Local edits are lost on the next generation.
317
+ examples:
318
+ - name: kubb.config.ts
319
+ files:
320
+ - lang: typescript
321
+ twoslash: false
322
+ code: |
323
+ import { defineConfig } from 'kubb'
324
+ import { pluginTs } from '@kubb/plugin-ts'
325
+
326
+ export default defineConfig({
327
+ input: { path: './petStore.yaml' },
328
+ output: { path: './src/gen' },
329
+ plugins: [
330
+ pluginTs({
331
+ output: { override: true },
332
+ }),
333
+ ],
334
+ })
112
335
  - name: resolver
113
336
  type: Partial<ResolverMcp> & ThisType<ResolverMcp>
114
337
  required: false
115
338
  description: |
116
- A single resolver that overrides the naming and path-resolution conventions. Each method you provide replaces the corresponding built-in one. When a method returns `null` or `undefined`, the resolver's result is used as the fallback, so you only need to supply the methods you want to change.
339
+ Overrides how the plugin builds names and paths for generated files and symbols. Use this to add prefixes, suffixes, or to swap the casing strategy without forking the plugin.
340
+
341
+ Only override the methods you want to change. Anything you omit falls back to the plugin's default resolver. A method that returns `null` or `undefined` also falls back.
117
342
 
118
- `this` inside each method is bound to the **full** resolver, so you can call other resolver methods (e.g. `this.default(name, 'function')`) without losing context.
343
+ Inside each method, `this` is bound to the full resolver, so you can call `this.default(name, 'function')` to delegate to the built-in implementation.
119
344
  body: |
120
- Each plugin ships a built-in resolver:
345
+ Each plugin ships with a default resolver:
121
346
 
122
347
  | Plugin | Default resolver |
123
348
  | --- | --- |
124
349
  | `@kubb/plugin-ts` | `resolverTs` |
125
350
  | `@kubb/plugin-zod` | `resolverZod` |
351
+ | `@kubb/plugin-faker` | `resolverFaker` |
126
352
  | `@kubb/plugin-cypress` | `resolverCypress` |
353
+ | `@kubb/plugin-msw` | `resolverMsw` |
127
354
  | `@kubb/plugin-mcp` | `resolverMcp` |
355
+ | `@kubb/plugin-client` | `resolverClient` |
128
356
  codeBlock:
129
357
  lang: typescript
130
- title: Custom resolver (plugin-mcp example)
358
+ title: Prefix every tool name with "Mcp"
131
359
  code: |
360
+ import { defineConfig } from 'kubb'
132
361
  import { pluginMcp } from '@kubb/plugin-mcp'
133
362
 
134
- pluginMcp({
135
- resolver: {
136
- resolveName(name) {
137
- return `Mcp${this.default(name, 'function')}`
138
- },
139
- },
363
+ export default defineConfig({
364
+ input: { path: './petStore.yaml' },
365
+ output: { path: './src/gen' },
366
+ plugins: [
367
+ pluginMcp({
368
+ resolver: {
369
+ resolveName(name) {
370
+ return `Mcp${this.default(name, 'function')}`
371
+ },
372
+ },
373
+ }),
374
+ ],
140
375
  })
141
376
  tip: |
142
- Use `resolver` for fine-grained control over naming conventions.
377
+ Use `resolver` for naming and file-location tweaks. For changing the AST nodes themselves (e.g. stripping descriptions), use `transformer` instead.
143
378
  - name: group
144
379
  type: Group
145
380
  required: false
146
381
  description: |
147
- Grouping combines files in a folder based on a specific `type`.
382
+ Splits generated files into subfolders based on the operation's tag, so each tag in your OpenAPI spec gets its own directory.
383
+
384
+ Without `group`, every file lands in the plugin's `output.path` folder. With `group`, files are bucketed under `{output.path}/{groupName}/`, where `groupName` is derived from the operation's first tag.
385
+ tip: |
386
+ Use `group` to mirror your API's domain structure (pet, store, user) in the generated code. Combine it with `output.barrel: { type: 'named', nested: true }` to get per-tag barrel files.
148
387
  examples:
149
388
  - name: kubb.config.ts
150
389
  files:
151
390
  - lang: typescript
152
- code: |
153
- group: {
154
- type: 'tag',
155
- name({ group }) {
156
- return `${group}Controller`
157
- }
158
- }
159
391
  twoslash: false
392
+ code: |
393
+ import { defineConfig } from 'kubb'
394
+ import { pluginTs } from '@kubb/plugin-ts'
395
+
396
+ export default defineConfig({
397
+ input: { path: './petStore.yaml' },
398
+ output: { path: './src/gen' },
399
+ plugins: [
400
+ pluginTs({
401
+ group: {
402
+ type: 'tag',
403
+ name: ({ group }) => `${group}Controller`,
404
+ },
405
+ }),
406
+ ],
407
+ })
160
408
  body: |
161
409
  With the configuration above, the generator emits:
162
410
 
163
411
  ```text
164
- .
165
- ├── src/
166
- └── petController/
167
- │ ├── addPet.ts
168
- │ │ └── getPet.ts
169
- │ └── storeController/
170
- │ ├── createStore.ts
171
- │ └── getStoreById.ts
172
- ├── petStore.yaml
173
- ├── kubb.config.ts
174
- └── package.json
412
+ src/gen/
413
+ ├── petController/
414
+ ├── AddPet.ts
415
+ └── GetPet.ts
416
+ └── storeController/
417
+ ├── CreateStore.ts
418
+ └── GetStoreById.ts
175
419
  ```
176
420
  properties:
177
421
  - name: type
178
422
  type: "'tag'"
179
423
  required: true
180
- description: Specify the property to group files by. Required when `group` is defined.
424
+ description: |
425
+ Property used to assign each operation to a group. Required whenever `group` is set.
426
+
427
+ Today only `'tag'` is supported: Kubb reads the first tag on the operation (`operation.getTags().at(0)?.name`) and uses it as the group key. Operations without a tag are placed in a default group.
181
428
  note: |
182
- `Required: true*` means this is required only when the `group` option is used. The `group` option itself is optional.
183
- body: |
184
- - `'tag'`: Uses the first tag from `operation.getTags().at(0)?.name`
429
+ `Required: true*` is conditional only required when the parent `group` option is used. `group` itself stays optional.
185
430
  - name: name
186
- type: "(context: GroupContext) => string"
431
+ type: '(context: GroupContext) => string'
187
432
  required: false
188
433
  default: (ctx) => `${ctx.group}Requests`
189
- description: Return the name of a group based on the group name. This is used for file and identifier generation.
434
+ description: Function that builds the folder/identifier name from a group key (the operation's first tag).
190
435
  - name: paramsCasing
191
436
  type: "'camelcase'"
192
437
  required: false
193
- description: Transform parameter names to camelCase in generated MCP handlers.
438
+ description: |
439
+ Renames parameter properties in the generated MCP handlers. The HTTP layer still uses the original spec names — Kubb adds the mapping for you.
194
440
  important: |
195
- When using `paramsCasing`, ensure that `@kubb/plugin-ts` also has the same `paramsCasing` setting. This option automatically maps transformed parameter names back to their original API names in HTTP requests.
196
- body: |
197
- - `'camelcase'` transforms parameter names to camelCase.
441
+ Set the same `paramsCasing` here as on `@kubb/plugin-ts` so the handlers' parameter types line up with the generated request types.
198
442
  examples:
199
- - name: With paramsCasing camelcase
443
+ - name: "With `paramsCasing: 'camelcase'`"
200
444
  files:
201
445
  - lang: typescript
446
+ twoslash: false
202
447
  code: |
203
- // Handler uses camelCase parameters
448
+ // Handler signature uses camelCase
204
449
  export async function findPetsByStatusHandler({
205
- stepId // ✓ camelCase
450
+ stepId,
206
451
  }: {
207
452
  stepId: FindPetsByStatusPathParams['stepId']
208
- }): Promise<Promise<CallToolResult>> {
209
- // Automatically maps back to original name
453
+ }): Promise<CallToolResult> {
210
454
  const step_id = stepId
211
455
 
212
456
  const res = await fetch({
213
457
  method: 'GET',
214
- url: `/pet/findByStatus/${step_id}`, // Uses original API name
215
- ...
458
+ url: `/pet/findByStatus/${step_id}`,
216
459
  })
217
- ...
460
+ // ...
218
461
  }
219
- twoslash: false
220
- - name: Without paramsCasing
462
+ - name: Without `paramsCasing`
221
463
  files:
222
464
  - lang: typescript
465
+ twoslash: false
223
466
  code: |
224
- // Handler uses original API naming
225
467
  export async function findPetsByStatusHandler({
226
- step_id // Original naming
468
+ step_id,
227
469
  }: {
228
470
  step_id: FindPetsByStatusPathParams['step_id']
229
- }): Promise<Promise<CallToolResult>> {
471
+ }): Promise<CallToolResult> {
230
472
  const res = await fetch({
231
473
  method: 'GET',
232
474
  url: `/pet/findByStatus/${step_id}`,
233
- ...
234
475
  })
235
- ...
476
+ // ...
236
477
  }
237
- twoslash: false
238
478
  - name: client
239
479
  type: ClientImportPath & { clientType?, dataReturnType?, baseURL?, bundle?, paramsCasing? }
240
480
  required: false
241
- description: Client configuration for HTTP request generation, including client type, data return type, base URL, and bundling options.
481
+ description: |
482
+ HTTP client used by each MCP handler to call the underlying API. Mirrors a subset of `pluginClient` options.
242
483
  properties:
243
484
  - name: importPath
244
485
  type: string
245
486
  required: false
246
- description: Path to the client used for API calls. Supports both relative and absolute paths.
487
+ description: |
488
+ Path or module specifier of a custom client module. Generated code imports its HTTP runtime from here instead of `@kubb/plugin-client/clients/{client}`.
489
+
490
+ Use this when you need to inject auth headers, add interceptors, change the base URL at runtime, or wrap a different HTTP library (ky, ofetch, ...). Both relative paths (`./src/client.ts`) and bare specifiers (`@my-org/api-client`) work.
247
491
  details:
248
492
  - title: When to use `importPath`
249
493
  body: |
250
- Use `importPath` when you want to:
494
+ Reach for a custom client when you need to:
251
495
 
252
- - **Customize the HTTP client**: Provide your own client implementation with custom configurations (e.g., baseURL, headers, interceptors)
253
- - **Add authentication**: Include authentication tokens or other security mechanisms in your client
254
- - **Override default behavior**: Replace the default Kubb client with your own implementation
496
+ - Add an auth token to every request.
497
+ - Plug in interceptors, retries, or logging.
498
+ - Configure `baseURL` and headers from environment variables.
499
+ - Wrap a library other than `axios`/`fetch`.
255
500
  - title: Default behavior
256
501
  body: |
257
- When `importPath` is not specified:
502
+ Without `importPath`:
503
+
504
+ - `bundle: false` (default) — generated code imports from `@kubb/plugin-client/clients/{axios|fetch}`.
505
+ - `bundle: true` — Kubb writes `.kubb/client.ts` and generated code imports from there.
506
+ - title: Required exports
507
+ body: |
508
+ The module pointed to by `importPath` must satisfy the same shape as the built-in client. At minimum:
258
509
 
259
- - If `bundle: false` (default): Uses `@kubb/plugin-client/clients/${client}` where client is either `axios` or `fetch`.
260
- - If `bundle: true`: Bundles the client into `.kubb/fetch.ts`.
261
- - title: Import structure
262
- body: "Generated code imports the client as a default import and the runtime types as named type imports:"
510
+ - A default export of the `client` function.
511
+ - A `RequestConfig` type.
512
+ - A `ResponseErrorConfig` type.
513
+
514
+ When used together with a query plugin (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`), it must also export a `Client` type alias.
515
+ - title: How generated files import it
516
+ body: |
517
+ Generated code imports the client as a default import (bound to the local name `client`) and the runtime types as named type imports:
263
518
  codeBlock:
264
519
  lang: typescript
265
- code: |-
266
- /**
267
- * Generated by Kubb (https://kubb.dev/).
268
- * Do not edit manually.
269
- */
520
+ code: |
270
521
  import client from '${client.importPath}'
271
522
  import type { RequestConfig, ResponseErrorConfig } from '${client.importPath}'
272
- // ... rest of generated file
523
+ // ... rest of the generated file
273
524
  important: |
274
- When using `importPath` with query plugins such as `@kubb/plugin-react-query` or `@kubb/plugin-vue-query`, the generated hooks also import a `Client` type alongside `RequestConfig` and `ResponseErrorConfig` from the custom module. Your custom client module **must** export all three typesif any is missing, TypeScript will report an unresolvable import error.
525
+ When used with query plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`), generated hooks also import a `Client` type alias. Your module **must** export `Client`, `RequestConfig`, and `ResponseErrorConfig` — TypeScript will fail the import otherwise.
275
526
  codeBlock:
276
527
  lang: typescript
277
- title: client.ts
528
+ title: src/client.ts
278
529
  code: |
530
+ import axios from 'axios'
531
+
279
532
  export type RequestConfig<TData = unknown> = {
280
533
  url?: string
281
534
  method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE'
@@ -294,153 +547,374 @@ options:
294
547
 
295
548
  export type ResponseErrorConfig<TError = unknown> = TError
296
549
 
297
- // The Client type alias is required when using query plugins
550
+ // Required when used with @kubb/plugin-react-query or @kubb/plugin-vue-query
298
551
  export type Client = <TData, _TError = unknown, TVariables = unknown>(
299
- config: RequestConfig<TVariables>
552
+ config: RequestConfig<TVariables>,
300
553
  ) => Promise<ResponseConfig<TData>>
301
554
 
302
- export const client: Client = async (config) => { /* ... */ }
555
+ const client: Client = async (config) => {
556
+ const response = await axios.request<TData>({
557
+ ...config,
558
+ headers: {
559
+ Authorization: `Bearer ${process.env.API_TOKEN}`,
560
+ ...config.headers,
561
+ },
562
+ })
563
+
564
+ return response
565
+ }
566
+
303
567
  export default client
304
568
  examples:
305
- - name: kubb.config.ts
569
+ - name: Wire up a custom client
306
570
  files:
307
- - lang: typescript
571
+ - name: kubb.config.ts
572
+ lang: typescript
573
+ twoslash: false
308
574
  code: |
309
575
  import { defineConfig } from 'kubb'
310
576
  import { pluginClient } from '@kubb/plugin-client'
311
577
 
312
578
  export default defineConfig({
313
- // ...
579
+ input: { path: './petStore.yaml' },
580
+ output: { path: './src/gen' },
314
581
  plugins: [
315
582
  pluginClient({
316
- importPath: './src/client.ts', // Path to your custom client
583
+ importPath: './src/client.ts',
317
584
  }),
318
585
  ],
319
586
  })
320
- twoslash: false
321
587
  tip: |
322
- Learn more about defining a custom client [here](https://kubb.dev/plugins/plugin-client#importpath).
588
+ See the [custom client guide](https://kubb.dev/plugins/plugin-client#importpath) for a worked example.
323
589
  - name: dataReturnType
324
590
  type: "'data' | 'full'"
325
591
  required: false
326
592
  default: "'data'"
327
593
  description: |
328
- Return type used when calling the client.
594
+ Shape of the value returned from each generated client function.
329
595
 
330
- - `'data'` returns `ResponseConfig['data']`.
331
- - `'full'` returns the full `ResponseConfig`.
596
+ - `'data'` returns only the response body (`response.data`). Concise and matches what most apps need.
597
+ - `'full'` returns the full response config — body, status code, headers, and the original request. Use this when callers need to inspect headers or status.
332
598
  examples:
333
- - name: data
599
+ - name: "'data' (default)"
334
600
  files:
335
- - lang: typescript
601
+ - name: getPetById.ts
602
+ lang: typescript
603
+ twoslash: false
336
604
  code: |
337
605
  export async function getPetById<TData>(
338
606
  petId: GetPetByIdPathParams,
339
607
  ): Promise<ResponseConfig<TData>['data']> {
340
- ...
608
+ // ...
341
609
  }
610
+ - name: usage.ts
611
+ lang: typescript
342
612
  twoslash: false
343
- - name: full
613
+ code: |
614
+ const pet = await getPetById(1)
615
+ // ^? Pet
616
+ - name: "'full'"
344
617
  files:
345
- - lang: typescript
618
+ - name: getPetById.ts
619
+ lang: typescript
620
+ twoslash: false
346
621
  code: |
347
622
  export async function getPetById<TData>(
348
623
  petId: GetPetByIdPathParams,
349
624
  ): Promise<ResponseConfig<TData>> {
350
- ...
625
+ // ...
351
626
  }
627
+ - name: usage.ts
628
+ lang: typescript
352
629
  twoslash: false
630
+ code: |
631
+ const response = await getPetById(1)
632
+ // ^? ResponseConfig<Pet>
633
+ console.log(response.status, response.headers)
634
+ const pet = response.data
353
635
  - name: baseURL
354
636
  type: string
355
637
  required: false
356
- description: Sets a custom base URL for all generated calls. When not set, the base URL is automatically taken from the OAS spec via the adapter (e.g. the `servers[0].url` field).
638
+ description: |
639
+ Base URL prepended to every request URL in the generated client. When omitted, the URL comes from the OpenAPI spec's `servers[0].url` (or whichever index the adapter is configured to read).
640
+
641
+ Set this when the generated client should point at a different environment (staging, production) than the one written in the spec.
642
+ examples:
643
+ - name: Override the spec's server URL
644
+ files:
645
+ - lang: typescript
646
+ twoslash: false
647
+ code: |
648
+ import { defineConfig } from 'kubb'
649
+ import { pluginClient } from '@kubb/plugin-client'
650
+
651
+ export default defineConfig({
652
+ input: { path: './petStore.yaml' },
653
+ output: { path: './src/gen' },
654
+ plugins: [
655
+ pluginClient({
656
+ baseURL: 'https://petstore.swagger.io/v2',
657
+ }),
658
+ ],
659
+ })
660
+ - name: clientType
661
+ type: "'function' | 'class' | 'staticClass'"
662
+ required: false
663
+ description: |
664
+ Shape of the underlying client the handlers call into. Mirrors `pluginClient`'s `clientType` option.
665
+ - name: bundle
666
+ type: boolean
667
+ required: false
668
+ description: |
669
+ Copies the HTTP client runtime into the generated output so consumers do not need `@kubb/plugin-client` at runtime. Mirrors `pluginClient`'s `bundle` option.
670
+ - name: paramsCasing
671
+ type: "'camelcase'"
672
+ required: false
673
+ description: |
674
+ Renames parameter properties passed to the underlying client. Mirrors `pluginClient`'s `paramsCasing` option.
357
675
  - name: include
358
676
  type: Array<Include>
359
677
  required: false
360
- description: Array containing include parameters to include tags, operations, methods, paths, or content types.
678
+ description: |
679
+ Restricts generation to operations that match at least one entry in the list. Anything not matched is skipped.
680
+
681
+ Each entry filters by one of:
682
+
683
+ - `tag` — the operation's first tag in the OpenAPI spec.
684
+ - `operationId` — the operation's `operationId`.
685
+ - `path` — the URL pattern (`'/pet/{petId}'`).
686
+ - `method` — HTTP method (`'get'`, `'post'`, ...).
687
+ - `contentType` — the media type of the request body.
688
+
689
+ `pattern` accepts either a string (exact match) or a `RegExp` for fuzzy matches.
361
690
  codeBlock:
362
691
  lang: typescript
363
- title: Include
692
+ title: Type definition
364
693
  code: |
365
694
  export type Include = {
366
695
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
367
696
  pattern: string | RegExp
368
697
  }
698
+ examples:
699
+ - name: Only the pet tag
700
+ files:
701
+ - name: kubb.config.ts
702
+ lang: typescript
703
+ twoslash: false
704
+ code: |
705
+ import { defineConfig } from 'kubb'
706
+ import { pluginTs } from '@kubb/plugin-ts'
707
+
708
+ export default defineConfig({
709
+ input: { path: './petStore.yaml' },
710
+ output: { path: './src/gen' },
711
+ plugins: [
712
+ pluginTs({
713
+ include: [
714
+ { type: 'tag', pattern: 'pet' },
715
+ ],
716
+ }),
717
+ ],
718
+ })
719
+ - name: Only GET operations under /pet
720
+ files:
721
+ - name: kubb.config.ts
722
+ lang: typescript
723
+ twoslash: false
724
+ code: |
725
+ import { defineConfig } from 'kubb'
726
+ import { pluginTs } from '@kubb/plugin-ts'
727
+
728
+ export default defineConfig({
729
+ input: { path: './petStore.yaml' },
730
+ output: { path: './src/gen' },
731
+ plugins: [
732
+ pluginTs({
733
+ include: [
734
+ { type: 'method', pattern: 'get' },
735
+ { type: 'path', pattern: /^\/pet/ },
736
+ ],
737
+ }),
738
+ ],
739
+ })
369
740
  - name: exclude
370
741
  type: Array<Exclude>
371
742
  required: false
372
- description: Array containing exclude parameters to exclude or skip tags, operations, methods, paths, or content types.
743
+ description: |
744
+ Skips any operation that matches at least one entry in the list. The opposite of `include`.
745
+
746
+ Each entry filters by one of:
747
+
748
+ - `tag` — the operation's first tag.
749
+ - `operationId` — the operation's `operationId`.
750
+ - `path` — the URL pattern (`'/pet/{petId}'`).
751
+ - `method` — HTTP method (`'get'`, `'post'`, ...).
752
+ - `contentType` — the media type of the request body.
753
+
754
+ `pattern` accepts a plain string or a `RegExp`. When both `include` and `exclude` are set, `exclude` wins.
373
755
  codeBlock:
374
756
  lang: typescript
375
- title: Exclude
757
+ title: Type definition
376
758
  code: |
377
759
  export type Exclude = {
378
760
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
379
761
  pattern: string | RegExp
380
762
  }
763
+ examples:
764
+ - name: Skip everything under the store tag
765
+ files:
766
+ - name: kubb.config.ts
767
+ lang: typescript
768
+ twoslash: false
769
+ code: |
770
+ import { defineConfig } from 'kubb'
771
+ import { pluginTs } from '@kubb/plugin-ts'
772
+
773
+ export default defineConfig({
774
+ input: { path: './petStore.yaml' },
775
+ output: { path: './src/gen' },
776
+ plugins: [
777
+ pluginTs({
778
+ exclude: [
779
+ { type: 'tag', pattern: 'store' },
780
+ ],
781
+ }),
782
+ ],
783
+ })
784
+ - name: Skip a specific operation and all delete methods
785
+ files:
786
+ - name: kubb.config.ts
787
+ lang: typescript
788
+ twoslash: false
789
+ code: |
790
+ import { defineConfig } from 'kubb'
791
+ import { pluginTs } from '@kubb/plugin-ts'
792
+
793
+ export default defineConfig({
794
+ input: { path: './petStore.yaml' },
795
+ output: { path: './src/gen' },
796
+ plugins: [
797
+ pluginTs({
798
+ exclude: [
799
+ { type: 'operationId', pattern: 'deletePet' },
800
+ { type: 'method', pattern: 'delete' },
801
+ ],
802
+ }),
803
+ ],
804
+ })
381
805
  - name: override
382
806
  type: Array<Override>
383
807
  required: false
384
- description: Array containing override parameters to override `options` based on tags, operations, methods, paths, or content types.
808
+ description: |
809
+ Applies a different set of plugin options to operations that match a pattern. Use this when most of your API should follow the global config, but a handful of endpoints need different treatment.
810
+
811
+ Each entry has the same `type` and `pattern` shape as `include`/`exclude`, plus an `options` object that overrides the plugin's options for matched operations.
812
+
813
+ Entries are evaluated top to bottom. The first matching entry's `options` is merged onto the plugin defaults; later entries do not stack.
385
814
  codeBlock:
386
815
  lang: typescript
387
- title: Override
816
+ title: Type definition
388
817
  code: |
389
818
  export type Override = {
390
819
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
391
820
  pattern: string | RegExp
392
821
  options: PluginOptions
393
822
  }
823
+ examples:
824
+ - name: Use a different enum style for the user tag
825
+ files:
826
+ - name: kubb.config.ts
827
+ lang: typescript
828
+ twoslash: false
829
+ code: |
830
+ import { defineConfig } from 'kubb'
831
+ import { pluginTs } from '@kubb/plugin-ts'
832
+
833
+ export default defineConfig({
834
+ input: { path: './petStore.yaml' },
835
+ output: { path: './src/gen' },
836
+ plugins: [
837
+ pluginTs({
838
+ enumType: 'asConst',
839
+ override: [
840
+ {
841
+ type: 'tag',
842
+ pattern: 'user',
843
+ options: { enumType: 'literal' },
844
+ },
845
+ ],
846
+ }),
847
+ ],
848
+ })
394
849
  - name: generators
395
850
  type: Array<Generator<PluginMcp>>
396
851
  required: false
397
852
  experimental: true
398
853
  description: |
399
- Define additional generators next to the built-in generators.
854
+ Adds custom generators that run alongside the plugin's built-in generators. Each generator can emit additional files or post-process existing ones using the plugin's AST and options.
400
855
 
401
- See [Generators](https://kubb.dev/docs/5.x/guides/creating-plugins) for more information on how to use generators.
856
+ Use this when you need output the plugin does not produce out of the box (a custom client wrapper, an extra index, a metadata file). For end-to-end guidance, see [Creating plugins](https://kubb.dev/docs/5.x/guides/creating-plugins).
857
+ warning: |
858
+ Generators are an experimental, low-level API. The signature may change between minor releases.
402
859
  - name: transformer
403
860
  type: Visitor
404
861
  required: false
405
862
  description: |
406
- A single AST visitor applied to every node before code is printed. Each method you provide replaces the corresponding built-in one. When a method returns `null` or `undefined`, the preset transformer's result is used as the fallback — so you only need to supply the methods you want to change.
863
+ Modifies AST nodes before they are printed to source code. Use this when you need to rewrite operation IDs, drop descriptions, or change schema metadata without forking the generator.
407
864
 
408
- Visitor methods receive the node and a context object. Return a modified node to replace it, or return `undefined`/`void` to leave it unchanged.
865
+ Each visitor method (e.g. `schema`, `operation`) receives the node and a context object. Return a new node to replace it, or return `undefined` to leave it untouched. Methods you omit keep the plugin's default behavior.
409
866
  examples:
410
867
  - name: Strip descriptions before printing
411
868
  files:
412
- - lang: typescript
869
+ - name: kubb.config.ts
870
+ lang: typescript
871
+ twoslash: false
413
872
  code: |
873
+ import { defineConfig } from 'kubb'
414
874
  import { pluginTs } from '@kubb/plugin-ts'
415
875
 
416
- pluginTs({
417
- transformer: {
418
- schema(node) {
419
- return { ...node, description: undefined }
420
- },
421
- },
876
+ export default defineConfig({
877
+ input: { path: './petStore.yaml' },
878
+ output: { path: './src/gen' },
879
+ plugins: [
880
+ pluginTs({
881
+ transformer: {
882
+ schema(node) {
883
+ return { ...node, description: undefined }
884
+ },
885
+ },
886
+ }),
887
+ ],
422
888
  })
423
- twoslash: false
424
889
  - name: Prefix every operationId
425
890
  files:
426
- - lang: typescript
891
+ - name: kubb.config.ts
892
+ lang: typescript
893
+ twoslash: false
427
894
  code: |
895
+ import { defineConfig } from 'kubb'
428
896
  import { pluginTs } from '@kubb/plugin-ts'
429
897
 
430
- pluginTs({
431
- transformer: {
432
- operation(node) {
433
- return { ...node, operationId: `api_${node.operationId}` }
434
- },
435
- },
898
+ export default defineConfig({
899
+ input: { path: './petStore.yaml' },
900
+ output: { path: './src/gen' },
901
+ plugins: [
902
+ pluginTs({
903
+ transformer: {
904
+ operation(node) {
905
+ return { ...node, operationId: `api_${node.operationId}` }
906
+ },
907
+ },
908
+ }),
909
+ ],
436
910
  })
437
- twoslash: false
438
911
  tip: |
439
- Use `transformer` to rewrite node properties before printing. For output naming customization, use `resolver` instead.
912
+ Use `transformer` to rewrite node properties before printing. For changing the names of generated symbols and files, use `resolver` instead.
440
913
  examples:
441
914
  - name: kubb.config.ts
442
915
  files:
443
916
  - lang: typescript
917
+ twoslash: false
444
918
  code: |
445
919
  import { defineConfig } from 'kubb'
446
920
  import { pluginTs } from '@kubb/plugin-ts'
@@ -456,7 +930,7 @@ examples:
456
930
  pluginClient(),
457
931
  pluginZod(),
458
932
  pluginMcp({
459
- output: { path: 'mcp', barrelType: 'named' },
933
+ output: { path: 'mcp', barrel: { type: 'named' } },
460
934
  client: {
461
935
  baseURL: 'https://petstore.swagger.io/v2',
462
936
  },
@@ -467,4 +941,3 @@ examples:
467
941
  }),
468
942
  ],
469
943
  })
470
- twoslash: false