@kubb/mcp 5.0.0-beta.5 → 5.0.0-beta.51
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/LICENSE +17 -10
- package/README.md +64 -17
- package/dist/index.cjs +358 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +353 -158
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
- 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 +115 -178
- package/src/tools/init.ts +37 -0
- package/src/tools/validate.ts +28 -0
- package/src/types.ts +2 -1
- package/src/utils/formatDiagnostics.ts +29 -0
- package/src/utils/loadUserConfig.ts +20 -27
- package/src/utils/resolveUserConfig.ts +5 -20
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
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");
|
|
32
|
+
let _kubb_core = require("@kubb/core");
|
|
33
|
+
let tmcp_tool = require("tmcp/tool");
|
|
34
|
+
let tmcp_utils = require("tmcp/utils");
|
|
35
|
+
let valibot = require("valibot");
|
|
36
|
+
valibot = __toESM(valibot, 1);
|
|
30
37
|
let node_fs = require("node:fs");
|
|
38
|
+
node_fs = __toESM(node_fs, 1);
|
|
31
39
|
let node_path = require("node:path");
|
|
32
40
|
node_path = __toESM(node_path, 1);
|
|
33
|
-
let _kubb_core = require("@kubb/core");
|
|
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.51";
|
|
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) {
|
|
@@ -161,6 +158,24 @@ var AsyncEventEmitter = class {
|
|
|
161
158
|
return this.#emitter.listenerCount(eventName);
|
|
162
159
|
}
|
|
163
160
|
/**
|
|
161
|
+
* Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
|
|
162
|
+
* Set this above the expected listener count when many listeners attach by design.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* emitter.setMaxListeners(40)
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
setMaxListeners(max) {
|
|
170
|
+
this.#emitter.setMaxListeners(max);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Returns the current per-event listener ceiling.
|
|
174
|
+
*/
|
|
175
|
+
getMaxListeners() {
|
|
176
|
+
return this.#emitter.getMaxListeners();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
164
179
|
* Removes all listeners from every event channel.
|
|
165
180
|
*
|
|
166
181
|
* @example
|
|
@@ -186,16 +201,48 @@ function isPromise(result) {
|
|
|
186
201
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
187
202
|
}
|
|
188
203
|
//#endregion
|
|
204
|
+
//#region src/schemas/generateSchema.ts
|
|
205
|
+
const generateSchema = valibot.object({
|
|
206
|
+
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"))),
|
|
207
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
|
|
208
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
|
|
209
|
+
logLevel: valibot.optional(valibot.pipe(valibot.picklist([
|
|
210
|
+
"silent",
|
|
211
|
+
"info",
|
|
212
|
+
"verbose"
|
|
213
|
+
]), valibot.description("Log level for build output")), "info")
|
|
214
|
+
});
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/utils/formatDiagnostics.ts
|
|
217
|
+
/**
|
|
218
|
+
* Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry
|
|
219
|
+
* keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so
|
|
220
|
+
* the agent can act on the problem rather than parsing a bare message. No ANSI styling,
|
|
221
|
+
* unlike the CLI renderer.
|
|
222
|
+
*/
|
|
223
|
+
function formatDiagnostics(diagnostics) {
|
|
224
|
+
return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join("\n\n");
|
|
225
|
+
}
|
|
226
|
+
function formatDiagnostic(diagnostic) {
|
|
227
|
+
const { code, severity, message, location, help, plugin, docsUrl } = diagnostic;
|
|
228
|
+
const lines = [`${severity} ${plugin ? `${plugin}(${code})` : code}: ${message}`];
|
|
229
|
+
if (location && "pointer" in location) lines.push(` at ${location.pointer}`);
|
|
230
|
+
if (help) lines.push(` help: ${help}`);
|
|
231
|
+
if (docsUrl) lines.push(` docs: ${docsUrl}`);
|
|
232
|
+
return lines.join("\n");
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
189
235
|
//#region src/types.ts
|
|
190
236
|
const NotifyTypes = {
|
|
191
237
|
INFO: "INFO",
|
|
192
238
|
SUCCESS: "SUCCESS",
|
|
193
239
|
ERROR: "ERROR",
|
|
194
240
|
WARN: "WARN",
|
|
241
|
+
DIAGNOSTIC: "DIAGNOSTIC",
|
|
195
242
|
PLUGIN_START: "PLUGIN_START",
|
|
196
243
|
PLUGIN_END: "PLUGIN_END",
|
|
197
244
|
FILES_START: "FILES_START",
|
|
198
|
-
|
|
245
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
199
246
|
FILES_END: "FILES_END",
|
|
200
247
|
GENERATION_START: "GENERATION_START",
|
|
201
248
|
GENERATION_END: "GENERATION_END",
|
|
@@ -239,8 +286,6 @@ async function loadModule(filePath) {
|
|
|
239
286
|
return mod;
|
|
240
287
|
}
|
|
241
288
|
async function loadUserConfig(configPath, { notify }) {
|
|
242
|
-
let userConfig;
|
|
243
|
-
let cwd;
|
|
244
289
|
if (configPath) {
|
|
245
290
|
const ext = node_path.default.extname(configPath);
|
|
246
291
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
@@ -256,41 +301,44 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
256
301
|
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
257
302
|
throw new Error(msg);
|
|
258
303
|
}
|
|
259
|
-
cwd = node_path.default.dirname(resolvedConfigPath);
|
|
304
|
+
const cwd = node_path.default.dirname(resolvedConfigPath);
|
|
260
305
|
try {
|
|
261
|
-
userConfig = await loadModule(resolvedConfigPath);
|
|
306
|
+
const userConfig = await loadModule(resolvedConfigPath);
|
|
262
307
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
|
|
308
|
+
return {
|
|
309
|
+
userConfig,
|
|
310
|
+
cwd
|
|
311
|
+
};
|
|
263
312
|
} 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 {}
|
|
313
|
+
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
314
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
315
|
+
throw new Error(msg);
|
|
284
316
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
317
|
+
}
|
|
318
|
+
const cwd = process.cwd();
|
|
319
|
+
const configFileNames = [
|
|
320
|
+
"kubb.config.ts",
|
|
321
|
+
"kubb.config.mts",
|
|
322
|
+
"kubb.config.cts",
|
|
323
|
+
"kubb.config.js",
|
|
324
|
+
"kubb.config.cjs"
|
|
325
|
+
];
|
|
326
|
+
for (const configFileName of configFileNames) {
|
|
327
|
+
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
328
|
+
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
329
|
+
try {
|
|
330
|
+
const userConfig = await loadModule(configFilePath);
|
|
331
|
+
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
332
|
+
return {
|
|
333
|
+
userConfig,
|
|
334
|
+
cwd
|
|
335
|
+
};
|
|
336
|
+
} catch (err) {
|
|
337
|
+
await notify(NotifyTypes.CONFIG_ERROR, `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
288
338
|
}
|
|
289
339
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
cwd
|
|
293
|
-
};
|
|
340
|
+
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
341
|
+
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
294
342
|
}
|
|
295
343
|
//#endregion
|
|
296
344
|
//#region src/utils/resolveCwd.ts
|
|
@@ -309,40 +357,27 @@ function resolveCwd(userConfig, cwd) {
|
|
|
309
357
|
}
|
|
310
358
|
//#endregion
|
|
311
359
|
//#region src/utils/resolveUserConfig.ts
|
|
312
|
-
/**
|
|
313
|
-
* Resolve the config by handling function configs and returning the final configuration
|
|
314
|
-
*/
|
|
315
360
|
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;
|
|
361
|
+
const result = typeof config === "function" ? config({
|
|
362
|
+
logLevel: options.logLevel,
|
|
363
|
+
config: options.configPath
|
|
364
|
+
}) : config;
|
|
365
|
+
const resolved = isPromise(result) ? await result : result;
|
|
366
|
+
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
326
367
|
}
|
|
327
368
|
//#endregion
|
|
328
369
|
//#region src/tools/generate.ts
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
async function generate(schema
|
|
370
|
+
const generateTool = (0, tmcp_tool.defineTool)({
|
|
371
|
+
name: "generate",
|
|
372
|
+
description: "Generate OpenAPI spec helpers using Kubb configuration",
|
|
373
|
+
schema: generateSchema
|
|
374
|
+
}, async function generate(schema) {
|
|
334
375
|
const { config: configPath, input, output, logLevel } = schema;
|
|
335
376
|
try {
|
|
336
377
|
const hooks = new AsyncEventEmitter();
|
|
337
378
|
const messages = [];
|
|
338
379
|
const notify = async (type, message, data) => {
|
|
339
|
-
messages.push(`${type}: ${message}`);
|
|
340
|
-
await handler.sendNotification("kubb/progress", {
|
|
341
|
-
type,
|
|
342
|
-
message,
|
|
343
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
344
|
-
...data
|
|
345
|
-
});
|
|
380
|
+
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
346
381
|
};
|
|
347
382
|
hooks.on("kubb:info", async ({ message }) => {
|
|
348
383
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -351,11 +386,14 @@ async function generate(schema, handler) {
|
|
|
351
386
|
await notify(NotifyTypes.SUCCESS, message);
|
|
352
387
|
});
|
|
353
388
|
hooks.on("kubb:error", async ({ error }) => {
|
|
354
|
-
await notify(NotifyTypes.ERROR, error.message
|
|
389
|
+
await notify(NotifyTypes.ERROR, error.message);
|
|
355
390
|
});
|
|
356
391
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
357
392
|
await notify(NotifyTypes.WARN, message);
|
|
358
393
|
});
|
|
394
|
+
hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
|
|
395
|
+
await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic));
|
|
396
|
+
});
|
|
359
397
|
hooks.on("kubb:plugin:start", async ({ plugin }) => {
|
|
360
398
|
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
|
|
361
399
|
});
|
|
@@ -365,8 +403,8 @@ async function generate(schema, handler) {
|
|
|
365
403
|
hooks.on("kubb:files:processing:start", async () => {
|
|
366
404
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
367
405
|
});
|
|
368
|
-
hooks.on("kubb:
|
|
369
|
-
await notify(NotifyTypes.
|
|
406
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
407
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
370
408
|
});
|
|
371
409
|
hooks.on("kubb:files:processing:end", async () => {
|
|
372
410
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -383,7 +421,7 @@ async function generate(schema, handler) {
|
|
|
383
421
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
384
422
|
userConfig = configResult.userConfig;
|
|
385
423
|
cwd = configResult.cwd;
|
|
386
|
-
if (Array.isArray(userConfig)
|
|
424
|
+
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
425
|
userConfig = await resolveUserConfig(userConfig, {
|
|
388
426
|
configPath,
|
|
389
427
|
logLevel
|
|
@@ -391,13 +429,7 @@ async function generate(schema, handler) {
|
|
|
391
429
|
} catch (error) {
|
|
392
430
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
393
431
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
|
|
394
|
-
return
|
|
395
|
-
content: [{
|
|
396
|
-
type: "text",
|
|
397
|
-
text: errorMessage
|
|
398
|
-
}],
|
|
399
|
-
isError: true
|
|
400
|
-
};
|
|
432
|
+
return tmcp_utils.tool.error(errorMessage);
|
|
401
433
|
}
|
|
402
434
|
const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
|
|
403
435
|
const config = {
|
|
@@ -412,89 +444,259 @@ async function generate(schema, handler) {
|
|
|
412
444
|
path: output
|
|
413
445
|
} : userConfig.output
|
|
414
446
|
};
|
|
415
|
-
await notify(NotifyTypes.CONFIG_READY, "Configuration ready"
|
|
447
|
+
await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
|
|
416
448
|
await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
|
|
417
449
|
const kubb = (0, _kubb_core.createKubb)(config, { hooks });
|
|
418
450
|
await kubb.setup();
|
|
419
451
|
await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
|
|
420
452
|
await notify(NotifyTypes.BUILD_START, "Starting build");
|
|
421
|
-
const { files,
|
|
453
|
+
const { files, diagnostics } = await kubb.safeBuild();
|
|
422
454
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
};
|
|
455
|
+
const problems = diagnostics.filter(_kubb_core.Diagnostics.isProblem);
|
|
456
|
+
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
457
|
+
if (errors.length > 0) {
|
|
458
|
+
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${errors.length} diagnostic(s)`);
|
|
459
|
+
const serialized = problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic));
|
|
460
|
+
return tmcp_utils.tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
436
461
|
}
|
|
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
|
-
}] };
|
|
462
|
+
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
463
|
+
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
442
464
|
} catch (caughtError) {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
465
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(caughtError));
|
|
466
|
+
return tmcp_utils.tool.error(`Build error:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region ../../internals/shared/src/constants.ts
|
|
471
|
+
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
472
|
+
const availablePlugins = [
|
|
473
|
+
{
|
|
474
|
+
value: "plugin-ts",
|
|
475
|
+
label: "TypeScript",
|
|
476
|
+
hint: "Recommended",
|
|
477
|
+
packageName: "@kubb/plugin-ts",
|
|
478
|
+
importName: "pluginTs",
|
|
479
|
+
category: "types"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
value: "plugin-client",
|
|
483
|
+
label: "Client (Fetch/Axios)",
|
|
484
|
+
packageName: "@kubb/plugin-client",
|
|
485
|
+
importName: "pluginClient",
|
|
486
|
+
category: "client"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
value: "plugin-react-query",
|
|
490
|
+
label: "React Query / TanStack Query",
|
|
491
|
+
packageName: "@kubb/plugin-react-query",
|
|
492
|
+
importName: "pluginReactQuery",
|
|
493
|
+
category: "framework"
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
value: "plugin-vue-query",
|
|
497
|
+
label: "Vue Query",
|
|
498
|
+
packageName: "@kubb/plugin-vue-query",
|
|
499
|
+
importName: "pluginVueQuery",
|
|
500
|
+
category: "framework"
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
value: "plugin-zod",
|
|
504
|
+
label: "Zod Schemas",
|
|
505
|
+
packageName: "@kubb/plugin-zod",
|
|
506
|
+
importName: "pluginZod",
|
|
507
|
+
category: "validation"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
value: "plugin-faker",
|
|
511
|
+
label: "Faker.js Mocks",
|
|
512
|
+
packageName: "@kubb/plugin-faker",
|
|
513
|
+
importName: "pluginFaker",
|
|
514
|
+
category: "mocks"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
value: "plugin-msw",
|
|
518
|
+
label: "MSW Handlers",
|
|
519
|
+
packageName: "@kubb/plugin-msw",
|
|
520
|
+
importName: "pluginMsw",
|
|
521
|
+
category: "mocks"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
value: "plugin-cypress",
|
|
525
|
+
label: "Cypress Tests",
|
|
526
|
+
packageName: "@kubb/plugin-cypress",
|
|
527
|
+
importName: "pluginCypress",
|
|
528
|
+
category: "testing"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
value: "plugin-mcp",
|
|
532
|
+
label: "MCP Server (AI / Model Context Protocol)",
|
|
533
|
+
packageName: "@kubb/plugin-mcp",
|
|
534
|
+
importName: "pluginMcp",
|
|
535
|
+
category: "ai"
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
value: "plugin-redoc",
|
|
539
|
+
label: "ReDoc Documentation",
|
|
540
|
+
packageName: "@kubb/plugin-redoc",
|
|
541
|
+
importName: "pluginRedoc",
|
|
542
|
+
category: "documentation"
|
|
457
543
|
}
|
|
544
|
+
];
|
|
545
|
+
const pluginDefaultConfigs = {
|
|
546
|
+
"plugin-ts": `pluginTs({
|
|
547
|
+
output: { path: 'models' },
|
|
548
|
+
})`,
|
|
549
|
+
"plugin-client": `pluginClient({
|
|
550
|
+
output: { path: 'clients' },
|
|
551
|
+
})`,
|
|
552
|
+
"plugin-react-query": `pluginReactQuery({
|
|
553
|
+
output: { path: 'hooks' },
|
|
554
|
+
})`,
|
|
555
|
+
"plugin-vue-query": `pluginVueQuery({
|
|
556
|
+
output: { path: 'hooks' },
|
|
557
|
+
})`,
|
|
558
|
+
"plugin-zod": `pluginZod({
|
|
559
|
+
output: { path: 'zod' },
|
|
560
|
+
})`,
|
|
561
|
+
"plugin-faker": `pluginFaker({
|
|
562
|
+
output: { path: 'mocks' },
|
|
563
|
+
})`,
|
|
564
|
+
"plugin-msw": `pluginMsw({
|
|
565
|
+
output: { path: 'msw' },
|
|
566
|
+
})`,
|
|
567
|
+
"plugin-cypress": `pluginCypress({
|
|
568
|
+
output: { path: 'cypress' },
|
|
569
|
+
})`,
|
|
570
|
+
"plugin-mcp": `pluginMcp({
|
|
571
|
+
output: { path: 'mcp' },
|
|
572
|
+
})`,
|
|
573
|
+
"plugin-redoc": `pluginRedoc({
|
|
574
|
+
output: { path: 'redoc' },
|
|
575
|
+
})`
|
|
576
|
+
};
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region ../../internals/shared/src/init.ts
|
|
579
|
+
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
580
|
+
return `import { defineConfig } from 'kubb'
|
|
581
|
+
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
582
|
+
|
|
583
|
+
export default defineConfig({
|
|
584
|
+
root: '.',
|
|
585
|
+
input: {
|
|
586
|
+
path: '${inputPath}',
|
|
587
|
+
},
|
|
588
|
+
output: {
|
|
589
|
+
path: '${outputPath}',
|
|
590
|
+
clean: true,
|
|
591
|
+
},
|
|
592
|
+
plugins: [
|
|
593
|
+
${selectedPlugins.map((plugin) => {
|
|
594
|
+
return ` ${pluginDefaultConfigs[plugin.value] ?? `${plugin.importName}()`},`;
|
|
595
|
+
}).join("\n")}
|
|
596
|
+
],
|
|
597
|
+
})
|
|
598
|
+
`;
|
|
458
599
|
}
|
|
459
600
|
//#endregion
|
|
460
|
-
//#region src/
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
601
|
+
//#region src/schemas/initSchema.ts
|
|
602
|
+
const initSchema = valibot.object({
|
|
603
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
604
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory (default: ./src/gen)"))),
|
|
605
|
+
plugins: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Comma-separated list of plugins: plugin-ts,plugin-zod,...")))
|
|
606
|
+
});
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region src/tools/init.ts
|
|
609
|
+
function resolvePlugins(pluginsFlag) {
|
|
610
|
+
if (!pluginsFlag) return [];
|
|
611
|
+
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
612
|
+
return availablePlugins.filter((p) => requested.includes(p.value));
|
|
613
|
+
}
|
|
614
|
+
const initTool = (0, tmcp_tool.defineTool)({
|
|
615
|
+
name: "init",
|
|
616
|
+
description: "Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.",
|
|
617
|
+
schema: initSchema
|
|
618
|
+
}, async ({ input = "./openapi.yaml", output = "./src/gen", plugins }) => {
|
|
619
|
+
const selected = resolvePlugins(plugins);
|
|
620
|
+
const content = generateConfigFile({
|
|
621
|
+
selectedPlugins: selected,
|
|
622
|
+
inputPath: input,
|
|
623
|
+
outputPath: output
|
|
624
|
+
});
|
|
625
|
+
const dest = node_path.default.join(node_process.default.cwd(), KUBB_CONFIG_FILENAME);
|
|
626
|
+
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.`);
|
|
627
|
+
node_fs.default.writeFileSync(dest, content, "utf-8");
|
|
628
|
+
const packageList = ["kubb", ...selected.map((p) => p.packageName)].join(" ");
|
|
629
|
+
return tmcp_utils.tool.text(`Created kubb.config.ts\n\nInstall packages:\n npm install ${packageList}\n\nThen run:\n npx kubb generate`);
|
|
630
|
+
});
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region src/schemas/validateSchema.ts
|
|
633
|
+
const validateSchema = valibot.object({ input: valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path or URL to the OpenAPI/Swagger specification")) });
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/tools/validate.ts
|
|
636
|
+
const validateTool = (0, tmcp_tool.defineTool)({
|
|
637
|
+
name: "validate",
|
|
638
|
+
description: "Validate an OpenAPI/Swagger specification file or URL",
|
|
639
|
+
schema: validateSchema
|
|
640
|
+
}, async ({ input }) => {
|
|
641
|
+
let mod;
|
|
467
642
|
try {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
643
|
+
mod = await import("@kubb/adapter-oas");
|
|
644
|
+
} catch {
|
|
645
|
+
return tmcp_utils.tool.error("The validate tool requires @kubb/adapter-oas.\nInstall: npm install @kubb/adapter-oas");
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
649
|
+
return tmcp_utils.tool.text(`Validation successful: ${input}`);
|
|
650
|
+
} catch (err) {
|
|
651
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(err));
|
|
652
|
+
return tmcp_utils.tool.error(`Validation failed:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/server.ts
|
|
657
|
+
function createMcpServer() {
|
|
658
|
+
const server = new tmcp.McpServer({
|
|
659
|
+
name: "Kubb",
|
|
660
|
+
version
|
|
661
|
+
}, {
|
|
662
|
+
adapter: new _tmcp_adapter_valibot.ValibotJsonSchemaAdapter(),
|
|
663
|
+
capabilities: { tools: {} }
|
|
664
|
+
});
|
|
665
|
+
server.tools([
|
|
666
|
+
generateTool,
|
|
667
|
+
validateTool,
|
|
668
|
+
initTool
|
|
669
|
+
]);
|
|
670
|
+
return server;
|
|
671
|
+
}
|
|
672
|
+
async function startServer({ port, host = "localhost" } = {}) {
|
|
673
|
+
const server = createMcpServer();
|
|
674
|
+
if (port === void 0) {
|
|
675
|
+
new _tmcp_transport_stdio.StdioTransport(server).listen();
|
|
676
|
+
return;
|
|
490
677
|
}
|
|
678
|
+
const transport = new _tmcp_transport_http.HttpTransport(server, { path: "/mcp" });
|
|
679
|
+
const httpServer = node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
|
|
680
|
+
return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
|
|
681
|
+
}));
|
|
682
|
+
httpServer.listen(port, host, () => {
|
|
683
|
+
console.log(`Kubb MCP server on http://${host}:${port}`);
|
|
684
|
+
});
|
|
685
|
+
const closeServer = () => httpServer.close();
|
|
686
|
+
process.once("SIGINT", closeServer);
|
|
687
|
+
process.once("SIGTERM", closeServer);
|
|
688
|
+
httpServer.once("close", () => {
|
|
689
|
+
process.off("SIGINT", closeServer);
|
|
690
|
+
process.off("SIGTERM", closeServer);
|
|
691
|
+
});
|
|
491
692
|
}
|
|
492
693
|
//#endregion
|
|
493
694
|
//#region src/index.ts
|
|
494
|
-
async function run(_argv) {
|
|
495
|
-
await startServer();
|
|
695
|
+
async function run(_argv, options) {
|
|
696
|
+
await startServer(options);
|
|
496
697
|
}
|
|
497
698
|
//#endregion
|
|
699
|
+
exports.createMcpServer = createMcpServer;
|
|
498
700
|
exports.run = run;
|
|
499
701
|
|
|
500
702
|
//# sourceMappingURL=index.cjs.map
|