@kubb/mcp 5.0.0-beta.8 → 5.0.0-beta.80
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 +29 -19
- package/dist/index.cjs +254 -218
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +254 -217
- 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,24 @@ 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);
|
|
41
37
|
let jiti = require("jiti");
|
|
42
38
|
let node_process = require("node:process");
|
|
43
39
|
node_process = __toESM(node_process, 1);
|
|
44
40
|
//#region package.json
|
|
45
|
-
var version = "5.0.0-beta.
|
|
41
|
+
var version = "5.0.0-beta.80";
|
|
46
42
|
//#endregion
|
|
47
43
|
//#region ../../internals/utils/src/errors.ts
|
|
48
44
|
/**
|
|
@@ -90,9 +86,12 @@ var AsyncEventEmitter = class {
|
|
|
90
86
|
* await emitter.emit('build', 'petstore')
|
|
91
87
|
* ```
|
|
92
88
|
*/
|
|
93
|
-
|
|
89
|
+
emit(eventName, ...eventArgs) {
|
|
94
90
|
const listeners = this.#emitter.listeners(eventName);
|
|
95
91
|
if (listeners.length === 0) return;
|
|
92
|
+
return this.#emitAll(eventName, listeners, eventArgs);
|
|
93
|
+
}
|
|
94
|
+
async #emitAll(eventName, listeners, eventArgs) {
|
|
96
95
|
for (const listener of listeners) try {
|
|
97
96
|
await listener(...eventArgs);
|
|
98
97
|
} catch (err) {
|
|
@@ -117,21 +116,6 @@ var AsyncEventEmitter = class {
|
|
|
117
116
|
this.#emitter.on(eventName, handler);
|
|
118
117
|
}
|
|
119
118
|
/**
|
|
120
|
-
* Registers a one-shot listener that removes itself after the first invocation.
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```ts
|
|
124
|
-
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
onOnce(eventName, handler) {
|
|
128
|
-
const wrapper = (...args) => {
|
|
129
|
-
this.off(eventName, wrapper);
|
|
130
|
-
return handler(...args);
|
|
131
|
-
};
|
|
132
|
-
this.on(eventName, wrapper);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
119
|
* Removes a previously registered listener.
|
|
136
120
|
*
|
|
137
121
|
* @example
|
|
@@ -155,6 +139,18 @@ var AsyncEventEmitter = class {
|
|
|
155
139
|
return this.#emitter.listenerCount(eventName);
|
|
156
140
|
}
|
|
157
141
|
/**
|
|
142
|
+
* Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
|
|
143
|
+
* Set this above the expected listener count when many listeners attach by design.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* emitter.setMaxListeners(40)
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
setMaxListeners(max) {
|
|
151
|
+
this.#emitter.setMaxListeners(max);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
158
154
|
* Removes all listeners from every event channel.
|
|
159
155
|
*
|
|
160
156
|
* @example
|
|
@@ -180,31 +176,33 @@ function isPromise(result) {
|
|
|
180
176
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
181
177
|
}
|
|
182
178
|
//#endregion
|
|
183
|
-
//#region src/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
179
|
+
//#region src/constants.ts
|
|
180
|
+
/**
|
|
181
|
+
* File extensions a Kubb config is allowed to use. A config path with any other
|
|
182
|
+
* extension is rejected before it is loaded.
|
|
183
|
+
*/
|
|
184
|
+
const ALLOWED_CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
185
|
+
".ts",
|
|
186
|
+
".mts",
|
|
187
|
+
".cts",
|
|
188
|
+
".js",
|
|
189
|
+
".mjs",
|
|
190
|
+
".cjs"
|
|
191
|
+
]);
|
|
192
|
+
/**
|
|
193
|
+
* Notification kinds reported back to the MCP client while loading config and
|
|
194
|
+
* running generation, covering progress, results, and errors.
|
|
195
|
+
*/
|
|
199
196
|
const NotifyTypes = {
|
|
200
197
|
INFO: "INFO",
|
|
201
198
|
SUCCESS: "SUCCESS",
|
|
202
199
|
ERROR: "ERROR",
|
|
203
200
|
WARN: "WARN",
|
|
201
|
+
DIAGNOSTIC: "DIAGNOSTIC",
|
|
204
202
|
PLUGIN_START: "PLUGIN_START",
|
|
205
203
|
PLUGIN_END: "PLUGIN_END",
|
|
206
204
|
FILES_START: "FILES_START",
|
|
207
|
-
|
|
205
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
208
206
|
FILES_END: "FILES_END",
|
|
209
207
|
GENERATION_START: "GENERATION_START",
|
|
210
208
|
GENERATION_END: "GENERATION_END",
|
|
@@ -216,37 +214,190 @@ const NotifyTypes = {
|
|
|
216
214
|
BUILD_START: "BUILD_START",
|
|
217
215
|
BUILD_END: "BUILD_END",
|
|
218
216
|
BUILD_FAILED: "BUILD_FAILED",
|
|
219
|
-
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
220
|
-
FATAL_ERROR: "FATAL_ERROR"
|
|
217
|
+
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
221
218
|
};
|
|
222
219
|
//#endregion
|
|
223
|
-
//#region src/
|
|
224
|
-
const
|
|
225
|
-
".ts",
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
//#region src/schemas/generateSchema.ts
|
|
221
|
+
const generateSchema = valibot.object({
|
|
222
|
+
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"))),
|
|
223
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
|
|
224
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory path (overrides config)"))),
|
|
225
|
+
logLevel: valibot.optional(valibot.pipe(valibot.picklist([
|
|
226
|
+
"silent",
|
|
227
|
+
"info",
|
|
228
|
+
"verbose"
|
|
229
|
+
]), valibot.description("Log level for build output")), "info")
|
|
230
|
+
});
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region ../../internals/shared/src/constants.ts
|
|
233
|
+
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
234
|
+
const availablePlugins = [
|
|
235
|
+
{
|
|
236
|
+
value: "plugin-ts",
|
|
237
|
+
label: "TypeScript",
|
|
238
|
+
hint: "Recommended",
|
|
239
|
+
packageName: "@kubb/plugin-ts",
|
|
240
|
+
importName: "pluginTs",
|
|
241
|
+
category: "types"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
value: "plugin-axios",
|
|
245
|
+
label: "Axios Client",
|
|
246
|
+
packageName: "@kubb/plugin-axios",
|
|
247
|
+
importName: "pluginAxios",
|
|
248
|
+
category: "client"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
value: "plugin-fetch",
|
|
252
|
+
label: "Fetch Client",
|
|
253
|
+
packageName: "@kubb/plugin-fetch",
|
|
254
|
+
importName: "pluginFetch",
|
|
255
|
+
category: "client"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
value: "plugin-react-query",
|
|
259
|
+
label: "React Query / TanStack Query",
|
|
260
|
+
packageName: "@kubb/plugin-react-query",
|
|
261
|
+
importName: "pluginReactQuery",
|
|
262
|
+
category: "framework"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
value: "plugin-vue-query",
|
|
266
|
+
label: "Vue Query",
|
|
267
|
+
packageName: "@kubb/plugin-vue-query",
|
|
268
|
+
importName: "pluginVueQuery",
|
|
269
|
+
category: "framework"
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
value: "plugin-zod",
|
|
273
|
+
label: "Zod Schemas",
|
|
274
|
+
packageName: "@kubb/plugin-zod",
|
|
275
|
+
importName: "pluginZod",
|
|
276
|
+
category: "validation"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
value: "plugin-faker",
|
|
280
|
+
label: "Faker.js Mocks",
|
|
281
|
+
packageName: "@kubb/plugin-faker",
|
|
282
|
+
importName: "pluginFaker",
|
|
283
|
+
category: "mocks"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
value: "plugin-msw",
|
|
287
|
+
label: "MSW Handlers",
|
|
288
|
+
packageName: "@kubb/plugin-msw",
|
|
289
|
+
importName: "pluginMsw",
|
|
290
|
+
category: "mocks"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
value: "plugin-cypress",
|
|
294
|
+
label: "Cypress Tests",
|
|
295
|
+
packageName: "@kubb/plugin-cypress",
|
|
296
|
+
importName: "pluginCypress",
|
|
297
|
+
category: "testing"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
value: "plugin-mcp",
|
|
301
|
+
label: "MCP Server (AI / Model Context Protocol)",
|
|
302
|
+
packageName: "@kubb/plugin-mcp",
|
|
303
|
+
importName: "pluginMcp",
|
|
304
|
+
category: "ai"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
value: "plugin-redoc",
|
|
308
|
+
label: "ReDoc Documentation",
|
|
309
|
+
packageName: "@kubb/plugin-redoc",
|
|
310
|
+
importName: "pluginRedoc",
|
|
311
|
+
category: "documentation"
|
|
312
|
+
}
|
|
313
|
+
];
|
|
232
314
|
//#endregion
|
|
233
|
-
//#region src/
|
|
234
|
-
|
|
315
|
+
//#region ../../internals/shared/src/init.ts
|
|
316
|
+
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
317
|
+
return `import { defineConfig } from 'kubb'
|
|
318
|
+
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
319
|
+
|
|
320
|
+
export default defineConfig({
|
|
321
|
+
root: '.',
|
|
322
|
+
input: {
|
|
323
|
+
path: '${inputPath}',
|
|
324
|
+
},
|
|
325
|
+
output: {
|
|
326
|
+
path: '${outputPath}',
|
|
327
|
+
clean: true,
|
|
328
|
+
},
|
|
329
|
+
plugins: [
|
|
330
|
+
${selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join("\n")}
|
|
331
|
+
],
|
|
332
|
+
})
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region ../../internals/shared/src/loader.ts
|
|
337
|
+
/**
|
|
338
|
+
* jiti options for loading Kubb config modules: the automatic JSX runtime pointed at
|
|
339
|
+
* `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.
|
|
340
|
+
*/
|
|
341
|
+
const JITI_OPTIONS = {
|
|
235
342
|
jsx: {
|
|
236
343
|
runtime: "automatic",
|
|
237
344
|
importSource: "@kubb/renderer-jsx"
|
|
238
345
|
},
|
|
239
346
|
moduleCache: false
|
|
240
|
-
}
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.
|
|
350
|
+
*
|
|
351
|
+
* jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly.
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```ts
|
|
355
|
+
* const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
function createModuleLoader() {
|
|
359
|
+
const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, JITI_OPTIONS);
|
|
360
|
+
return { load(filePath, options) {
|
|
361
|
+
return options?.default ? jiti$1.import(filePath, { default: true }) : jiti$1.import(filePath);
|
|
362
|
+
} };
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/utils.ts
|
|
366
|
+
/**
|
|
367
|
+
* Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry
|
|
368
|
+
* keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so
|
|
369
|
+
* the agent can act on the problem rather than parsing a bare message. No ANSI styling,
|
|
370
|
+
* unlike the CLI renderer.
|
|
371
|
+
*/
|
|
372
|
+
function formatDiagnostics(diagnostics) {
|
|
373
|
+
return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join("\n\n");
|
|
374
|
+
}
|
|
375
|
+
function formatDiagnostic(diagnostic) {
|
|
376
|
+
const { code, message, location, help, plugin, docsUrl } = diagnostic;
|
|
377
|
+
const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`];
|
|
378
|
+
if (location && "pointer" in location) lines.push(` at: ${location.pointer}`);
|
|
379
|
+
if (help) lines.push(` fix: ${help}`);
|
|
380
|
+
if (docsUrl) lines.push(` see: ${docsUrl}`);
|
|
381
|
+
return lines.join("\n");
|
|
382
|
+
}
|
|
383
|
+
const loader = createModuleLoader();
|
|
241
384
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
242
385
|
async function loadModule(filePath) {
|
|
243
386
|
const ext = node_path.default.extname(filePath);
|
|
244
387
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
|
|
245
388
|
if (loadedModules.has(filePath)) return loadedModules.get(filePath);
|
|
246
|
-
const mod = await
|
|
389
|
+
const mod = await loader.load(filePath, { default: true });
|
|
247
390
|
loadedModules.set(filePath, mod);
|
|
248
391
|
return mod;
|
|
249
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Loads the user's Kubb config and returns it with the directory it was found in.
|
|
395
|
+
*
|
|
396
|
+
* When `configPath` is given it must use an allowed extension and resolve inside
|
|
397
|
+
* the current working directory, otherwise loading throws. When omitted, the
|
|
398
|
+
* known `kubb.config.*` file names are tried in the current directory. Every
|
|
399
|
+
* outcome is reported through `notify` before the function returns or throws.
|
|
400
|
+
*/
|
|
250
401
|
async function loadUserConfig(configPath, { notify }) {
|
|
251
402
|
if (configPath) {
|
|
252
403
|
const ext = node_path.default.extname(configPath);
|
|
@@ -302,8 +453,6 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
302
453
|
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
303
454
|
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
304
455
|
}
|
|
305
|
-
//#endregion
|
|
306
|
-
//#region src/utils/resolveCwd.ts
|
|
307
456
|
/**
|
|
308
457
|
* Determine the root directory based on userConfig.root and resolvedConfigDir
|
|
309
458
|
* 1. If userConfig.root exists and is absolute, use it as-is
|
|
@@ -317,8 +466,12 @@ function resolveCwd(userConfig, cwd) {
|
|
|
317
466
|
}
|
|
318
467
|
return cwd;
|
|
319
468
|
}
|
|
320
|
-
|
|
321
|
-
|
|
469
|
+
/**
|
|
470
|
+
* Normalizes a possible config into a single resolved `Config`.
|
|
471
|
+
*
|
|
472
|
+
* Calls the config when it is a function, awaits it when it is a promise, and
|
|
473
|
+
* picks the first entry when it resolves to an array.
|
|
474
|
+
*/
|
|
322
475
|
async function resolveUserConfig(config, options) {
|
|
323
476
|
const result = typeof config === "function" ? config({
|
|
324
477
|
logLevel: options.logLevel,
|
|
@@ -338,8 +491,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
338
491
|
try {
|
|
339
492
|
const hooks = new AsyncEventEmitter();
|
|
340
493
|
const messages = [];
|
|
341
|
-
const notify = async (type, message,
|
|
342
|
-
messages.push(`${type}: ${message}`);
|
|
494
|
+
const notify = async (type, message, data) => {
|
|
495
|
+
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
343
496
|
};
|
|
344
497
|
hooks.on("kubb:info", async ({ message }) => {
|
|
345
498
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -353,6 +506,9 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
353
506
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
354
507
|
await notify(NotifyTypes.WARN, message);
|
|
355
508
|
});
|
|
509
|
+
hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
|
|
510
|
+
await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic));
|
|
511
|
+
});
|
|
356
512
|
hooks.on("kubb:plugin:start", async ({ plugin }) => {
|
|
357
513
|
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
|
|
358
514
|
});
|
|
@@ -362,8 +518,8 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
362
518
|
hooks.on("kubb:files:processing:start", async () => {
|
|
363
519
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
364
520
|
});
|
|
365
|
-
hooks.on("kubb:
|
|
366
|
-
await notify(NotifyTypes.
|
|
521
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
522
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
367
523
|
});
|
|
368
524
|
hooks.on("kubb:files:processing:end", async () => {
|
|
369
525
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -409,152 +565,23 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
409
565
|
await kubb.setup();
|
|
410
566
|
await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
|
|
411
567
|
await notify(NotifyTypes.BUILD_START, "Starting build");
|
|
412
|
-
const { files,
|
|
568
|
+
const { files, diagnostics } = await kubb.safeBuild();
|
|
413
569
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
570
|
+
const problems = diagnostics.filter(_kubb_core.Diagnostics.isProblem);
|
|
571
|
+
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
572
|
+
if (errors.length > 0) {
|
|
573
|
+
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${errors.length} diagnostic(s)`);
|
|
574
|
+
const serialized = problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic));
|
|
575
|
+
return tmcp_utils.tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
418
576
|
}
|
|
419
577
|
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
420
578
|
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
421
579
|
} catch (caughtError) {
|
|
422
|
-
const
|
|
423
|
-
return tmcp_utils.tool.error(`Build error
|
|
580
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(caughtError));
|
|
581
|
+
return tmcp_utils.tool.error(`Build error:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
424
582
|
}
|
|
425
583
|
});
|
|
426
584
|
//#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
585
|
//#region src/schemas/initSchema.ts
|
|
559
586
|
const initSchema = valibot.object({
|
|
560
587
|
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
@@ -563,6 +590,10 @@ const initSchema = valibot.object({
|
|
|
563
590
|
});
|
|
564
591
|
//#endregion
|
|
565
592
|
//#region src/tools/init.ts
|
|
593
|
+
/**
|
|
594
|
+
* Resolves a comma-separated plugin flag into the matching known plugin options.
|
|
595
|
+
* Unrecognized names are dropped, and a missing flag yields an empty list.
|
|
596
|
+
*/
|
|
566
597
|
function resolvePlugins(pluginsFlag) {
|
|
567
598
|
if (!pluginsFlag) return [];
|
|
568
599
|
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
@@ -605,11 +636,18 @@ const validateTool = (0, tmcp_tool.defineTool)({
|
|
|
605
636
|
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
606
637
|
return tmcp_utils.tool.text(`Validation successful: ${input}`);
|
|
607
638
|
} catch (err) {
|
|
608
|
-
|
|
639
|
+
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(err));
|
|
640
|
+
return tmcp_utils.tool.error(`Validation failed:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
609
641
|
}
|
|
610
642
|
});
|
|
611
643
|
//#endregion
|
|
612
644
|
//#region src/server.ts
|
|
645
|
+
/**
|
|
646
|
+
* Builds the Kubb MCP server with the generate, validate, and init tools registered.
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* `const server = createMcpServer()`
|
|
650
|
+
*/
|
|
613
651
|
function createMcpServer() {
|
|
614
652
|
const server = new tmcp.McpServer({
|
|
615
653
|
name: "Kubb",
|
|
@@ -625,23 +663,21 @@ function createMcpServer() {
|
|
|
625
663
|
]);
|
|
626
664
|
return server;
|
|
627
665
|
}
|
|
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
|
-
});
|
|
666
|
+
/**
|
|
667
|
+
* Starts the Kubb MCP server over stdio, the transport every local MCP client
|
|
668
|
+
* (Claude, Copilot, editors) uses when it launches the server as a subprocess.
|
|
669
|
+
*/
|
|
670
|
+
async function startServer() {
|
|
671
|
+
new _tmcp_transport_stdio.StdioTransport(createMcpServer()).listen();
|
|
640
672
|
}
|
|
641
673
|
//#endregion
|
|
642
674
|
//#region src/index.ts
|
|
643
|
-
|
|
644
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Entry point that starts the MCP server over stdio. The argument is accepted
|
|
677
|
+
* for CLI parity but ignored.
|
|
678
|
+
*/
|
|
679
|
+
async function run(_argv) {
|
|
680
|
+
await startServer();
|
|
645
681
|
}
|
|
646
682
|
//#endregion
|
|
647
683
|
exports.createMcpServer = createMcpServer;
|