@kubb/mcp 5.0.0-beta.5 → 5.0.0-beta.6
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 +295 -148
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +289 -149
- package/dist/index.js.map +1 -1
- package/package.json +13 -7
- package/src/index.ts +5 -2
- package/src/schemas/generateSchema.ts +11 -10
- package/src/schemas/initSchema.ts +7 -0
- package/src/schemas/validateSchema.ts +5 -0
- package/src/server.ts +34 -41
- package/src/tools/generate.ts +113 -177
- package/src/tools/init.ts +37 -0
- package/src/tools/validate.ts +25 -0
- package/src/utils/loadUserConfig.ts +20 -27
- package/src/utils/resolveUserConfig.ts +5 -20
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
|
|
25
|
-
|
|
26
|
-
let
|
|
27
|
-
let
|
|
28
|
-
let
|
|
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");
|
|
37
|
+
let tmcp_tool = require("tmcp/tool");
|
|
38
|
+
let tmcp_utils = require("tmcp/utils");
|
|
39
|
+
let valibot = require("valibot");
|
|
40
|
+
valibot = __toESM(valibot, 1);
|
|
34
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.
|
|
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.6";
|
|
52
46
|
//#endregion
|
|
53
47
|
//#region ../../internals/utils/src/errors.ts
|
|
54
48
|
/**
|
|
@@ -186,6 +180,21 @@ function isPromise(result) {
|
|
|
186
180
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
187
181
|
}
|
|
188
182
|
//#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
|
|
189
198
|
//#region src/types.ts
|
|
190
199
|
const NotifyTypes = {
|
|
191
200
|
INFO: "INFO",
|
|
@@ -239,8 +248,6 @@ async function loadModule(filePath) {
|
|
|
239
248
|
return mod;
|
|
240
249
|
}
|
|
241
250
|
async function loadUserConfig(configPath, { notify }) {
|
|
242
|
-
let userConfig;
|
|
243
|
-
let cwd;
|
|
244
251
|
if (configPath) {
|
|
245
252
|
const ext = node_path.default.extname(configPath);
|
|
246
253
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
@@ -256,41 +263,44 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
256
263
|
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
257
264
|
throw new Error(msg);
|
|
258
265
|
}
|
|
259
|
-
cwd = node_path.default.dirname(resolvedConfigPath);
|
|
266
|
+
const cwd = node_path.default.dirname(resolvedConfigPath);
|
|
260
267
|
try {
|
|
261
|
-
userConfig = await loadModule(resolvedConfigPath);
|
|
268
|
+
const userConfig = await loadModule(resolvedConfigPath);
|
|
262
269
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
|
|
270
|
+
return {
|
|
271
|
+
userConfig,
|
|
272
|
+
cwd
|
|
273
|
+
};
|
|
263
274
|
} catch (error) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
} else {
|
|
268
|
-
cwd = process.cwd();
|
|
269
|
-
const configFileNames = [
|
|
270
|
-
"kubb.config.ts",
|
|
271
|
-
"kubb.config.mts",
|
|
272
|
-
"kubb.config.cts",
|
|
273
|
-
"kubb.config.js",
|
|
274
|
-
"kubb.config.cjs"
|
|
275
|
-
];
|
|
276
|
-
for (const configFileName of configFileNames) {
|
|
277
|
-
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
278
|
-
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
279
|
-
try {
|
|
280
|
-
userConfig = await loadModule(configFilePath);
|
|
281
|
-
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
282
|
-
break;
|
|
283
|
-
} catch {}
|
|
275
|
+
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
276
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
277
|
+
throw new Error(msg);
|
|
284
278
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
279
|
+
}
|
|
280
|
+
const cwd = process.cwd();
|
|
281
|
+
const configFileNames = [
|
|
282
|
+
"kubb.config.ts",
|
|
283
|
+
"kubb.config.mts",
|
|
284
|
+
"kubb.config.cts",
|
|
285
|
+
"kubb.config.js",
|
|
286
|
+
"kubb.config.cjs"
|
|
287
|
+
];
|
|
288
|
+
for (const configFileName of configFileNames) {
|
|
289
|
+
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
290
|
+
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
291
|
+
try {
|
|
292
|
+
const userConfig = await loadModule(configFilePath);
|
|
293
|
+
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
294
|
+
return {
|
|
295
|
+
userConfig,
|
|
296
|
+
cwd
|
|
297
|
+
};
|
|
298
|
+
} catch (err) {
|
|
299
|
+
await notify(NotifyTypes.CONFIG_ERROR, `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
288
300
|
}
|
|
289
301
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
cwd
|
|
293
|
-
};
|
|
302
|
+
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
303
|
+
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
294
304
|
}
|
|
295
305
|
//#endregion
|
|
296
306
|
//#region src/utils/resolveCwd.ts
|
|
@@ -309,40 +319,27 @@ function resolveCwd(userConfig, cwd) {
|
|
|
309
319
|
}
|
|
310
320
|
//#endregion
|
|
311
321
|
//#region src/utils/resolveUserConfig.ts
|
|
312
|
-
/**
|
|
313
|
-
* Resolve the config by handling function configs and returning the final configuration
|
|
314
|
-
*/
|
|
315
322
|
async function resolveUserConfig(config, options) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (isPromise(possiblePromise)) kubbUserConfig = possiblePromise;
|
|
323
|
-
else kubbUserConfig = Promise.resolve(possiblePromise);
|
|
324
|
-
}
|
|
325
|
-
return await kubbUserConfig;
|
|
323
|
+
const result = typeof config === "function" ? config({
|
|
324
|
+
logLevel: options.logLevel,
|
|
325
|
+
config: options.configPath
|
|
326
|
+
}) : config;
|
|
327
|
+
const resolved = isPromise(result) ? await result : result;
|
|
328
|
+
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
326
329
|
}
|
|
327
330
|
//#endregion
|
|
328
331
|
//#region src/tools/generate.ts
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
async function generate(schema
|
|
332
|
+
const generateTool = (0, tmcp_tool.defineTool)({
|
|
333
|
+
name: "generate",
|
|
334
|
+
description: "Generate OpenAPI spec helpers using Kubb configuration",
|
|
335
|
+
schema: generateSchema
|
|
336
|
+
}, async function generate(schema) {
|
|
334
337
|
const { config: configPath, input, output, logLevel } = schema;
|
|
335
338
|
try {
|
|
336
339
|
const hooks = new AsyncEventEmitter();
|
|
337
340
|
const messages = [];
|
|
338
|
-
const notify = async (type, message,
|
|
341
|
+
const notify = async (type, message, _data) => {
|
|
339
342
|
messages.push(`${type}: ${message}`);
|
|
340
|
-
await handler.sendNotification("kubb/progress", {
|
|
341
|
-
type,
|
|
342
|
-
message,
|
|
343
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
344
|
-
...data
|
|
345
|
-
});
|
|
346
343
|
};
|
|
347
344
|
hooks.on("kubb:info", async ({ message }) => {
|
|
348
345
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -351,7 +348,7 @@ async function generate(schema, handler) {
|
|
|
351
348
|
await notify(NotifyTypes.SUCCESS, message);
|
|
352
349
|
});
|
|
353
350
|
hooks.on("kubb:error", async ({ error }) => {
|
|
354
|
-
await notify(NotifyTypes.ERROR, error.message
|
|
351
|
+
await notify(NotifyTypes.ERROR, error.message);
|
|
355
352
|
});
|
|
356
353
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
357
354
|
await notify(NotifyTypes.WARN, message);
|
|
@@ -383,7 +380,7 @@ async function generate(schema, handler) {
|
|
|
383
380
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
384
381
|
userConfig = configResult.userConfig;
|
|
385
382
|
cwd = configResult.cwd;
|
|
386
|
-
if (Array.isArray(userConfig)
|
|
383
|
+
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.");
|
|
387
384
|
userConfig = await resolveUserConfig(userConfig, {
|
|
388
385
|
configPath,
|
|
389
386
|
logLevel
|
|
@@ -391,13 +388,7 @@ async function generate(schema, handler) {
|
|
|
391
388
|
} catch (error) {
|
|
392
389
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
393
390
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
|
|
394
|
-
return
|
|
395
|
-
content: [{
|
|
396
|
-
type: "text",
|
|
397
|
-
text: errorMessage
|
|
398
|
-
}],
|
|
399
|
-
isError: true
|
|
400
|
-
};
|
|
391
|
+
return tmcp_utils.tool.error(errorMessage);
|
|
401
392
|
}
|
|
402
393
|
const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
|
|
403
394
|
const config = {
|
|
@@ -412,7 +403,7 @@ async function generate(schema, handler) {
|
|
|
412
403
|
path: output
|
|
413
404
|
} : userConfig.output
|
|
414
405
|
};
|
|
415
|
-
await notify(NotifyTypes.CONFIG_READY, "Configuration ready"
|
|
406
|
+
await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
|
|
416
407
|
await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
|
|
417
408
|
const kubb = (0, _kubb_core.createKubb)(config, { hooks });
|
|
418
409
|
await kubb.setup();
|
|
@@ -422,79 +413,235 @@ async function generate(schema, handler) {
|
|
|
422
413
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
423
414
|
if (error || failedPlugins.size > 0) {
|
|
424
415
|
const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
|
|
425
|
-
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)
|
|
426
|
-
|
|
427
|
-
errors: allErrors.map((err) => err.message)
|
|
428
|
-
});
|
|
429
|
-
return {
|
|
430
|
-
content: [{
|
|
431
|
-
type: "text",
|
|
432
|
-
text: `Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`
|
|
433
|
-
}],
|
|
434
|
-
isError: true
|
|
435
|
-
};
|
|
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")}`);
|
|
436
418
|
}
|
|
437
|
-
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files
|
|
438
|
-
return
|
|
439
|
-
type: "text",
|
|
440
|
-
text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`
|
|
441
|
-
}] };
|
|
419
|
+
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
420
|
+
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
442
421
|
} catch (caughtError) {
|
|
443
|
-
const error = caughtError;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
422
|
+
const error = toError(caughtError);
|
|
423
|
+
return tmcp_utils.tool.error(`Build error: ${error.message}\n${error.stack ?? ""}`);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
//#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"
|
|
457
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
|
+
`;
|
|
458
556
|
}
|
|
459
557
|
//#endregion
|
|
460
|
-
//#region src/
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
558
|
+
//#region src/schemas/initSchema.ts
|
|
559
|
+
const initSchema = valibot.object({
|
|
560
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
561
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory (default: ./src/gen)"))),
|
|
562
|
+
plugins: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Comma-separated list of plugins: plugin-ts,plugin-zod,...")))
|
|
563
|
+
});
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/tools/init.ts
|
|
566
|
+
function resolvePlugins(pluginsFlag) {
|
|
567
|
+
if (!pluginsFlag) return [];
|
|
568
|
+
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
569
|
+
return availablePlugins.filter((p) => requested.includes(p.value));
|
|
570
|
+
}
|
|
571
|
+
const initTool = (0, tmcp_tool.defineTool)({
|
|
572
|
+
name: "init",
|
|
573
|
+
description: "Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.",
|
|
574
|
+
schema: initSchema
|
|
575
|
+
}, async ({ input = "./openapi.yaml", output = "./src/gen", plugins }) => {
|
|
576
|
+
const selected = resolvePlugins(plugins);
|
|
577
|
+
const content = generateConfigFile({
|
|
578
|
+
selectedPlugins: selected,
|
|
579
|
+
inputPath: input,
|
|
580
|
+
outputPath: output
|
|
581
|
+
});
|
|
582
|
+
const dest = node_path.default.join(node_process.default.cwd(), KUBB_CONFIG_FILENAME);
|
|
583
|
+
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.`);
|
|
584
|
+
node_fs.default.writeFileSync(dest, content, "utf-8");
|
|
585
|
+
const packageList = ["kubb", ...selected.map((p) => p.packageName)].join(" ");
|
|
586
|
+
return tmcp_utils.tool.text(`Created kubb.config.ts\n\nInstall packages:\n npm install ${packageList}\n\nThen run:\n npx kubb generate`);
|
|
587
|
+
});
|
|
588
|
+
//#endregion
|
|
589
|
+
//#region src/schemas/validateSchema.ts
|
|
590
|
+
const validateSchema = valibot.object({ input: valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path or URL to the OpenAPI/Swagger specification")) });
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/tools/validate.ts
|
|
593
|
+
const validateTool = (0, tmcp_tool.defineTool)({
|
|
594
|
+
name: "validate",
|
|
595
|
+
description: "Validate an OpenAPI/Swagger specification file or URL",
|
|
596
|
+
schema: validateSchema
|
|
597
|
+
}, async ({ input }) => {
|
|
598
|
+
let mod;
|
|
467
599
|
try {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
600
|
+
mod = await import("@kubb/adapter-oas");
|
|
601
|
+
} catch {
|
|
602
|
+
return tmcp_utils.tool.error("The validate tool requires @kubb/adapter-oas.\nInstall: npm install @kubb/adapter-oas");
|
|
603
|
+
}
|
|
604
|
+
try {
|
|
605
|
+
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
606
|
+
return tmcp_utils.tool.text(`Validation successful: ${input}`);
|
|
607
|
+
} catch (err) {
|
|
608
|
+
return tmcp_utils.tool.error(`Validation failed:\n${err instanceof Error ? err.message : String(err)}`);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region src/server.ts
|
|
613
|
+
function createMcpServer() {
|
|
614
|
+
const server = new tmcp.McpServer({
|
|
615
|
+
name: "Kubb",
|
|
616
|
+
version
|
|
617
|
+
}, { adapter: new _tmcp_adapter_valibot.ValibotJsonSchemaAdapter() });
|
|
618
|
+
server.tools([
|
|
619
|
+
generateTool,
|
|
620
|
+
validateTool,
|
|
621
|
+
initTool
|
|
622
|
+
]);
|
|
623
|
+
return server;
|
|
624
|
+
}
|
|
625
|
+
async function startServer({ port, host = "localhost" } = {}) {
|
|
626
|
+
const server = createMcpServer();
|
|
627
|
+
if (port === void 0) {
|
|
628
|
+
new _tmcp_transport_stdio.StdioTransport(server).listen();
|
|
629
|
+
return;
|
|
490
630
|
}
|
|
631
|
+
const transport = new _tmcp_transport_http.HttpTransport(server, { path: "/mcp" });
|
|
632
|
+
node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
|
|
633
|
+
return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
|
|
634
|
+
})).listen(port, host, () => {
|
|
635
|
+
console.log(`Kubb MCP server on http://${host}:${port}`);
|
|
636
|
+
});
|
|
491
637
|
}
|
|
492
638
|
//#endregion
|
|
493
639
|
//#region src/index.ts
|
|
494
|
-
async function run(_argv) {
|
|
495
|
-
await startServer();
|
|
640
|
+
async function run(_argv, options) {
|
|
641
|
+
await startServer(options);
|
|
496
642
|
}
|
|
497
643
|
//#endregion
|
|
644
|
+
exports.createMcpServer = createMcpServer;
|
|
498
645
|
exports.run = run;
|
|
499
646
|
|
|
500
647
|
//# sourceMappingURL=index.cjs.map
|