@kubb/plugin-mcp 5.0.0-beta.22 → 5.0.0-beta.25

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/dist/index.d.ts CHANGED
@@ -28,44 +28,50 @@ type ResolverMcp = Resolver & {
28
28
  };
29
29
  type Options = {
30
30
  /**
31
- * Specify the export location for the files and define the behavior of the output.
32
- * @default { path: 'mcp', barrelType: 'named' }
31
+ * Where the generated MCP tool handlers are written and how they are exported.
32
+ *
33
+ * @default { path: 'mcp', barrel: { type: 'named' } }
33
34
  */
34
35
  output?: Output;
35
36
  /**
36
- * Client configuration for HTTP request generation.
37
+ * HTTP client used by each MCP handler to call the underlying API. Mirrors a
38
+ * subset of `pluginClient` options.
37
39
  */
38
40
  client?: ClientImportPath & Pick<PluginClient['options'], 'clientType' | 'dataReturnType' | 'baseURL' | 'bundle' | 'paramsCasing'>;
39
41
  /**
40
- * Apply casing to parameter names to match your configuration.
42
+ * Rename parameter properties in the generated handlers. The HTTP layer still
43
+ * uses the original spec names; Kubb writes the mapping for you.
44
+ *
45
+ * @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
41
46
  */
42
47
  paramsCasing?: 'camelcase';
43
48
  /**
44
- * Group the MCP requests based on the provided name.
49
+ * Split generated files into subfolders based on the operation's tag.
45
50
  */
46
51
  group?: Group;
47
52
  /**
48
- * Tags, operations, or paths to exclude from generation.
53
+ * Skip operations matching at least one entry in the list.
49
54
  */
50
55
  exclude?: Array<Exclude>;
51
56
  /**
52
- * Tags, operations, or paths to include in generation.
57
+ * Restrict generation to operations matching at least one entry in the list.
53
58
  */
54
59
  include?: Array<Include>;
55
60
  /**
56
- * Override options for specific tags, operations, or paths.
61
+ * Apply a different options object to operations matching a pattern.
57
62
  */
58
63
  override?: Array<Override<ResolvedOptions>>;
59
64
  /**
60
- * Override naming conventions for function names and types.
65
+ * Override how handler names and file paths are built. Methods you omit fall
66
+ * back to the default `resolverMcp`.
61
67
  */
62
68
  resolver?: Partial<ResolverMcp> & ThisType<ResolverMcp>;
63
69
  /**
64
- * AST visitor to transform generated nodes.
70
+ * AST visitor applied to each operation node before printing.
65
71
  */
66
72
  transformer?: ast.Visitor;
67
73
  /**
68
- * Additional generators alongside the default generators.
74
+ * Custom generators that run alongside the built-in MCP generators.
69
75
  */
70
76
  generators?: Array<Generator<PluginMcp>>;
71
77
  };
@@ -74,7 +80,7 @@ type ResolvedOptions = {
74
80
  exclude: Array<Exclude>;
75
81
  include: Array<Include> | undefined;
76
82
  override: Array<Override<ResolvedOptions>>;
77
- group: Group | undefined;
83
+ group: Group | null;
78
84
  client: Pick<PluginClient['options'], 'client' | 'clientType' | 'dataReturnType' | 'importPath' | 'baseURL' | 'bundle' | 'paramsCasing'>;
79
85
  paramsCasing: Options['paramsCasing'];
80
86
  resolver: ResolverMcp;
@@ -170,13 +176,13 @@ type Props = {
170
176
  /**
171
177
  * Query params — individual schemas to compose into `z.object({ ... })`.
172
178
  */
173
- queryParams?: string | Array<ZodParam>;
179
+ queryParams?: string | Array<ZodParam> | null;
174
180
  /**
175
181
  * Header params — individual schemas to compose into `z.object({ ... })`.
176
182
  */
177
- headerParams?: string | Array<ZodParam>;
178
- requestName?: string;
179
- responseName?: string;
183
+ headerParams?: string | Array<ZodParam> | null;
184
+ requestName?: string | null;
185
+ responseName?: string | null;
180
186
  };
181
187
  node: ast.OperationNode;
182
188
  }>;
