@kubb/mcp 5.0.0-beta.2 → 5.0.0-beta.21

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,34 +21,28 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- let node_process = require("node:process");
25
- node_process = __toESM(node_process, 1);
26
- let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
27
- let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
28
- let zod = require("zod");
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
+ let _tmcp_adapter_valibot = require("@tmcp/adapter-valibot");
28
+ let _tmcp_transport_http = require("@tmcp/transport-http");
29
+ let _tmcp_transport_stdio = require("@tmcp/transport-stdio");
30
+ let tmcp = require("tmcp");
29
31
  let node_events = require("node:events");
30
32
  let node_fs = require("node:fs");
33
+ node_fs = __toESM(node_fs, 1);
31
34
  let node_path = require("node:path");
32
35
  node_path = __toESM(node_path, 1);
33
36
  let _kubb_core = require("@kubb/core");
34
- let unrun = require("unrun");
37
+ let tmcp_tool = require("tmcp/tool");
38
+ let tmcp_utils = require("tmcp/utils");
39
+ let valibot = require("valibot");
40
+ valibot = __toESM(valibot, 1);
41
+ let jiti = require("jiti");
42
+ let node_process = require("node:process");
43
+ node_process = __toESM(node_process, 1);
35
44
  //#region package.json
36
- var version = "5.0.0-beta.2";
37
- //#endregion
38
- //#region src/schemas/generateSchema.ts
39
- const generateSchema = zod.z.object({
40
- config: zod.z.string().optional().describe("Path to kubb.config file (supports .ts, .js, .cjs). If not provided, will look for kubb.config.{ts,js,cjs} in current directory"),
41
- input: zod.z.string().optional().describe("Path to OpenAPI/Swagger spec file (overrides config)"),
42
- output: zod.z.string().optional().describe("Output directory path (overrides config)"),
43
- logLevel: zod.z.enum([
44
- "silent",
45
- "error",
46
- "warn",
47
- "info",
48
- "verbose",
49
- "debug"
50
- ]).optional().default("info").describe("Log level for build output")
51
- });
45
+ var version = "5.0.0-beta.21";
52
46
  //#endregion
53
47
  //#region ../../internals/utils/src/errors.ts
54
48
  /**
@@ -96,9 +90,12 @@ var AsyncEventEmitter = class {
96
90
  * await emitter.emit('build', 'petstore')
97
91
  * ```
98
92
  */
