@kubb/mcp 5.0.0-beta.8 → 5.0.0-beta.80

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.cjs CHANGED
@@ -21,28 +21,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- let node_http = require("node:http");
25
- node_http = __toESM(node_http, 1);
26
- let _remix_run_node_fetch_server = require("@remix-run/node-fetch-server");
27
24
  let _tmcp_adapter_valibot = require("@tmcp/adapter-valibot");
28
- let _tmcp_transport_http = require("@tmcp/transport-http");
29
25
  let _tmcp_transport_stdio = require("@tmcp/transport-stdio");
30
26
  let tmcp = require("tmcp");
31
27
  let node_events = require("node:events");
32
- let node_fs = require("node:fs");
33
- node_fs = __toESM(node_fs, 1);
34
- let node_path = require("node:path");
35
- node_path = __toESM(node_path, 1);
36
28
  let _kubb_core = require("@kubb/core");
37
29
  let tmcp_tool = require("tmcp/tool");
38
30
  let tmcp_utils = require("tmcp/utils");
39
31
  let valibot = require("valibot");
40
32
  valibot = __toESM(valibot, 1);
33
+ let node_fs = require("node:fs");
34
+ node_fs = __toESM(node_fs, 1);
35
+ let node_path = require("node:path");
36
+ node_path = __toESM(node_path, 1);
41
37
  let jiti = require("jiti");
42
38
  let node_process = require("node:process");
43
39
  node_process = __toESM(node_process, 1);
44
40
  //#region package.json
45
- var version = "5.0.0-beta.8";
41
+ var version = "5.0.0-beta.80";
46
42
  //#endregion
47
43
  //#region ../../internals/utils/src/errors.ts
48
44
  /**
@@ -90,9 +86,12 @@ var AsyncEventEmitter = class {
90
86
  * await emitter.emit('build', 'petstore')
91
87
  * ```
92
88
  */