@@ -190,6 +196,12 @@ declare function Server({
190
196
  }: Props): KubbReactNode;
191
197
  //#endregion
192
198
  //#region src/generators/mcpGenerator.d.ts
199
+ /**
200
+ * Built-in operation generator for `@kubb/plugin-mcp`. Emits one MCP tool
201
+ * handler per OpenAPI operation, wiring the input Zod schema, the HTTP call,
202
+ * and the response shape into a single function that an MCP server can
203
+ * register as a callable tool.
204
+ */
193
205
  declare const mcpGenerator: _$_kubb_core0.Generator<PluginMcp, unknown>;
194
206
  //#endregion
195
207
  //#region src/generators/serverGenerator.d.ts
@@ -203,17 +215,53 @@ declare const mcpGenerator: _$_kubb_core0.Generator<PluginMcp, unknown>;
203
215
  declare const serverGenerator: _$_kubb_core0.Generator<PluginMcp, unknown>;
204
216
  //#endregion
205
217
  //#region src/plugin.d.ts
218
+ /**
219
+ * Canonical plugin name for `@kubb/plugin-mcp`. Used for driver lookups and
220
+ * cross-plugin dependency references.
221
+ */
206
222
  declare const pluginMcpName = "plugin-mcp";
223
+ /**
224
+ * Generates a Model Context Protocol (MCP) server from an OpenAPI spec. Every
225
+ * operation becomes a typed MCP tool that AI assistants (Claude Desktop, Claude
226
+ * Code, MCP-compatible clients) can call directly.
227
+ *
228
+ * @example
229
+ * ```ts
230
+ * import { defineConfig } from 'kubb'
231
+ * import { pluginTs } from '@kubb/plugin-ts'
232
+ * import { pluginClient } from '@kubb/plugin-client'
233
+ * import { pluginZod } from '@kubb/plugin-zod'
234
+ * import { pluginMcp } from '@kubb/plugin-mcp'
235
+ *
236
+ * export default defineConfig({
237
+ * input: { path: './petStore.yaml' },
238
+ * output: { path: './src/gen' },
239
+ * plugins: [
240
+ * pluginTs(),
241
+ * pluginClient(),
242
+ * pluginZod(),
243
+ * pluginMcp({
244
+ * output: { path: './mcp' },
245
+ * client: { baseURL: 'https://petstore.swagger.io/v2' },
246
+ * }),
247
+ * ],
248
+ * })
249
+ * ```
250
+ */
207
251
  declare const pluginMcp: (options?: Options | undefined) => _$_kubb_core0.Plugin<PluginMcp>;
208
252
  //#endregion
209
253
  //#region src/resolvers/resolverMcp.d.ts
210
254
  /**
211
- * Naming convention resolver for MCP plugin.
255
+ * Default resolver used by `@kubb/plugin-mcp`. Decides the names and file
256
+ * paths for every generated MCP tool handler. Function names get a `Handler`
257
+ * suffix so an operation `addPet` becomes `addPetHandler`.
212
258
  *
213
- * Provides default naming helpers using camelCase with a `handler` suffix for functions.
259
+ * @example Resolve a handler name
260
+ * ```ts
261
+ * import { resolverMcp } from '@kubb/plugin-mcp'
214
262
  *
215
- * @example
216
- * `resolverMcp.default('addPet', 'function') // → 'addPetHandler'`
263
+ * resolverMcp.default('addPet', 'function') // 'addPetHandler'
264
+ * ```
217
265
  */
218
266
  declare const resolverMcp: ResolverMcp;
219
267
  //#endregion