99
- async emit(eventName, ...eventArgs) {
93
+ emit(eventName, ...eventArgs) {
100
94
  const listeners = this.#emitter.listeners(eventName);
101
95
  if (listeners.length === 0) return;
96
+ return this.#emitAll(eventName, listeners, eventArgs);
97
+ }
98
+ async #emitAll(eventName, listeners, eventArgs) {
102
99
  for (const listener of listeners) try {
103
100
  await listener(...eventArgs);
104
101
  } catch (err) {
@@ -186,6 +183,21 @@ function isPromise(result) {
186
183
  return result !== null && result !== void 0 && typeof result["then"] === "function";
187
184
  }
188
185
  //#endregion
186
+ //#region src/schemas/generateSchema.ts
187
+ const generateSchema = valibot.object({
188
+ 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"))),
189
+ input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
190
+ output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
191
+ logLevel: valibot.optional(valibot.pipe(valibot.picklist([
192
+ "silent",
193
+ "error",
194
+ "warn",
195
+ "info",
196
+ "verbose",
197
+ "debug"
198
+ ]), valibot.description("Log level for build output")), "info")
199
+ });
200
+ //#endregion
189
201
  //#region src/types.ts
190
202
  const NotifyTypes = {
191
203
  INFO: "INFO",
@@ -195,7 +207,7 @@ const NotifyTypes = {
195
207
  PLUGIN_START: "PLUGIN_START",
196
208
  PLUGIN_END: "PLUGIN_END",
197
209
  FILES_START: "FILES_START",
198
- FILE_UPDATE: "FILE_UPDATE",
210
+ FILES_UPDATE: "FILES_UPDATE",
199
211
  FILES_END: "FILES_END",
200
212
  GENERATION_START: "GENERATION_START",
201
213
  GENERATION_END: "GENERATION_END",
@@ -222,18 +234,23 @@ const ALLOWED_CONFIG_EXTENSIONS = new Set([
222
234
  ]);
223
235
  //#endregion
224
236
  //#region src/utils/loadUserConfig.ts
237
+ const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, {
238
+ jsx: {
239
+ runtime: "automatic",
240
+ importSource: "@kubb/renderer-jsx"
241
+ },
242
+ moduleCache: false
243
+ });
225
244
  const loadedModules = /* @__PURE__ */ new Map();
226
245
  async function loadModule(filePath) {
227
246
  const ext = node_path.default.extname(filePath);
228
247
  if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
229
248
  if (loadedModules.has(filePath)) return loadedModules.get(filePath);
230
- const { module } = await (0, unrun.unrun)({ path: filePath });
231
- loadedModules.set(filePath, module);
232
- return module;
249
+ const mod = await jiti$1.import(filePath, { default: true });
250
+ loadedModules.set(filePath, mod);
251
+ return mod;
233
252
  }
234
253
  async function loadUserConfig(configPath, { notify }) {
235
- let userConfig;
236
- let cwd;
237
254
  if (configPath) {
238
255
  const ext = node_path.default.extname(configPath);
239
256
  if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
@@ -249,41 +266,44 @@ async function loadUserConfig(configPath, { notify }) {
249
266
  await notify(NotifyTypes.CONFIG_ERROR, msg);
250
267
  throw new Error(msg);
251
268
  }
252
- cwd = node_path.default.dirname(resolvedConfigPath);
269
+ const cwd = node_path.default.dirname(resolvedConfigPath);
253
270
  try {
254
- userConfig = await loadModule(resolvedConfigPath);
271
+ const userConfig = await loadModule(resolvedConfigPath);
255
272
  await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
273
+ return {
274
+ userConfig,
275
+ cwd
276
+ };
256
277
  } catch (error) {
257
- await notify(NotifyTypes.CONFIG_ERROR, `Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
258
- throw new Error(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
259
- }
260
- } else {
261
- cwd = process.cwd();
262
- const configFileNames = [
263
- "kubb.config.ts",
264
- "kubb.config.mts",
265
- "kubb.config.cts",
266
- "kubb.config.js",
267
- "kubb.config.cjs"
268
- ];
269
- for (const configFileName of configFileNames) {
270
- const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
271
- if (!(0, node_fs.existsSync)(configFilePath)) continue;
272
- try {
273
- userConfig = await loadModule(configFilePath);
274
- await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
275
- break;
276
- } catch {}
278
+ const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
279
+ await notify(NotifyTypes.CONFIG_ERROR, msg);
280
+ throw new Error(msg);
277
281
  }
278
- if (!userConfig) {
279
- await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
280
- throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
282
+ }
283
+ const cwd = process.cwd();
284
+ const configFileNames = [
285
+ "kubb.config.ts",
286
+ "kubb.config.mts",
287
+ "kubb.config.cts",
288
+ "kubb.config.js",
289
+ "kubb.config.cjs"
290
+ ];
291
+ for (const configFileName of configFileNames) {
292
+ const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
293
+ if (!(0, node_fs.existsSync)(configFilePath)) continue;
294
+ try {
295
+ const userConfig = await loadModule(configFilePath);
296
+ await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
297
+ return {
298
+ userConfig,
299
+ cwd
300
+ };
301
+ } catch (err) {
302
+ await notify(NotifyTypes.CONFIG_ERROR, `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
281
303
  }
282
304
  }
283
- return {
284
- userConfig,
285
- cwd
286
- };
305
+ await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
306
+ throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
287
307
  }
288
308
  //#endregion
289
309
  //#region src/utils/resolveCwd.ts
@@ -302,40 +322,27 @@ function resolveCwd(userConfig, cwd) {
302
322
  }
303
323
  //#endregion
304
324
  //#region src/utils/resolveUserConfig.ts
305
- /**
306
- * Resolve the config by handling function configs and returning the final configuration
307
- */
308
325
  async function resolveUserConfig(config, options) {
309
- let kubbUserConfig = Promise.resolve(config);
310
- if (typeof config === "function") {
311
- const possiblePromise = config({
312
- logLevel: options.logLevel,
313
- config: options.configPath
314
- });
315
- if (isPromise(possiblePromise)) kubbUserConfig = possiblePromise;
316
- else kubbUserConfig = Promise.resolve(possiblePromise);
317
- }
318
- return await kubbUserConfig;
326
+ const result = typeof config === "function" ? config({
327
+ logLevel: options.logLevel,
328
+ config: options.configPath
329
+ }) : config;
330
+ const resolved = isPromise(result) ? await result : result;
331
+ return Array.isArray(resolved) ? resolved[0] : resolved;
319
332
  }
320
333
  //#endregion
321
334
  //#region src/tools/generate.ts
322
- /**
323
- * Build tool that generates code from OpenAPI specs using Kubb.
324
- * Sends real-time notifications of build progress and events.
325
- */
326
- async function generate(schema, handler) {
335
+ const generateTool = (0, tmcp_tool.defineTool)({
336
+ name: "generate",
337
+ description: "Generate OpenAPI spec helpers using Kubb configuration",
338
+ schema: generateSchema
339
+ }, async function generate(schema) {
327
340
  const { config: configPath, input, output, logLevel } = schema;
328
341
  try {
329
342
  const hooks = new AsyncEventEmitter();
330
343
  const messages = [];
331
- const notify = async (type, message, data) => {
344
+ const notify = async (type, message, _data) => {
332
345
  messages.push(`${type}: ${message}`);
333
- await handler.sendNotification("kubb/progress", {
334
- type,
335
- message,
336
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
337
- ...data
338
- });
339
346
  };
340
347
  hooks.on("kubb:info", async ({ message }) => {
341
348
  await notify(NotifyTypes.INFO, message);
@@ -344,7 +351,7 @@ async function generate(schema, handler) {
344
351
  await notify(NotifyTypes.SUCCESS, message);
345
352
  });
346
353
  hooks.on("kubb:error", async ({ error }) => {
347
- await notify(NotifyTypes.ERROR, error.message, { stack: error.stack });
354
+ await notify(NotifyTypes.ERROR, error.message);
348
355
  });
349
356
  hooks.on("kubb:warn", async ({ message }) => {
350
357
  await notify(NotifyTypes.WARN, message);
@@ -358,8 +365,8 @@ async function generate(schema, handler) {
358
365
  hooks.on("kubb:files:processing:start", async () => {
359
366
  await notify(NotifyTypes.FILES_START, "Starting file processing");
360
367
  });
361
- hooks.on("kubb:file:processing:update", async ({ file }) => {
362
- await notify(NotifyTypes.FILE_UPDATE, `Processing file: ${file.name}`);
368
+ hooks.on("kubb:files:processing:update", async ({ files }) => {
369
+ await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
363
370
  });
364
371
  hooks.on("kubb:files:processing:end", async () => {
365
372
  await notify(NotifyTypes.FILES_END, "File processing complete");
@@ -376,7 +383,7 @@ async function generate(schema, handler) {
376
383
  const configResult = await loadUserConfig(configPath, { notify });
377
384
  userConfig = configResult.userConfig;
378
385
  cwd = configResult.cwd;
379
- if (Array.isArray(userConfig) && userConfig.length) throw new Error("Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.");
386
+ if (Array.isArray(userConfig)) throw new Error("Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.");
380
387
  userConfig = await resolveUserConfig(userConfig, {
381
388
  configPath,
382
389
  logLevel
@@ -384,15 +391,9 @@ async function generate(schema, handler) {
384
391
  } catch (error) {
385
392
  const errorMessage = error instanceof Error ? error.message : String(error);
386
393
  await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
387
- return {
388
- content: [{
389
- type: "text",
390
- text: errorMessage
391
- }],
392
- isError: true
393
- };
394
+ return tmcp_utils.tool.error(errorMessage);
394
395
  }
395
- const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0);
396
+ const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
396
397
  const config = {
397
398
  ...userConfig,
398
399
  root: resolveCwd(userConfig, cwd),
@@ -405,7 +406,7 @@ async function generate(schema, handler) {
405
406
  path: output
406
407
  } : userConfig.output
407
408
  };
408
- await notify(NotifyTypes.CONFIG_READY, "Configuration ready", { root: config.root });
409
+ await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
409
410
  await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
410
411
  const kubb = (0, _kubb_core.createKubb)(config, { hooks });
411
412
  await kubb.setup();
@@ -415,79 +416,246 @@ async function generate(schema, handler) {
415
416
  await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
416
417
  if (error || failedPlugins.size > 0) {
417
418
  const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
418
- await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)`, {
419
- errorCount: allErrors.length,
420
- errors: allErrors.map((err) => err.message)
421
- });
422
- return {
423
- content: [{
424
- type: "text",
425
- text: `Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`
426
- }],
427
- isError: true
428
- };
419
+ await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)`);
420
+ return tmcp_utils.tool.error(`Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`);
429
421
  }
430
- await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`, { filesCount: files.length });
431
- return { content: [{
432
- type: "text",
433
- text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`
434
- }] };
422
+ await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
423
+ return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
435
424
  } catch (caughtError) {
436
- const error = caughtError;
437
- await handler.sendNotification("kubb/progress", {
438
- type: NotifyTypes.FATAL_ERROR,
439
- message: error.message,
440
- stack: error.stack,
441
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
442
- });
443
- return {
444
- content: [{
445
- type: "text",
446
- text: `Build error: ${error.message}\n${error.stack || ""}`
447
- }],
448
- isError: true
449
- };
425
+ const error = toError(caughtError);
426
+ return tmcp_utils.tool.error(`Build error: ${error.message}\n${error.stack ?? ""}`);
427
+ }
428
+ });
429
+ //#endregion
430
+ //#region ../../internals/shared/src/constants.ts
431
+ const KUBB_CONFIG_FILENAME = "kubb.config.ts";
432
+ const availablePlugins = [
433
+ {
434
+ value: "plugin-ts",
435
+ label: "TypeScript",
436
+ hint: "Recommended",
437
+ packageName: "@kubb/plugin-ts",
438
+ importName: "pluginTs",
439
+ category: "types"
440
+ },
441
+ {
442
+ value: "plugin-client",
443
+ label: "Client (Fetch/Axios)",
444
+ packageName: "@kubb/plugin-client",
445
+ importName: "pluginClient",
446
+ category: "client"
447
+ },
448
+ {
449
+ value: "plugin-react-query",
450
+ label: "React Query / TanStack Query",
451
+ packageName: "@kubb/plugin-react-query",
452
+ importName: "pluginReactQuery",
453
+ category: "framework"
454
+ },
455
+ {
456
+ value: "plugin-vue-query",
457
+ label: "Vue Query",
458
+ packageName: "@kubb/plugin-vue-query",
459
+ importName: "pluginVueQuery",
460
+ category: "framework"
461
+ },
462
+ {
463
+ value: "plugin-zod",
464
+ label: "Zod Schemas",
465
+ packageName: "@kubb/plugin-zod",
466
+ importName: "pluginZod",
467
+ category: "validation"
468
+ },
469
+ {
470
+ value: "plugin-faker",
471
+ label: "Faker.js Mocks",
472
+ packageName: "@kubb/plugin-faker",
473
+ importName: "pluginFaker",
474
+ category: "mocks"
475
+ },
476
+ {
477
+ value: "plugin-msw",
478
+ label: "MSW Handlers",
479
+ packageName: "@kubb/plugin-msw",
480
+ importName: "pluginMsw",
481
+ category: "mocks"
482
+ },
483
+ {
484
+ value: "plugin-cypress",
485
+ label: "Cypress Tests",
486
+ packageName: "@kubb/plugin-cypress",
487
+ importName: "pluginCypress",
488
+ category: "testing"
489
+ },
490
+ {
491
+ value: "plugin-mcp",
492
+ label: "MCP Server (AI / Model Context Protocol)",
493
+ packageName: "@kubb/plugin-mcp",
494
+ importName: "pluginMcp",
495
+ category: "ai"
496
+ },
497
+ {
498
+ value: "plugin-redoc",
499
+ label: "ReDoc Documentation",
500
+ packageName: "@kubb/plugin-redoc",
501
+ importName: "pluginRedoc",
502
+ category: "documentation"
450
503
  }
504
+ ];
505
+ const pluginDefaultConfigs = {
506
+ "plugin-ts": `pluginTs({
507
+ output: { path: 'models' },
508
+ })`,
509
+ "plugin-client": `pluginClient({
510
+ output: { path: 'clients' },
511
+ })`,
512
+ "plugin-react-query": `pluginReactQuery({
513
+ output: { path: 'hooks' },
514
+ })`,
515
+ "plugin-vue-query": `pluginVueQuery({
516
+ output: { path: 'hooks' },
517
+ })`,
518
+ "plugin-zod": `pluginZod({
519
+ output: { path: 'zod' },
520
+ })`,
521
+ "plugin-faker": `pluginFaker({
522
+ output: { path: 'mocks' },
523
+ })`,
524
+ "plugin-msw": `pluginMsw({
525
+ output: { path: 'msw' },
526
+ })`,
527
+ "plugin-cypress": `pluginCypress({
528
+ output: { path: 'cypress' },
529
+ })`,
530
+ "plugin-mcp": `pluginMcp({
531
+ output: { path: 'mcp' },
532
+ })`,
533
+ "plugin-redoc": `pluginRedoc({
534
+ output: { path: 'redoc' },
535
+ })`
536
+ };
537
+ //#endregion
538
+ //#region ../../internals/shared/src/init.ts
539
+ function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
540
+ return `import { defineConfig } from 'kubb'
541
+ ${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
542
+
543
+ export default defineConfig({
544
+ root: '.',
545
+ input: {
546
+ path: '${inputPath}',
547
+ },
548
+ output: {
549
+ path: '${outputPath}',
550
+ clean: true,
551
+ },
552
+ plugins: [
553
+ ${selectedPlugins.map((plugin) => {
554
+ return ` ${pluginDefaultConfigs[plugin.value] ?? `${plugin.importName}()`},`;
555
+ }).join("\n")}
556
+ ],
557
+ })
558
+ `;
451
559
  }
452
560
  //#endregion
453
- //#region src/server.ts
454
- /**
455
- * Kubb MCP Server
456
- *
457
- * Provides tools for building OpenAPI specifications using Kubb.
458
- */
459
- async function startServer() {
561
+ //#region src/schemas/initSchema.ts
562
+ const initSchema = valibot.object({
563
+ input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
564
+ output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory (default: ./src/gen)"))),
565
+ plugins: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Comma-separated list of plugins: plugin-ts,plugin-zod,...")))
566
+ });
567
+ //#endregion
568
+ //#region src/tools/init.ts
569
+ function resolvePlugins(pluginsFlag) {
570
+ if (!pluginsFlag) return [];
571
+ const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
572
+ return availablePlugins.filter((p) => requested.includes(p.value));
573
+ }
574
+ const initTool = (0, tmcp_tool.defineTool)({
575
+ name: "init",
576
+ description: "Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.",
577
+ schema: initSchema
578
+ }, async ({ input = "./openapi.yaml", output = "./src/gen", plugins }) => {
579
+ const selected = resolvePlugins(plugins);
580
+ const content = generateConfigFile({
581
+ selectedPlugins: selected,
582
+ inputPath: input,
583
+ outputPath: output
584
+ });
585
+ const dest = node_path.default.join(node_process.default.cwd(), KUBB_CONFIG_FILENAME);
586
+ if (node_fs.default.existsSync(dest)) return tmcp_utils.tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`);
587
+ node_fs.default.writeFileSync(dest, content, "utf-8");
588
+ const packageList = ["kubb", ...selected.map((p) => p.packageName)].join(" ");
589
+ return tmcp_utils.tool.text(`Created kubb.config.ts\n\nInstall packages:\n npm install ${packageList}\n\nThen run:\n npx kubb generate`);
590
+ });
591
+ //#endregion
592
+ //#region src/schemas/validateSchema.ts
593
+ const validateSchema = valibot.object({ input: valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path or URL to the OpenAPI/Swagger specification")) });
594
+ //#endregion
595
+ //#region src/tools/validate.ts
596
+ const validateTool = (0, tmcp_tool.defineTool)({
597
+ name: "validate",
598
+ description: "Validate an OpenAPI/Swagger specification file or URL",
599
+ schema: validateSchema
600
+ }, async ({ input }) => {
601
+ let mod;
460
602
  try {
461
- const transport = new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
462
- const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
463
- name: "Kubb",
464
- version
465
- });
466
- server.tool("generate", "Generate OpenAPI spec helpers using Kubb configuration", generateSchema.shape, async (args) => {
467
- return generate(args, { sendNotification: async (method, params) => {
468
- try {
469
- await transport.send({
470
- jsonrpc: "2.0",
471
- method,
472
- params
473
- });
474
- } catch (error) {
475
- console.error("Failed to send notification:", error);
476
- }
477
- } });
478
- });
479
- await server.connect(transport);
480
- } catch (error) {
481
- console.error("Failed to start MCP server:", error);
482
- node_process.default.exit(1);
603
+ mod = await import("@kubb/adapter-oas");
604
+ } catch {
605
+ return tmcp_utils.tool.error("The validate tool requires @kubb/adapter-oas.\nInstall: npm install @kubb/adapter-oas");
606
+ }
607
+ try {
608
+ await mod.adapterOas().validate(input, { throwOnError: true });
609
+ return tmcp_utils.tool.text(`Validation successful: ${input}`);
610
+ } catch (err) {
611
+ return tmcp_utils.tool.error(`Validation failed:\n${err instanceof Error ? err.message : String(err)}`);
612
+ }
613
+ });
614
+ //#endregion
615
+ //#region src/server.ts
616
+ function createMcpServer() {
617
+ const server = new tmcp.McpServer({
618
+ name: "Kubb",
619
+ version
620
+ }, {
621
+ adapter: new _tmcp_adapter_valibot.ValibotJsonSchemaAdapter(),
622
+ capabilities: { tools: {} }
623
+ });
624
+ server.tools([
625
+ generateTool,
626
+ validateTool,
627
+ initTool
628
+ ]);
629
+ return server;
630
+ }
631
+ async function startServer({ port, host = "localhost" } = {}) {
632
+ const server = createMcpServer();
633
+ if (port === void 0) {
634
+ new _tmcp_transport_stdio.StdioTransport(server).listen();
635
+ return;
483
636
  }
637
+ const transport = new _tmcp_transport_http.HttpTransport(server, { path: "/mcp" });
638
+ const httpServer = node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
639
+ return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
640
+ }));
641
+ httpServer.listen(port, host, () => {
642
+ console.log(`Kubb MCP server on http://${host}:${port}`);
643
+ });
644
+ const closeServer = () => httpServer.close();
645
+ process.once("SIGINT", closeServer);
646
+ process.once("SIGTERM", closeServer);
647
+ httpServer.once("close", () => {
648
+ process.off("SIGINT", closeServer);
649
+ process.off("SIGTERM", closeServer);
650
+ });
484
651
  }
485
652
  //#endregion
486
653
  //#region src/index.ts
487
- async function run(_argv) {
488
- await startServer();
654
+ async function run(_argv, options) {
655
+ await startServer(options);
489
656
  }
490
657
  //#endregion
658
+ exports.createMcpServer = createMcpServer;
491
659
  exports.run = run;
492
660
 
493
661
  //# sourceMappingURL=index.cjs.map