93
- async emit(eventName, ...eventArgs) {
89
+ emit(eventName, ...eventArgs) {
94
90
  const listeners = this.#emitter.listeners(eventName);
95
91
  if (listeners.length === 0) return;
92
+ return this.#emitAll(eventName, listeners, eventArgs);
93
+ }
94
+ async #emitAll(eventName, listeners, eventArgs) {
96
95
  for (const listener of listeners) try {
97
96
  await listener(...eventArgs);
98
97
  } catch (err) {
@@ -117,21 +116,6 @@ var AsyncEventEmitter = class {
117
116
  this.#emitter.on(eventName, handler);
118
117
  }
119
118
  /**
120
- * Registers a one-shot listener that removes itself after the first invocation.
121
- *
122
- * @example
123
- * ```ts
124
- * emitter.onOnce('build', async (name) => { console.log(name) })
125
- * ```
126
- */
127
- onOnce(eventName, handler) {
128
- const wrapper = (...args) => {
129
- this.off(eventName, wrapper);
130
- return handler(...args);
131
- };
132
- this.on(eventName, wrapper);
133
- }
134
- /**
135
119
  * Removes a previously registered listener.
136
120
  *
137
121
  * @example
@@ -155,6 +139,18 @@ var AsyncEventEmitter = class {
155
139
  return this.#emitter.listenerCount(eventName);
156
140
  }
157
141
  /**
142
+ * Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
143
+ * Set this above the expected listener count when many listeners attach by design.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * emitter.setMaxListeners(40)
148
+ * ```
149
+ */
150
+ setMaxListeners(max) {
151
+ this.#emitter.setMaxListeners(max);
152
+ }
153
+ /**
158
154
  * Removes all listeners from every event channel.
159
155
  *
160
156
  * @example
@@ -180,31 +176,33 @@ function isPromise(result) {
180
176
  return result !== null && result !== void 0 && typeof result["then"] === "function";
181
177
  }
182
178
  //#endregion
183
- //#region src/schemas/generateSchema.ts
184
- const generateSchema = valibot.object({
185
- config: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to kubb.config file (supports .ts, .js, .cjs). If not provided, will look for kubb.config.{ts,js,cjs} in current directory"))),
186
- input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
187
- output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
188
- logLevel: valibot.optional(valibot.pipe(valibot.picklist([
189
- "silent",
190
- "error",
191
- "warn",
192
- "info",
193
- "verbose",
194
- "debug"
195
- ]), valibot.description("Log level for build output")), "info")
196
- });
197
- //#endregion
198
- //#region src/types.ts
179
+ //#region src/constants.ts
180
+ /**
181
+ * File extensions a Kubb config is allowed to use. A config path with any other
182
+ * extension is rejected before it is loaded.
183
+ */
184
+ const ALLOWED_CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([
185
+ ".ts",
186
+ ".mts",
187
+ ".cts",
188
+ ".js",
189
+ ".mjs",
190
+ ".cjs"
191
+ ]);
192
+ /**
193
+ * Notification kinds reported back to the MCP client while loading config and
194
+ * running generation, covering progress, results, and errors.
195
+ */
199
196
  const NotifyTypes = {
200
197
  INFO: "INFO",
201
198
  SUCCESS: "SUCCESS",
202
199
  ERROR: "ERROR",
203
200
  WARN: "WARN",
201
+ DIAGNOSTIC: "DIAGNOSTIC",
204
202
  PLUGIN_START: "PLUGIN_START",
205
203
  PLUGIN_END: "PLUGIN_END",
206
204
  FILES_START: "FILES_START",
207
- FILE_UPDATE: "FILE_UPDATE",
205
+ FILES_UPDATE: "FILES_UPDATE",
208
206
  FILES_END: "FILES_END",
209
207
  GENERATION_START: "GENERATION_START",
210
208
  GENERATION_END: "GENERATION_END",
@@ -216,37 +214,190 @@ const NotifyTypes = {
216
214
  BUILD_START: "BUILD_START",
217
215
  BUILD_END: "BUILD_END",
218
216
  BUILD_FAILED: "BUILD_FAILED",
219
- BUILD_SUCCESS: "BUILD_SUCCESS",
220
- FATAL_ERROR: "FATAL_ERROR"
217
+ BUILD_SUCCESS: "BUILD_SUCCESS"
221
218
  };
222
219
  //#endregion
223
- //#region src/constants.ts
224
- const ALLOWED_CONFIG_EXTENSIONS = new Set([
225
- ".ts",
226
- ".mts",
227
- ".cts",
228
- ".js",
229
- ".mjs",
230
- ".cjs"
231
- ]);
220
+ //#region src/schemas/generateSchema.ts
221
+ const generateSchema = valibot.object({
222
+ config: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to kubb.config file (supports .ts, .js, .cjs). If not provided, will look for kubb.config.{ts,js,cjs} in current directory"))),
223
+ input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
224
+ output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
225
+ logLevel: valibot.optional(valibot.pipe(valibot.picklist([
226
+ "silent",
227
+ "info",
228
+ "verbose"
229
+ ]), valibot.description("Log level for build output")), "info")
230
+ });
231
+ //#endregion
232
+ //#region ../../internals/shared/src/constants.ts
233
+ const KUBB_CONFIG_FILENAME = "kubb.config.ts";
234
+ const availablePlugins = [
235
+ {
236
+ value: "plugin-ts",
237
+ label: "TypeScript",
238
+ hint: "Recommended",
239
+ packageName: "@kubb/plugin-ts",
240
+ importName: "pluginTs",
241
+ category: "types"
242
+ },
243
+ {
244
+ value: "plugin-axios",
245
+ label: "Axios Client",
246
+ packageName: "@kubb/plugin-axios",
247
+ importName: "pluginAxios",
248
+ category: "client"
249
+ },
250
+ {
251
+ value: "plugin-fetch",
252
+ label: "Fetch Client",
253
+ packageName: "@kubb/plugin-fetch",
254
+ importName: "pluginFetch",
255
+ category: "client"
256
+ },
257
+ {
258
+ value: "plugin-react-query",
259
+ label: "React Query / TanStack Query",
260
+ packageName: "@kubb/plugin-react-query",
261
+ importName: "pluginReactQuery",
262
+ category: "framework"
263
+ },
264
+ {
265
+ value: "plugin-vue-query",
266
+ label: "Vue Query",
267
+ packageName: "@kubb/plugin-vue-query",
268
+ importName: "pluginVueQuery",
269
+ category: "framework"
270
+ },
271
+ {
272
+ value: "plugin-zod",
273
+ label: "Zod Schemas",
274
+ packageName: "@kubb/plugin-zod",
275
+ importName: "pluginZod",
276
+ category: "validation"
277
+ },
278
+ {
279
+ value: "plugin-faker",
280
+ label: "Faker.js Mocks",
281
+ packageName: "@kubb/plugin-faker",
282
+ importName: "pluginFaker",
283
+ category: "mocks"
284
+ },
285
+ {
286
+ value: "plugin-msw",
287
+ label: "MSW Handlers",
288
+ packageName: "@kubb/plugin-msw",
289
+ importName: "pluginMsw",
290
+ category: "mocks"
291
+ },
292
+ {
293
+ value: "plugin-cypress",
294
+ label: "Cypress Tests",
295
+ packageName: "@kubb/plugin-cypress",
296
+ importName: "pluginCypress",
297
+ category: "testing"
298
+ },
299
+ {
300
+ value: "plugin-mcp",
301
+ label: "MCP Server (AI / Model Context Protocol)",
302
+ packageName: "@kubb/plugin-mcp",
303
+ importName: "pluginMcp",
304
+ category: "ai"
305
+ },
306
+ {
307
+ value: "plugin-redoc",
308
+ label: "ReDoc Documentation",
309
+ packageName: "@kubb/plugin-redoc",
310
+ importName: "pluginRedoc",
311
+ category: "documentation"
312
+ }
313
+ ];
232
314
  //#endregion
233
- //#region src/utils/loadUserConfig.ts
234
- const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, {
315
+ //#region ../../internals/shared/src/init.ts
316
+ function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
317
+ return `import { defineConfig } from 'kubb'
318
+ ${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
319
+
320
+ export default defineConfig({
321
+ root: '.',
322
+ input: {
323
+ path: '${inputPath}',
324
+ },
325
+ output: {
326
+ path: '${outputPath}',
327
+ clean: true,
328
+ },
329
+ plugins: [
330
+ ${selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join("\n")}
331
+ ],
332
+ })
333
+ `;
334
+ }
335
+ //#endregion
336
+ //#region ../../internals/shared/src/loader.ts
337
+ /**
338
+ * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at
339
+ * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.
340
+ */
341
+ const JITI_OPTIONS = {
235
342
  jsx: {
236
343
  runtime: "automatic",
237
344
  importSource: "@kubb/renderer-jsx"
238
345
  },
239
346
  moduleCache: false
240
- });
347
+ };
348
+ /**
349
+ * Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.
350
+ *
351
+ * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly.
352
+ *
353
+ * @example
354
+ * ```ts
355
+ * const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })
356
+ * ```
357
+ */
358
+ function createModuleLoader() {
359
+ const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, JITI_OPTIONS);
360
+ return { load(filePath, options) {
361
+ return options?.default ? jiti$1.import(filePath, { default: true }) : jiti$1.import(filePath);
362
+ } };
363
+ }
364
+ //#endregion
365
+ //#region src/utils.ts
366
+ /**
367
+ * Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry
368
+ * keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so
369
+ * the agent can act on the problem rather than parsing a bare message. No ANSI styling,
370
+ * unlike the CLI renderer.
371
+ */
372
+ function formatDiagnostics(diagnostics) {
373
+ return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join("\n\n");
374
+ }
375
+ function formatDiagnostic(diagnostic) {
376
+ const { code, message, location, help, plugin, docsUrl } = diagnostic;
377
+ const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`];
378
+ if (location && "pointer" in location) lines.push(` at: ${location.pointer}`);
379
+ if (help) lines.push(` fix: ${help}`);
380
+ if (docsUrl) lines.push(` see: ${docsUrl}`);
381
+ return lines.join("\n");
382
+ }
383
+ const loader = createModuleLoader();
241
384
  const loadedModules = /* @__PURE__ */ new Map();
242
385
  async function loadModule(filePath) {
243
386
  const ext = node_path.default.extname(filePath);
244
387
  if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
245
388
  if (loadedModules.has(filePath)) return loadedModules.get(filePath);
246
- const mod = await jiti$1.import(filePath, { default: true });
389
+ const mod = await loader.load(filePath, { default: true });
247
390
  loadedModules.set(filePath, mod);
248
391
  return mod;
249
392
  }
393
+ /**
394
+ * Loads the user's Kubb config and returns it with the directory it was found in.
395
+ *
396
+ * When `configPath` is given it must use an allowed extension and resolve inside
397
+ * the current working directory, otherwise loading throws. When omitted, the
398
+ * known `kubb.config.*` file names are tried in the current directory. Every
399
+ * outcome is reported through `notify` before the function returns or throws.
400
+ */
250
401
  async function loadUserConfig(configPath, { notify }) {
251
402
  if (configPath) {
252
403
  const ext = node_path.default.extname(configPath);
@@ -302,8 +453,6 @@ async function loadUserConfig(configPath, { notify }) {
302
453
  await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
303
454
  throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
304
455
  }
305
- //#endregion
306
- //#region src/utils/resolveCwd.ts
307
456
  /**
308
457
  * Determine the root directory based on userConfig.root and resolvedConfigDir
309
458
  * 1. If userConfig.root exists and is absolute, use it as-is
@@ -317,8 +466,12 @@ function resolveCwd(userConfig, cwd) {
317
466
  }
318
467
  return cwd;
319
468
  }
320
- //#endregion
321
- //#region src/utils/resolveUserConfig.ts
469
+ /**
470
+ * Normalizes a possible config into a single resolved `Config`.
471
+ *
472
+ * Calls the config when it is a function, awaits it when it is a promise, and
473
+ * picks the first entry when it resolves to an array.
474
+ */
322
475
  async function resolveUserConfig(config, options) {
323
476
  const result = typeof config === "function" ? config({
324
477
  logLevel: options.logLevel,
@@ -338,8 +491,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
338
491
  try {
339
492
  const hooks = new AsyncEventEmitter();
340
493
  const messages = [];
341
- const notify = async (type, message, _data) => {
342
- messages.push(`${type}: ${message}`);
494
+ const notify = async (type, message, data) => {
495
+ messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
343
496
  };
344
497
  hooks.on("kubb:info", async ({ message }) => {
345
498
  await notify(NotifyTypes.INFO, message);
@@ -353,6 +506,9 @@ const generateTool = (0, tmcp_tool.defineTool)({
353
506
  hooks.on("kubb:warn", async ({ message }) => {
354
507
  await notify(NotifyTypes.WARN, message);
355
508
  });
509
+ hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
510
+ await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic));
511
+ });
356
512
  hooks.on("kubb:plugin:start", async ({ plugin }) => {
357
513
  await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
358
514
  });
@@ -362,8 +518,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
362
518
  hooks.on("kubb:files:processing:start", async () => {
363
519
  await notify(NotifyTypes.FILES_START, "Starting file processing");
364
520
  });
365
- hooks.on("kubb:file:processing:update", async ({ file }) => {
366
- await notify(NotifyTypes.FILE_UPDATE, `Processing file: ${file.name}`);
521
+ hooks.on("kubb:files:processing:update", async ({ files }) => {
522
+ await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
367
523
  });
368
524
  hooks.on("kubb:files:processing:end", async () => {
369
525
  await notify(NotifyTypes.FILES_END, "File processing complete");
@@ -409,152 +565,23 @@ const generateTool = (0, tmcp_tool.defineTool)({
409
565
  await kubb.setup();
410
566
  await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
411
567
  await notify(NotifyTypes.BUILD_START, "Starting build");
412
- const { files, failedPlugins, error } = await kubb.safeBuild();
568
+ const { files, diagnostics } = await kubb.safeBuild();
413
569
  await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
414
- if (error || failedPlugins.size > 0) {
415
- const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
416
- await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)`);
417
- return tmcp_utils.tool.error(`Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`);
570
+ const problems = diagnostics.filter(_kubb_core.Diagnostics.isProblem);
571
+ const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
572
+ if (errors.length > 0) {
573
+ await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${errors.length} diagnostic(s)`);
574
+ const serialized = problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic));
575
+ return tmcp_utils.tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
418
576
  }
419
577
  await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
420
578
  return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
421
579
  } catch (caughtError) {
422
- const error = toError(caughtError);
423
- return tmcp_utils.tool.error(`Build error: ${error.message}\n${error.stack ?? ""}`);
580
+ const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(caughtError));
581
+ return tmcp_utils.tool.error(`Build error:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
424
582
  }