package/dist/index.js CHANGED
@@ -220,12 +220,12 @@ var URLPath = class {
220
220
  get object() {
221
221
  return this.toObject();
222
222
  }
223
- /** Returns a map of path parameter names, or `undefined` when the path has no parameters.
223
+ /** Returns a map of path parameter names, or `null` when the path has no parameters.
224
224
  *
225
225
  * @example
226
226
  * ```ts
227
227
  * new URLPath('/pet/{petId}').params // { petId: 'petId' }
228
- * new URLPath('/pet').params // undefined
228
+ * new URLPath('/pet').params // null
229
229
  * ```
230
230
  */
231
231
  get params() {
@@ -288,7 +288,7 @@ var URLPath = class {
288
288
  const key = replacer ? replacer(param) : param;
289
289
  params[key] = key;
290
290
  });
291
- return Object.keys(params).length > 0 ? params : void 0;
291
+ return Object.keys(params).length > 0 ? params : null;
292
292
  }
293
293
  /** Converts the OpenAPI path to Express-style colon syntax.
294
294
  *
@@ -304,9 +304,9 @@ var URLPath = class {
304
304
  //#endregion
305
305
  //#region ../../internals/shared/src/operation.ts
306
306
  function getOperationLink(node, link) {
307
- if (!link) return;
308
- if (typeof link === "function") return link(node);
309
- if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : void 0;
307
+ if (!link) return null;
308
+ if (typeof link === "function") return link(node) ?? null;
309
+ if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
310
310
  return `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}`;
311
311
  }
312
312
  function buildOperationComments(node, options = {}) {
@@ -337,15 +337,15 @@ function getOperationParameters(node, options = {}) {
337
337
  }
338
338
  function getStatusCodeNumber(statusCode) {
339
339
  const code = Number(statusCode);
340
- return Number.isNaN(code) ? void 0 : code;
340
+ return Number.isNaN(code) ? null : code;
341
341
  }
342
342
  function isSuccessStatusCode(statusCode) {
343
343
  const code = getStatusCodeNumber(statusCode);
344
- return code !== void 0 && code >= 200 && code < 300;
344
+ return code !== null && code >= 200 && code < 300;
345
345
  }
346
346
  function isErrorStatusCode(statusCode) {
347
347
  const code = getStatusCodeNumber(statusCode);
348
- return code !== void 0 && code >= 400;
348
+ return code !== null && code >= 400;
349
349
  }
350
350
  function resolveErrorNames(node, resolver) {
351
351
  return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
@@ -372,7 +372,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
372
372
  ...query.map((param) => resolver.resolveQueryParamsName(node, param)),
373
373
  ...header.map((param) => resolver.resolveHeaderParamsName(node, param))
374
374
  ];
375
- const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0, resolver.resolveResponseName(node)];
375
+ const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
376
376
  const result = (options.order === "body-response-first" ? [
377
377
  ...bodyAndResponseNames,
378
378
  ...paramNames,
@@ -387,6 +387,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
387
387
  }
388
388
  function findSuccessStatusCode(responses) {
389
389
  for (const response of responses) if (isSuccessStatusCode(response.statusCode)) return response.statusCode;
390
+ return null;
390
391
  }
391
392
  //#endregion
392
393
  //#region ../../internals/shared/src/params.ts
@@ -398,10 +399,10 @@ function buildParamsMapping(originalParams, mappedParams) {
398
399
  mapping[param.name] = mappedName;
399
400
  if (param.name !== mappedName) hasChanged = true;
400
401
  });
401
- return hasChanged ? mapping : void 0;
402
+ return hasChanged ? mapping : null;
402
403
  }
403
404
  function buildTransformedParamsMapping(params, transformName) {
404
- if (!params.length) return;
405
+ if (!params.length) return null;
405
406
  return buildParamsMapping(params, params.map((param) => ({
406
407
  ...param,
407
408
  name: transformName(param.name)
@@ -422,7 +423,7 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
422
423
  const isFormData = contentType === "multipart/form-data";
423
424
  const { query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing });
424
425
  const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
425
- const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0;
426
+ const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
426
427
  const responseName = resolver.resolveResponseName(node);
427
428
  const errorResponses = node.responses.filter((r) => Number(r.statusCode) >= 400).map((r) => resolver.resolveResponseStatusName(node, r.statusCode));
428
429
  const generics = [
@@ -438,11 +439,11 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
438
439
  });
439
440
  const baseParamsSignature = declarationPrinter.print(paramsNode) ?? "";
440
441
  const paramsSignature = baseParamsSignature ? `${baseParamsSignature}, request: RequestHandlerExtra<ServerRequest, ServerNotification>` : "request: RequestHandlerExtra<ServerRequest, ServerNotification>";
441
- const pathParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalPathParams, camelCase) : void 0;
442
- const queryParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalQueryParams, camelCase) : void 0;
443
- const headerParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalHeaderParams, camelCase) : void 0;
444
- const contentTypeHeader = contentType && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0;
445
- const headers = [headerParams.length ? headerParamsMapping ? "...mappedHeaders" : "...headers" : void 0, contentTypeHeader].filter(Boolean);
442
+ const pathParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalPathParams, camelCase) : null;
443
+ const queryParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalQueryParams, camelCase) : null;
444
+ const headerParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalHeaderParams, camelCase) : null;
445
+ const contentTypeHeader = contentType && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null;
446
+ const headers = [headerParams.length ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null, contentTypeHeader].filter(Boolean);
446
447
  const fetchConfig = [];
447
448
  fetchConfig.push(`method: ${JSON.stringify(node.method.toUpperCase())}`);
448
449
  fetchConfig.push(`url: ${urlPath.template}`);
@@ -585,9 +586,9 @@ function Server({ name, serverName, serverVersion, paramsCasing, operations }) {
585
586
  const paramsNode = entries.length ? ast.createFunctionParameters({ params: [ast.createParameterGroup({ properties: entries.map((e) => ast.createFunctionParameter({
586
587
  name: e.key,
587
588
  optional: false
588
- })) })] }) : void 0;
589
+ })) })] }) : null;
589
590
  const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
590
- const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} }` : void 0;
591
+ const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} }` : null;
591
592
  const outputSchema = zod.responseName;
592
593
  const config = [
593
594
  tool.title ? `title: ${JSON.stringify(tool.title)}` : null,
@@ -628,6 +629,12 @@ server.registerTool(${JSON.stringify(tool.name)}, {
628
629
  }
629
630
  //#endregion
630
631
  //#region src/generators/mcpGenerator.tsx
632
+ /**
633
+ * Built-in operation generator for `@kubb/plugin-mcp`. Emits one MCP tool
634
+ * handler per OpenAPI operation, wiring the input Zod schema, the HTTP call,
635
+ * and the response shape into a single function that an MCP server can
636
+ * register as a callable tool.
637
+ */
631
638
  const mcpGenerator = defineGenerator({
632
639
  name: "mcp",
633
640
  renderer: jsxRendererSync,
@@ -651,7 +658,7 @@ const mcpGenerator = defineGenerator({
651
658
  }, {
652
659
  root,
653
660
  output,
654
- group
661
+ group: group ?? void 0
655
662
  }),
656
663
  fileTs: tsResolver.resolveFile({
657
664
  name: node.operationId,
@@ -661,7 +668,7 @@ const mcpGenerator = defineGenerator({
661
668
  }, {
662
669
  root,
663
670
  output: pluginTs.options?.output ?? output,
664
- group: pluginTs.options?.group
671
+ group: pluginTs.options?.group ?? void 0
665
672
  })
666
673
  };
667
674
  return /* @__PURE__ */ jsxs(File, {
@@ -787,7 +794,7 @@ const serverGenerator = defineGenerator({
787
794
  }, {
788
795
  root,
789
796
  output,
790
- group
797
+ group: group ?? void 0
791
798
  });
792
799
  const zodFile = zodResolver.resolveFile({
793
800
  name: node.operationId,
@@ -797,11 +804,11 @@ const serverGenerator = defineGenerator({
797
804
  }, {
798
805
  root,
799
806
  output: pluginZod.options?.output ?? output,
800
- group: pluginZod.options?.group
807
+ group: pluginZod.options?.group ?? void 0
801
808
  });
802
- const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) : void 0;
809
+ const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) : null;
803
810
  const successStatus = findSuccessStatusCode(node.responses);
804
- const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) : void 0;
811
+ const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) : null;
805
812
  const resolveParams = (params) => params.map((p) => ({
806
813
  name: p.name,
807
814
  schemaName: zodResolver.resolveParamName(node, p)
@@ -818,8 +825,8 @@ const serverGenerator = defineGenerator({
818
825
  },
819
826
  zod: {
820
827
  pathParams: resolveParams(pathParams),
821
- queryParams: queryParams.length ? resolveParams(queryParams) : void 0,
822
- headerParams: headerParams.length ? resolveParams(headerParams) : void 0,
828
+ queryParams: queryParams.length ? resolveParams(queryParams) : null,
829
+ headerParams: headerParams.length ? resolveParams(headerParams) : null,
823
830
  requestName,
824
831
  responseName,
825
832
  file: zodFile
@@ -904,12 +911,16 @@ const serverGenerator = defineGenerator({
904
911
  //#endregion
905
912
  //#region src/resolvers/resolverMcp.ts
906
913
  /**
907
- * Naming convention resolver for MCP plugin.
914
+ * Default resolver used by `@kubb/plugin-mcp`. Decides the names and file
915
+ * paths for every generated MCP tool handler. Function names get a `Handler`
916
+ * suffix so an operation `addPet` becomes `addPetHandler`.
908
917
  *
909
- * Provides default naming helpers using camelCase with a `handler` suffix for functions.
918
+ * @example Resolve a handler name
919
+ * ```ts
920
+ * import { resolverMcp } from '@kubb/plugin-mcp'
910
921
  *
911
- * @example
912
- * `resolverMcp.default('addPet', 'function') // → 'addPetHandler'`
922
+ * resolverMcp.default('addPet', 'function') // 'addPetHandler'
923
+ * ```
913
924
  */
914
925
  const resolverMcp = defineResolver(() => ({
915
926
  name: "default",
@@ -930,7 +941,39 @@ const resolverMcp = defineResolver(() => ({
930
941
  }));
931
942
  //#endregion
932
943
  //#region src/plugin.ts
944
+ /**
945
+ * Canonical plugin name for `@kubb/plugin-mcp`. Used for driver lookups and
946
+ * cross-plugin dependency references.
947
+ */
933
948
  const pluginMcpName = "plugin-mcp";
949
+ /**
950
+ * Generates a Model Context Protocol (MCP) server from an OpenAPI spec. Every
951
+ * operation becomes a typed MCP tool that AI assistants (Claude Desktop, Claude
952
+ * Code, MCP-compatible clients) can call directly.
953
+ *
954
+ * @example
955
+ * ```ts
956
+ * import { defineConfig } from 'kubb'
957
+ * import { pluginTs } from '@kubb/plugin-ts'
958
+ * import { pluginClient } from '@kubb/plugin-client'
959
+ * import { pluginZod } from '@kubb/plugin-zod'
960
+ * import { pluginMcp } from '@kubb/plugin-mcp'
961
+ *
962
+ * export default defineConfig({
963
+ * input: { path: './petStore.yaml' },
964
+ * output: { path: './src/gen' },
965
+ * plugins: [
966
+ * pluginTs(),
967
+ * pluginClient(),
968
+ * pluginZod(),
969
+ * pluginMcp({
970
+ * output: { path: './mcp' },
971
+ * client: { baseURL: 'https://petstore.swagger.io/v2' },
972
+ * }),
973
+ * ],
974
+ * })
975
+ * ```
976
+ */
934
977
  const pluginMcp = definePlugin((options) => {
935
978
  const { output = {
936
979
  path: "mcp",
@@ -944,7 +987,7 @@ const pluginMcp = definePlugin((options) => {
944
987
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
945
988
  return `${camelCase(ctx.group)}Requests`;
946
989
  }
947
- } : void 0;
990
+ } : null;
948
991
  return {
949
992
  name: pluginMcpName,
950
993
  options,