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