425
583
  });
426
584
  //#endregion
427
- //#region ../../internals/shared/src/constants.ts
428
- const KUBB_CONFIG_FILENAME = "kubb.config.ts";
429
- const availablePlugins = [
430
- {
431
- value: "plugin-ts",
432
- label: "TypeScript",
433
- hint: "Recommended",
434
- packageName: "@kubb/plugin-ts",
435
- importName: "pluginTs",
436
- category: "types"
437
- },
438
- {
439
- value: "plugin-client",
440
- label: "Client (Fetch/Axios)",
441
- packageName: "@kubb/plugin-client",
442
- importName: "pluginClient",
443
- category: "client"
444
- },
445
- {
446
- value: "plugin-react-query",
447
- label: "React Query / TanStack Query",
448
- packageName: "@kubb/plugin-react-query",
449
- importName: "pluginReactQuery",
450
- category: "framework"
451
- },
452
- {
453
- value: "plugin-vue-query",
454
- label: "Vue Query",
455
- packageName: "@kubb/plugin-vue-query",
456
- importName: "pluginVueQuery",
457
- category: "framework"
458
- },
459
- {
460
- value: "plugin-zod",
461
- label: "Zod Schemas",
462
- packageName: "@kubb/plugin-zod",
463
- importName: "pluginZod",
464
- category: "validation"
465
- },
466
- {
467
- value: "plugin-faker",
468
- label: "Faker.js Mocks",
469
- packageName: "@kubb/plugin-faker",
470
- importName: "pluginFaker",
471
- category: "mocks"
472
- },
473
- {
474
- value: "plugin-msw",
475
- label: "MSW Handlers",
476
- packageName: "@kubb/plugin-msw",
477
- importName: "pluginMsw",
478
- category: "mocks"
479
- },
480
- {
481
- value: "plugin-cypress",
482
- label: "Cypress Tests",
483
- packageName: "@kubb/plugin-cypress",
484
- importName: "pluginCypress",
485
- category: "testing"
486
- },
487
- {
488
- value: "plugin-mcp",
489
- label: "MCP Server (AI / Model Context Protocol)",
490
- packageName: "@kubb/plugin-mcp",
491
- importName: "pluginMcp",
492
- category: "ai"
493
- },
494
- {
495
- value: "plugin-redoc",
496
- label: "ReDoc Documentation",
497
- packageName: "@kubb/plugin-redoc",
498
- importName: "pluginRedoc",
499
- category: "documentation"
500
- }
501
- ];
502
- const pluginDefaultConfigs = {
503
- "plugin-ts": `pluginTs({
504
- output: { path: 'models' },
505
- })`,
506
- "plugin-client": `pluginClient({
507
- output: { path: 'clients' },
508
- })`,
509
- "plugin-react-query": `pluginReactQuery({
510
- output: { path: 'hooks' },
511
- })`,
512
- "plugin-vue-query": `pluginVueQuery({
513
- output: { path: 'hooks' },
514
- })`,
515
- "plugin-zod": `pluginZod({
516
- output: { path: 'zod' },
517
- })`,
518
- "plugin-faker": `pluginFaker({
519
- output: { path: 'mocks' },
520
- })`,
521
- "plugin-msw": `pluginMsw({
522
- output: { path: 'msw' },
523
- })`,
524
- "plugin-cypress": `pluginCypress({
525
- output: { path: 'cypress' },
526
- })`,
527
- "plugin-mcp": `pluginMcp({
528
- output: { path: 'mcp' },
529
- })`,
530
- "plugin-redoc": `pluginRedoc({
531
- output: { path: 'redoc' },
532
- })`
533
- };
534
- //#endregion
535
- //#region ../../internals/shared/src/init.ts
536
- function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
537
- return `import { defineConfig } from 'kubb'
538
- ${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
539
-
540
- export default defineConfig({
541
- root: '.',
542
- input: {
543
- path: '${inputPath}',
544
- },
545
- output: {
546
- path: '${outputPath}',
547
- clean: true,
548
- },
549
- plugins: [
550
- ${selectedPlugins.map((plugin) => {
551
- return ` ${pluginDefaultConfigs[plugin.value] ?? `${plugin.importName}()`},`;
552
- }).join("\n")}
553
- ],
554
- })
555
- `;
556
- }
557
- //#endregion
558
585
  //#region src/schemas/initSchema.ts
