@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/README.md +57 -20
- package/dist/index.cjs +325 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +319 -158
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- 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 +42 -41
- package/src/tools/generate.ts +113 -177
- package/src/tools/init.ts +37 -0
- package/src/tools/validate.ts +25 -0
- package/src/types.ts +1 -1
- package/src/utils/loadUserConfig.ts +32 -31
- 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");
|
|
34
|
-
let
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
231
|
-
loadedModules.set(filePath,
|
|
232
|
-
return
|
|
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
|
-
|
|
258
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
284
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
async function generate(schema
|
|
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,
|
|
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
|
|
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:
|
|
362
|
-
await notify(NotifyTypes.
|
|
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)
|
|
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"
|
|
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
|
-
|
|
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
|
|
431
|
-
return
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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/
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|