@kubb/mcp 5.0.0-beta.6 → 5.0.0-beta.60
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 +322 -187
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +322 -187
- package/dist/index.js.map +1 -1
- package/package.json +16 -24
- package/src/constants.ts +24 -0
- package/src/index.ts +1 -1
- package/src/schemas/generateSchema.ts +1 -1
- package/src/server.ts +9 -1
- package/src/tools/generate.ts +22 -24
- package/src/tools/init.ts +1 -1
- package/src/tools/validate.ts +4 -1
- package/src/{utils/loadUserConfig.ts → utils.ts} +63 -12
- package/src/types.ts +0 -23
- package/src/utils/resolveCwd.ts +0 -20
- package/src/utils/resolveUserConfig.ts +0 -13
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -29,20 +29,21 @@ let _tmcp_transport_http = require("@tmcp/transport-http");
|
|
|
29
29
|
let _tmcp_transport_stdio = require("@tmcp/transport-stdio");
|
|
30
30
|
let tmcp = require("tmcp");
|
|
31
31
|
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
32
|
let _kubb_core = require("@kubb/core");
|
|
37
33
|
let tmcp_tool = require("tmcp/tool");
|
|
38
34
|
let tmcp_utils = require("tmcp/utils");
|
|
39
35
|
let valibot = require("valibot");
|
|
40
36
|
valibot = __toESM(valibot, 1);
|
|
37
|
+
let node_fs = require("node:fs");
|
|
38
|
+
node_fs = __toESM(node_fs, 1);
|
|
39
|
+
let node_path = require("node:path");
|
|
40
|
+
node_path = __toESM(node_path, 1);
|
|
41
|
+
let node_url = require("node:url");
|
|
41
42
|
let jiti = require("jiti");
|
|
42
43
|
let node_process = require("node:process");
|
|
43
44
|
node_process = __toESM(node_process, 1);
|
|
44
45
|
//#region package.json
|
|
45
|
-
var version = "5.0.0-beta.
|
|
46
|
+
var version = "5.0.0-beta.60";
|
|
46
47
|
//#endregion
|
|
47
48
|
//#region ../../internals/utils/src/errors.ts
|
|
48
49
|
/**
|
|
@@ -90,9 +91,12 @@ var AsyncEventEmitter = class {
|
|
|
90
91
|
* await emitter.emit('build', 'petstore')
|
|
91
92
|
* ```
|
|
92
93
|
*/
|
|
93
|
-
|
|
94
|
+
emit(eventName, ...eventArgs) {
|
|
94
95
|
const listeners = this.#emitter.listeners(eventName);
|
|
95
96
|
if (listeners.length === 0) return;
|
|
97
|
+
return this.#emitAll(eventName, listeners, eventArgs);
|
|
98
|
+
}
|
|
99
|
+
async #emitAll(eventName, listeners, eventArgs) {
|
|
96
100
|
for (const listener of listeners) try {
|
|
97
101
|
await listener(...eventArgs);
|
|
98
102
|
} catch (err) {
|
|
@@ -155,6 +159,24 @@ var AsyncEventEmitter = class {
|
|
|
155
159
|
return this.#emitter.listenerCount(eventName);
|
|
156
160
|
}
|
|
157
161
|
/**
|
|
162
|
+
* Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
|
|
163
|
+
* Set this above the expected listener count when many listeners attach by design.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* emitter.setMaxListeners(40)
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
setMaxListeners(max) {
|
|
171
|
+
this.#emitter.setMaxListeners(max);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Returns the current per-event listener ceiling.
|
|
175
|
+
*/
|
|
176
|
+
getMaxListeners() {
|
|
177
|
+
return this.#emitter.getMaxListeners();
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
158
180
|
* Removes all listeners from every event channel.
|
|
159
181
|
*
|
|
160
182
|
* @example
|
|
@@ -180,31 +202,95 @@ function isPromise(result) {
|
|
|
180
202
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
181
203
|
}
|
|
182
204
|
//#endregion
|
|
183
|
-
//#region src/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
205
|
+
//#region ../../internals/utils/src/runtime.ts
|
|
206
|
+
/**
|
|
207
|
+
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
208
|
+
*
|
|
209
|
+
* Prefer the shared {@link runtime} instance over constructing your own.
|
|
210
|
+
*/
|
|
211
|
+
var Runtime = class {
|
|
212
|
+
/**
|
|
213
|
+
* `true` when the current process is running under Bun.
|
|
214
|
+
*
|
|
215
|
+
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
216
|
+
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
217
|
+
* otherwise look like Node.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* if (runtime.isBun) {
|
|
222
|
+
* await Bun.write(path, data)
|
|
223
|
+
* }
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
get isBun() {
|
|
227
|
+
return typeof Bun !== "undefined";
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* `true` when the current process is running under Deno.
|
|
231
|
+
*/
|
|
232
|
+
get isDeno() {
|
|
233
|
+
return typeof globalThis.Deno !== "undefined";
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* `true` when the current process is running under Node.
|
|
237
|
+
*
|
|
238
|
+
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
239
|
+
*/
|
|
240
|
+
get isNode() {
|
|
241
|
+
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Name of the runtime executing the current process.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
get name() {
|
|
252
|
+
if (this.isBun) return "bun";
|
|
253
|
+
if (this.isDeno) return "deno";
|
|
254
|
+
return "node";
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Version of the active runtime, or an empty string when it cannot be read.
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
get version() {
|
|
265
|
+
if (this.isBun) return process.versions.bun ?? "";
|
|
266
|
+
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
267
|
+
return process.versions?.node ?? "";
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
|
|
272
|
+
*/
|
|
273
|
+
const runtime = new Runtime();
|
|
197
274
|
//#endregion
|
|
198
|
-
//#region src/
|
|
275
|
+
//#region src/constants.ts
|
|
276
|
+
const ALLOWED_CONFIG_EXTENSIONS = new Set([
|
|
277
|
+
".ts",
|
|
278
|
+
".mts",
|
|
279
|
+
".cts",
|
|
280
|
+
".js",
|
|
281
|
+
".mjs",
|
|
282
|
+
".cjs"
|
|
283
|
+
]);
|
|
199
284
|
const NotifyTypes = {
|
|
200
285
|
INFO: "INFO",
|
|
201
286
|
SUCCESS: "SUCCESS",
|
|
202
287
|
ERROR: "ERROR",
|
|
203
288
|
WARN: "WARN",
|
|
289
|
+
DIAGNOSTIC: "DIAGNOSTIC",
|
|
204
290
|
PLUGIN_START: "PLUGIN_START",
|
|
205
291
|
PLUGIN_END: "PLUGIN_END",
|
|
206
292
|
FILES_START: "FILES_START",
|
|
207
|
-
|
|
293
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
208
294
|
FILES_END: "FILES_END",
|
|
209
295
|
GENERATION_START: "GENERATION_START",
|
|
210
296
|
GENERATION_END: "GENERATION_END",
|
|
@@ -216,34 +302,201 @@ const NotifyTypes = {
|
|
|
216
302
|
BUILD_START: "BUILD_START",
|
|
217
303
|
BUILD_END: "BUILD_END",
|
|
218
304
|
BUILD_FAILED: "BUILD_FAILED",
|
|
219
|
-
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
220
|
-
FATAL_ERROR: "FATAL_ERROR"
|
|
305
|
+
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
221
306
|
};
|
|
222
307
|
//#endregion
|
|
223
|
-
//#region src/
|
|
224
|
-
const
|
|
225
|
-
".ts",
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
308
|
+
//#region src/schemas/generateSchema.ts
|
|
309
|
+
const generateSchema = valibot.object({
|
|
310
|
+
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"))),
|
|
311
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
|
|
312
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
|
|
313
|
+
logLevel: valibot.optional(valibot.pipe(valibot.picklist([
|
|
314
|
+
"silent",
|
|
315
|
+
"info",
|
|
316
|
+
"verbose"
|
|
317
|
+
]), valibot.description("Log level for build output")), "info")
|
|
318
|
+
});
|
|
232
319
|
//#endregion
|
|
233
|
-
//#region src/
|
|
234
|
-
const
|
|
320
|
+
//#region ../../internals/shared/src/constants.ts
|
|
321
|
+
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
322
|
+
const availablePlugins = [
|
|
323
|
+
{
|
|
324
|
+
value: "plugin-ts",
|
|
325
|
+
label: "TypeScript",
|
|
326
|
+
hint: "Recommended",
|
|
327
|
+
packageName: "@kubb/plugin-ts",
|
|
328
|
+
importName: "pluginTs",
|
|
329
|
+
category: "types"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
value: "plugin-client",
|
|
333
|
+
label: "Client (Fetch/Axios)",
|
|
334
|
+
packageName: "@kubb/plugin-client",
|
|
335
|
+
importName: "pluginClient",
|
|
336
|
+
category: "client"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
value: "plugin-react-query",
|
|
340
|
+
label: "React Query / TanStack Query",
|
|
341
|
+
packageName: "@kubb/plugin-react-query",
|
|
342
|
+
importName: "pluginReactQuery",
|
|
343
|
+
category: "framework"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
value: "plugin-vue-query",
|
|
347
|
+
label: "Vue Query",
|
|
348
|
+
packageName: "@kubb/plugin-vue-query",
|
|
349
|
+
importName: "pluginVueQuery",
|
|
350
|
+
category: "framework"
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
value: "plugin-zod",
|
|
354
|
+
label: "Zod Schemas",
|
|
355
|
+
packageName: "@kubb/plugin-zod",
|
|
356
|
+
importName: "pluginZod",
|
|
357
|
+
category: "validation"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
value: "plugin-faker",
|
|
361
|
+
label: "Faker.js Mocks",
|
|
362
|
+
packageName: "@kubb/plugin-faker",
|
|
363
|
+
importName: "pluginFaker",
|
|
364
|
+
category: "mocks"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
value: "plugin-msw",
|
|
368
|
+
label: "MSW Handlers",
|
|
369
|
+
packageName: "@kubb/plugin-msw",
|
|
370
|
+
importName: "pluginMsw",
|
|
371
|
+
category: "mocks"
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
value: "plugin-cypress",
|
|
375
|
+
label: "Cypress Tests",
|
|
376
|
+
packageName: "@kubb/plugin-cypress",
|
|
377
|
+
importName: "pluginCypress",
|
|
378
|
+
category: "testing"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
value: "plugin-mcp",
|
|
382
|
+
label: "MCP Server (AI / Model Context Protocol)",
|
|
383
|
+
packageName: "@kubb/plugin-mcp",
|
|
384
|
+
importName: "pluginMcp",
|
|
385
|
+
category: "ai"
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
value: "plugin-redoc",
|
|
389
|
+
label: "ReDoc Documentation",
|
|
390
|
+
packageName: "@kubb/plugin-redoc",
|
|
391
|
+
importName: "pluginRedoc",
|
|
392
|
+
category: "documentation"
|
|
393
|
+
}
|
|
394
|
+
];
|
|
395
|
+
const pluginDefaultConfigs = {
|
|
396
|
+
"plugin-ts": `pluginTs()`,
|
|
397
|
+
"plugin-client": `pluginClient()`,
|
|
398
|
+
"plugin-react-query": `pluginReactQuery()`,
|
|
399
|
+
"plugin-vue-query": `pluginVueQuery()`,
|
|
400
|
+
"plugin-zod": `pluginZod()`,
|
|
401
|
+
"plugin-faker": `pluginFaker()`,
|
|
402
|
+
"plugin-msw": `pluginMsw()`,
|
|
403
|
+
"plugin-cypress": `pluginCypress()`,
|
|
404
|
+
"plugin-mcp": `pluginMcp()`,
|
|
405
|
+
"plugin-redoc": `pluginRedoc()`
|
|
406
|
+
};
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region ../../internals/shared/src/init.ts
|
|
409
|
+
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
410
|
+
return `import { defineConfig } from 'kubb'
|
|
411
|
+
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
412
|
+
|
|
413
|
+
export default defineConfig({
|
|
414
|
+
root: '.',
|
|
415
|
+
input: {
|
|
416
|
+
path: '${inputPath}',
|
|
417
|
+
},
|
|
418
|
+
output: {
|
|
419
|
+
path: '${outputPath}',
|
|
420
|
+
clean: true,
|
|
421
|
+
},
|
|
422
|
+
plugins: [
|
|
423
|
+
${selectedPlugins.map((plugin) => {
|
|
424
|
+
return ` ${pluginDefaultConfigs[plugin.value] ?? `${plugin.importName}()`},`;
|
|
425
|
+
}).join("\n")}
|
|
426
|
+
],
|
|
427
|
+
})
|
|
428
|
+
`;
|
|
429
|
+
}
|
|
430
|
+
//#endregion
|
|
431
|
+
//#region ../../internals/shared/src/loader.ts
|
|
432
|
+
/**
|
|
433
|
+
* jiti options for loading Kubb config modules: the automatic JSX runtime pointed at
|
|
434
|
+
* `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.
|
|
435
|
+
*/
|
|
436
|
+
const JITI_OPTIONS = {
|
|
235
437
|
jsx: {
|
|
236
438
|
runtime: "automatic",
|
|
237
439
|
importSource: "@kubb/renderer-jsx"
|
|
238
440
|
},
|
|
239
441
|
moduleCache: false
|
|
240
|
-
}
|
|
442
|
+
};
|
|
443
|
+
/**
|
|
444
|
+
* Creates a runtime-aware loader for Kubb's TypeScript and JavaScript config modules.
|
|
445
|
+
*
|
|
446
|
+
* On Bun and Deno it imports the file natively, skipping jiti's transform step, and falls back to
|
|
447
|
+
* jiti only when the native import throws. On Node it always uses jiti, which transpiles TypeScript
|
|
448
|
+
* and the `@kubb/renderer-jsx` JSX runtime on the fly. The jiti instance is created lazily, so the
|
|
449
|
+
* Bun/Deno happy path never pays for it.
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```ts
|
|
453
|
+
* const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
function createModuleLoader() {
|
|
457
|
+
let jiti$1;
|
|
458
|
+
const getJiti = () => jiti$1 ??= (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, JITI_OPTIONS);
|
|
459
|
+
const viaJiti = (filePath, options) => options?.default ? getJiti().import(filePath, { default: true }) : getJiti().import(filePath);
|
|
460
|
+
const viaNative = async (filePath, options) => {
|
|
461
|
+
const href = (0, node_url.pathToFileURL)(filePath).href;
|
|
462
|
+
const mod = await (options?.bust != null ? import(`${href}?t=${options.bust}`) : import(href));
|
|
463
|
+
return options?.default ? mod.default ?? mod : mod;
|
|
464
|
+
};
|
|
465
|
+
return { async load(filePath, options) {
|
|
466
|
+
if (runtime.isBun || runtime.isDeno) try {
|
|
467
|
+
return await viaNative(filePath, options);
|
|
468
|
+
} catch {
|
|
469
|
+
return viaJiti(filePath, options);
|
|
470
|
+
}
|
|
471
|
+
return viaJiti(filePath, options);
|
|
472
|
+
} };
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/utils.ts
|
|
476
|
+
/**
|
|
477
|
+
* Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry
|
|
478
|
+
* keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so
|
|
479
|
+
* the agent can act on the problem rather than parsing a bare message. No ANSI styling,
|
|
480
|
+
* unlike the CLI renderer.
|
|
481
|
+
*/
|
|
482
|
+
function formatDiagnostics(diagnostics) {
|
|
483
|
+
return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join("\n\n");
|
|
484
|
+
}
|
|
485
|
+
function formatDiagnostic(diagnostic) {
|
|
486
|
+
const { code, severity, message, location, help, plugin, docsUrl } = diagnostic;
|
|
487
|
+
const lines = [`${severity} ${plugin ? `${plugin}(${code})` : code}: ${message}`];
|
|
488
|
+
if (location && "pointer" in location) lines.push(` at ${location.pointer}`);
|
|
489
|
+
if (help) lines.push(` help: ${help}`);
|
|
490
|
+
if (docsUrl) lines.push(` docs: ${docsUrl}`);
|
|
491
|
+
return lines.join("\n");
|
|
492
|
+
}
|
|
493
|
+
const loader = createModuleLoader();
|
|
241
494
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
242
495
|
async function loadModule(filePath) {
|
|
243
496
|
const ext = node_path.default.extname(filePath);
|
|
244
497
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
|
|
245
498
|
if (loadedModules.has(filePath)) return loadedModules.get(filePath);
|
|
246
|
-
const mod = await
|
|
499
|
+
const mod = await loader.load(filePath, { default: true });
|
|
247
500
|
loadedModules.set(filePath, mod);
|
|
248
501
|
return mod;
|
|
249
502
|
}
|
|
@@ -302,8 +555,6 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
302
555
|
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
303
556
|
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
304
557
|
}
|
|
305
|
-
//#endregion
|
|
306
|
-
//#region src/utils/resolveCwd.ts
|
|
307
558
|
/**
|
|
308
559
|
* Determine the root directory based on userConfig.root and resolvedConfigDir
|
|
309
560
|
* 1. If userConfig.root exists and is absolute, use it as-is
|
|
@@ -317,8 +568,6 @@ function resolveCwd(userConfig, cwd) {
|
|
|
317
568
|
}
|
|
318
569
|
return cwd;
|
|
319
570
|
}
|
|
320
|
-
//#endregion
|
|
321
|
-
//#region src/utils/resolveUserConfig.ts
|
|
322
571
|
async function resolveUserConfig(config, options) {
|
|
323
572
|
const result = typeof config === "function" ? config({
|
|
324
573
|
logLevel: options.logLevel,
|
|
@@ -338,8 +587,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
338
587
|
try {
|
|
339
588
|
const hooks = new AsyncEventEmitter();
|
|
340
589
|
const messages = [];
|
|
341
|
-
const notify = async (type, message,
|
|
342
|
-
messages.push(`${type}: ${message}`);
|
|
590
|
+
const notify = async (type, message, data) => {
|
|
591
|
+
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
343
592
|
};
|
|
344
593
|
hooks.on("kubb:info", async ({ message }) => {
|
|
345
594
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -353,6 +602,9 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
353
602
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
354
603
|
await notify(NotifyTypes.WARN, message);
|
|
355
604
|
});
|
|
605
|
+
hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
|
|
606
|
+
await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic));
|
|
607
|
+
});
|
|
356
608
|
hooks.on("kubb:plugin:start", async ({ plugin }) => {
|
|
357
609
|
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
|
|
358
610
|
});
|
|
@@ -362,8 +614,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
362
614
|
hooks.on("kubb:files:processing:start", async () => {
|
|
363
615
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
364
616
|
});
|
|
365
|
-
hooks.on("kubb:
|
|
366
|
-
await notify(NotifyTypes.
|
|
617
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
618
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
367
619
|
});
|
|
368
620
|
hooks.on("kubb:files:processing:end", async () => {
|
|
369
621
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -409,152 +661,23 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
409
661
|
await kubb.setup();
|
|
410
662
|
await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
|
|
411
663
|
await notify(NotifyTypes.BUILD_START, "Starting build");
|
|
412
|
-
const { files,
|
|
664
|
+
const { files, diagnostics } = await kubb.safeBuild();
|
|
413
665
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
666
|
+
const problems = diagnostics.filter(_kubb_core.Diagnostics.isProblem);
|
|
667
|
+
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
668
|
+
if (errors.length > 0) {
|
|
669
|
+
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${errors.length} diagnostic(s)`);
|
|
670
|
+
const serialized = problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic));
|
|
671
|
+
return tmcp_utils.tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
418
672
|
}
|
|
419
673
|
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
420
674
|
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
421
675
|
} catch (caughtError) {
|
|
422
|
-
const
|
|
423
|
-
return tmcp_utils.tool.error(`Build error
|
|
676
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(caughtError));
|
|
677
|
+
return tmcp_utils.tool.error(`Build error:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
424
678
|
}
|
|
425
679
|
});
|
|
426
680
|
//#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
681
|
//#region src/schemas/initSchema.ts
|
|
559
682
|
const initSchema = valibot.object({
|
|
560
683
|
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
@@ -605,7 +728,8 @@ const validateTool = (0, tmcp_tool.defineTool)({
|
|
|
605
728
|
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
606
729
|
return tmcp_utils.tool.text(`Validation successful: ${input}`);
|
|
607
730
|
} catch (err) {
|
|
608
|
-
|
|
731
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(err));
|
|
732
|
+
return tmcp_utils.tool.error(`Validation failed:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
609
733
|
}
|
|
610
734
|
});
|
|
611
735
|
//#endregion
|
|
@@ -614,7 +738,10 @@ function createMcpServer() {
|
|
|
614
738
|
const server = new tmcp.McpServer({
|
|
615
739
|
name: "Kubb",
|
|
616
740
|
version
|
|
617
|
-
}, {
|
|
741
|
+
}, {
|
|
742
|
+
adapter: new _tmcp_adapter_valibot.ValibotJsonSchemaAdapter(),
|
|
743
|
+
capabilities: { tools: {} }
|
|
744
|
+
});
|
|
618
745
|
server.tools([
|
|
619
746
|
generateTool,
|
|
620
747
|
validateTool,
|
|
@@ -629,11 +756,19 @@ async function startServer({ port, host = "localhost" } = {}) {
|
|
|
629
756
|
return;
|
|
630
757
|
}
|
|
631
758
|
const transport = new _tmcp_transport_http.HttpTransport(server, { path: "/mcp" });
|
|
632
|
-
node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
|
|
759
|
+
const httpServer = node_http.default.createServer((0, _remix_run_node_fetch_server.createRequestListener)(async (request) => {
|
|
633
760
|
return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
|
|
634
|
-
}))
|
|
761
|
+
}));
|
|
762
|
+
httpServer.listen(port, host, () => {
|
|
635
763
|
console.log(`Kubb MCP server on http://${host}:${port}`);
|
|
636
764
|
});
|
|
765
|
+
const closeServer = () => httpServer.close();
|
|
766
|
+
process.once("SIGINT", closeServer);
|
|
767
|
+
process.once("SIGTERM", closeServer);
|
|
768
|
+
httpServer.once("close", () => {
|
|
769
|
+
process.off("SIGINT", closeServer);
|
|
770
|
+
process.off("SIGTERM", closeServer);
|
|
771
|
+
});
|
|
637
772
|
}
|
|
638
773
|
//#endregion
|
|
639
774
|
//#region src/index.ts
|