559
586
  const initSchema = valibot.object({
560
587
  input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
@@ -563,6 +590,10 @@ const initSchema = valibot.object({
563
590
  });
564
591
  //#endregion
565
592
  //#region src/tools/init.ts
593
+ /**
594
+ * Resolves a comma-separated plugin flag into the matching known plugin options.
595
+ * Unrecognized names are dropped, and a missing flag yields an empty list.
596
+ */
566
597
  function resolvePlugins(pluginsFlag) {
567
598
  if (!pluginsFlag) return [];
568
599
  const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
@@ -605,11 +636,18 @@ const validateTool = (0, tmcp_tool.defineTool)({
605
636
  await mod.adapterOas().validate(input, { throwOnError: true });
606
637
  return tmcp_utils.tool.text(`Validation successful: ${input}`);
607
638
  } catch (err) {
608
- return tmcp_utils.tool.error(`Validation failed:\n${err instanceof Error ? err.message : String(err)}`);
639
+ const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(err));
640
+ return tmcp_utils.tool.error(`Validation failed:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
609
641
  }
610
642
  });
611
643
  //#endregion
612
644
  //#region src/server.ts
645
+ /**
646
+ * Builds the Kubb MCP server with the generate, validate, and init tools registered.
647
+ *
648
+ * @example
649
+ * `const server = createMcpServer()`
650
+ */
613
651
  function createMcpServer() {
614
652
  const server = new tmcp.McpServer({
615
653
  name: "Kubb",
@@ -625,23 +663,21 @@ function createMcpServer() {
625
663
  ]);
626
664
  return server;
627
665
  }
628
- async function startServer({ port, host = "localhost" } = {}) {
629
- const server = createMcpServer();
630
- if (port === void 0) {
631
- new _tmcp_transport_stdio.StdioTransport(server).listen();
632
- return;
633
- }
634
- const transport = new _tmcp_transport_http.HttpTransport(server, { path: "/mcp" });
635
- node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
636
- return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
637
- })).listen(port, host, () => {
638
- console.log(`Kubb MCP server on http://${host}:${port}`);
639
- });
666
+ /**
667
+ * Starts the Kubb MCP server over stdio, the transport every local MCP client
668
+ * (Claude, Copilot, editors) uses when it launches the server as a subprocess.
669
+ */
670
+ async function startServer() {
671
+ new _tmcp_transport_stdio.StdioTransport(createMcpServer()).listen();
640
672
  }
641
673
  //#endregion
642
674
  //#region src/index.ts
643
- async function run(_argv, options) {
644
- await startServer(options);
675
+ /**
676
+ * Entry point that starts the MCP server over stdio. The argument is accepted
677
+ * for CLI parity but ignored.
678
+ */
679
+ async function run(_argv) {
680
+ await startServer();
645
681
  }
646
682
  //#endregion
647
683
  exports.createMcpServer = createMcpServer;