@kubb/mcp 5.0.0-beta.75 → 5.0.0-beta.76
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 +66 -19
- package/dist/index.cjs +379 -186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.js +375 -188
- package/dist/index.js.map +1 -1
- package/package.json +17 -22
- package/src/constants.ts +0 -1
- package/src/index.ts +0 -5
- package/src/schemas/generateSchema.ts +0 -12
- package/src/server.ts +0 -48
- package/src/tools/generate.ts +0 -210
- package/src/types.ts +0 -23
- package/src/utils/loadUserConfig.ts +0 -77
- package/src/utils/resolveCwd.ts +0 -20
- package/src/utils/resolveUserConfig.ts +0 -28
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -21,34 +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
|
|
25
|
-
|
|
26
|
-
let
|
|
27
|
-
let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
28
|
-
let zod = require("zod");
|
|
24
|
+
let _tmcp_adapter_valibot = require("@tmcp/adapter-valibot");
|
|
25
|
+
let _tmcp_transport_stdio = require("@tmcp/transport-stdio");
|
|
26
|
+
let tmcp = require("tmcp");
|
|
29
27
|
let node_events = require("node:events");
|
|
28
|
+
let _kubb_core = require("@kubb/core");
|
|
29
|
+
let tmcp_tool = require("tmcp/tool");
|
|
30
|
+
let tmcp_utils = require("tmcp/utils");
|
|
31
|
+
let valibot = require("valibot");
|
|
32
|
+
valibot = __toESM(valibot, 1);
|
|
30
33
|
let node_fs = require("node:fs");
|
|
34
|
+
node_fs = __toESM(node_fs, 1);
|
|
31
35
|
let node_path = require("node:path");
|
|
32
36
|
node_path = __toESM(node_path, 1);
|
|
33
|
-
let
|
|
34
|
-
let
|
|
37
|
+
let jiti = require("jiti");
|
|
38
|
+
let node_process = require("node:process");
|
|
39
|
+
node_process = __toESM(node_process, 1);
|
|
35
40
|
//#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
|
-
});
|
|
41
|
+
var version = "5.0.0-beta.76";
|
|
52
42
|
//#endregion
|
|
53
43
|
//#region ../../internals/utils/src/errors.ts
|
|
54
44
|
/**
|
|
@@ -96,9 +86,12 @@ var AsyncEventEmitter = class {
|
|
|
96
86
|
* await emitter.emit('build', 'petstore')
|
|
97
87
|
* ```
|
|
98
88
|
*/
|
|
99
|
-
|
|
89
|
+
emit(eventName, ...eventArgs) {
|
|
100
90
|
const listeners = this.#emitter.listeners(eventName);
|
|
101
91
|
if (listeners.length === 0) return;
|
|
92
|
+
return this.#emitAll(eventName, listeners, eventArgs);
|
|
93
|
+
}
|
|
94
|
+
async #emitAll(eventName, listeners, eventArgs) {
|
|
102
95
|
for (const listener of listeners) try {
|
|
103
96
|
await listener(...eventArgs);
|
|
104
97
|
} catch (err) {
|
|
@@ -123,21 +116,6 @@ var AsyncEventEmitter = class {
|
|
|
123
116
|
this.#emitter.on(eventName, handler);
|
|
124
117
|
}
|
|
125
118
|
/**
|
|
126
|
-
* Registers a one-shot listener that removes itself after the first invocation.
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```ts
|
|
130
|
-
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
|
-
onOnce(eventName, handler) {
|
|
134
|
-
const wrapper = (...args) => {
|
|
135
|
-
this.off(eventName, wrapper);
|
|
136
|
-
return handler(...args);
|
|
137
|
-
};
|
|
138
|
-
this.on(eventName, wrapper);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
119
|
* Removes a previously registered listener.
|
|
142
120
|
*
|
|
143
121
|
* @example
|
|
@@ -161,6 +139,18 @@ var AsyncEventEmitter = class {
|
|
|
161
139
|
return this.#emitter.listenerCount(eventName);
|
|
162
140
|
}
|
|
163
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
|
+
/**
|
|
164
154
|
* Removes all listeners from every event channel.
|
|
165
155
|
*
|
|
166
156
|
* @example
|
|
@@ -186,16 +176,33 @@ function isPromise(result) {
|
|
|
186
176
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
187
177
|
}
|
|
188
178
|
//#endregion
|
|
189
|
-
//#region src/
|
|
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 = 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
|
+
*/
|
|
190
196
|
const NotifyTypes = {
|
|
191
197
|
INFO: "INFO",
|
|
192
198
|
SUCCESS: "SUCCESS",
|
|
193
199
|
ERROR: "ERROR",
|
|
194
200
|
WARN: "WARN",
|
|
201
|
+
DIAGNOSTIC: "DIAGNOSTIC",
|
|
195
202
|
PLUGIN_START: "PLUGIN_START",
|
|
196
203
|
PLUGIN_END: "PLUGIN_END",
|
|
197
204
|
FILES_START: "FILES_START",
|
|
198
|
-
|
|
205
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
199
206
|
FILES_END: "FILES_END",
|
|
200
207
|
GENERATION_START: "GENERATION_START",
|
|
201
208
|
GENERATION_END: "GENERATION_END",
|
|
@@ -207,33 +214,191 @@ const NotifyTypes = {
|
|
|
207
214
|
BUILD_START: "BUILD_START",
|
|
208
215
|
BUILD_END: "BUILD_END",
|
|
209
216
|
BUILD_FAILED: "BUILD_FAILED",
|
|
210
|
-
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
211
|
-
FATAL_ERROR: "FATAL_ERROR"
|
|
217
|
+
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
212
218
|
};
|
|
213
219
|
//#endregion
|
|
214
|
-
//#region src/
|
|
215
|
-
const
|
|
216
|
-
".ts",
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
];
|
|
314
|
+
//#endregion
|
|
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 = {
|
|
342
|
+
jsx: {
|
|
343
|
+
runtime: "automatic",
|
|
344
|
+
importSource: "@kubb/renderer-jsx"
|
|
345
|
+
},
|
|
346
|
+
moduleCache: false
|
|
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
|
+
}
|
|
223
364
|
//#endregion
|
|
224
|
-
//#region src/utils
|
|
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();
|
|
225
384
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
226
385
|
async function loadModule(filePath) {
|
|
227
386
|
const ext = node_path.default.extname(filePath);
|
|
228
387
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
|
|
229
388
|
if (loadedModules.has(filePath)) return loadedModules.get(filePath);
|
|
230
|
-
const
|
|
231
|
-
loadedModules.set(filePath,
|
|
232
|
-
return
|
|
389
|
+
const mod = await loader.load(filePath, { default: true });
|
|
390
|
+
loadedModules.set(filePath, mod);
|
|
391
|
+
return mod;
|
|
233
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
|
+
*/
|
|
234
401
|
async function loadUserConfig(configPath, { notify }) {
|
|
235
|
-
let userConfig;
|
|
236
|
-
let cwd;
|
|
237
402
|
if (configPath) {
|
|
238
403
|
const ext = node_path.default.extname(configPath);
|
|
239
404
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
@@ -249,44 +414,45 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
249
414
|
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
250
415
|
throw new Error(msg);
|
|
251
416
|
}
|
|
252
|
-
cwd = node_path.default.dirname(resolvedConfigPath);
|
|
417
|
+
const cwd = node_path.default.dirname(resolvedConfigPath);
|
|
253
418
|
try {
|
|
254
|
-
userConfig = await loadModule(resolvedConfigPath);
|
|
419
|
+
const userConfig = await loadModule(resolvedConfigPath);
|
|
255
420
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
|
|
421
|
+
return {
|
|
422
|
+
userConfig,
|
|
423
|
+
cwd
|
|
424
|
+
};
|
|
256
425
|
} catch (error) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
} else {
|
|
261
|
-
cwd = process.cwd();
|
|
262
|
-
const configFileNames = [
|
|
263
|
-
"kubb.config.ts",
|
|
264
|
-
"kubb.config.mts",
|
|
265
|
-
"kubb.config.cts",
|
|
266
|
-
"kubb.config.js",
|
|
267
|
-
"kubb.config.cjs"
|
|
268
|
-
];
|
|
269
|
-
for (const configFileName of configFileNames) {
|
|
270
|
-
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
271
|
-
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
272
|
-
try {
|
|
273
|
-
userConfig = await loadModule(configFilePath);
|
|
274
|
-
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
275
|
-
break;
|
|
276
|
-
} catch {}
|
|
426
|
+
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
427
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
428
|
+
throw new Error(msg);
|
|
277
429
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
430
|
+
}
|
|
431
|
+
const cwd = process.cwd();
|
|
432
|
+
const configFileNames = [
|
|
433
|
+
"kubb.config.ts",
|
|
434
|
+
"kubb.config.mts",
|
|
435
|
+
"kubb.config.cts",
|
|
436
|
+
"kubb.config.js",
|
|
437
|
+
"kubb.config.cjs"
|
|
438
|
+
];
|
|
439
|
+
for (const configFileName of configFileNames) {
|
|
440
|
+
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
441
|
+
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
442
|
+
try {
|
|
443
|
+
const userConfig = await loadModule(configFilePath);
|
|
444
|
+
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
445
|
+
return {
|
|
446
|
+
userConfig,
|
|
447
|
+
cwd
|
|
448
|
+
};
|
|
449
|
+
} catch (err) {
|
|
450
|
+
await notify(NotifyTypes.CONFIG_ERROR, `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
281
451
|
}
|
|
282
452
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
cwd
|
|
286
|
-
};
|
|
453
|
+
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
454
|
+
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
287
455
|
}
|
|
288
|
-
//#endregion
|
|
289
|
-
//#region src/utils/resolveCwd.ts
|
|
290
456
|
/**
|
|
291
457
|
* Determine the root directory based on userConfig.root and resolvedConfigDir
|
|
292
458
|
* 1. If userConfig.root exists and is absolute, use it as-is
|
|
@@ -300,42 +466,33 @@ function resolveCwd(userConfig, cwd) {
|
|
|
300
466
|
}
|
|
301
467
|
return cwd;
|
|
302
468
|
}
|
|
303
|
-
//#endregion
|
|
304
|
-
//#region src/utils/resolveUserConfig.ts
|
|
305
469
|
/**
|
|
306
|
-
*
|
|
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.
|
|
307
474
|
*/
|
|
308
475
|
async function resolveUserConfig(config, options) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (isPromise(possiblePromise)) kubbUserConfig = possiblePromise;
|
|
316
|
-
else kubbUserConfig = Promise.resolve(possiblePromise);
|
|
317
|
-
}
|
|
318
|
-
return await kubbUserConfig;
|
|
476
|
+
const result = typeof config === "function" ? config({
|
|
477
|
+
logLevel: options.logLevel,
|
|
478
|
+
config: options.configPath
|
|
479
|
+
}) : config;
|
|
480
|
+
const resolved = isPromise(result) ? await result : result;
|
|
481
|
+
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
319
482
|
}
|
|
320
483
|
//#endregion
|
|
321
484
|
//#region src/tools/generate.ts
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
async function generate(schema
|
|
485
|
+
const generateTool = (0, tmcp_tool.defineTool)({
|
|
486
|
+
name: "generate",
|
|
487
|
+
description: "Generate OpenAPI spec helpers using Kubb configuration",
|
|
488
|
+
schema: generateSchema
|
|
489
|
+
}, async function generate(schema) {
|
|
327
490
|
const { config: configPath, input, output, logLevel } = schema;
|
|
328
491
|
try {
|
|
329
492
|
const hooks = new AsyncEventEmitter();
|
|
330
493
|
const messages = [];
|
|
331
494
|
const notify = async (type, message, data) => {
|
|
332
|
-
messages.push(`${type}: ${message}`);
|
|
333
|
-
await handler.sendNotification("kubb/progress", {
|
|
334
|
-
type,
|
|
335
|
-
message,
|
|
336
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
337
|
-
...data
|
|
338
|
-
});
|
|
495
|
+
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
339
496
|
};
|
|
340
497
|
hooks.on("kubb:info", async ({ message }) => {
|
|
341
498
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -344,11 +501,14 @@ async function generate(schema, handler) {
|
|
|
344
501
|
await notify(NotifyTypes.SUCCESS, message);
|
|
345
502
|
});
|
|
346
503
|
hooks.on("kubb:error", async ({ error }) => {
|
|
347
|
-
await notify(NotifyTypes.ERROR, error.message
|
|
504
|
+
await notify(NotifyTypes.ERROR, error.message);
|
|
348
505
|
});
|
|
349
506
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
350
507
|
await notify(NotifyTypes.WARN, message);
|
|
351
508
|
});
|
|
509
|
+
hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
|
|
510
|
+
await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic));
|
|
511
|
+
});
|
|
352
512
|
hooks.on("kubb:plugin:start", async ({ plugin }) => {
|
|
353
513
|
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
|
|
354
514
|
});
|
|
@@ -358,8 +518,8 @@ async function generate(schema, handler) {
|
|
|
358
518
|
hooks.on("kubb:files:processing:start", async () => {
|
|
359
519
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
360
520
|
});
|
|
361
|
-
hooks.on("kubb:
|
|
362
|
-
await notify(NotifyTypes.
|
|
521
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
522
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
363
523
|
});
|
|
364
524
|
hooks.on("kubb:files:processing:end", async () => {
|
|
365
525
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -376,7 +536,7 @@ async function generate(schema, handler) {
|
|
|
376
536
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
377
537
|
userConfig = configResult.userConfig;
|
|
378
538
|
cwd = configResult.cwd;
|
|
379
|
-
if (Array.isArray(userConfig)
|
|
539
|
+
if (Array.isArray(userConfig)) throw new Error("Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.");
|
|
380
540
|
userConfig = await resolveUserConfig(userConfig, {
|
|
381
541
|
configPath,
|
|
382
542
|
logLevel
|
|
@@ -384,15 +544,9 @@ async function generate(schema, handler) {
|
|
|
384
544
|
} catch (error) {
|
|
385
545
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
386
546
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
|
|
387
|
-
return
|
|
388
|
-
content: [{
|
|
389
|
-
type: "text",
|
|
390
|
-
text: errorMessage
|
|
391
|
-
}],
|
|
392
|
-
isError: true
|
|
393
|
-
};
|
|
547
|
+
return tmcp_utils.tool.error(errorMessage);
|
|
394
548
|
}
|
|
395
|
-
const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0);
|
|
549
|
+
const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
|
|
396
550
|
const config = {
|
|
397
551
|
...userConfig,
|
|
398
552
|
root: resolveCwd(userConfig, cwd),
|
|
@@ -405,89 +559,128 @@ async function generate(schema, handler) {
|
|
|
405
559
|
path: output
|
|
406
560
|
} : userConfig.output
|
|
407
561
|
};
|
|
408
|
-
await notify(NotifyTypes.CONFIG_READY, "Configuration ready"
|
|
562
|
+
await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
|
|
409
563
|
await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
|
|
410
564
|
const kubb = (0, _kubb_core.createKubb)(config, { hooks });
|
|
411
565
|
await kubb.setup();
|
|
412
566
|
await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
|
|
413
567
|
await notify(NotifyTypes.BUILD_START, "Starting build");
|
|
414
|
-
const { files,
|
|
568
|
+
const { files, diagnostics } = await kubb.safeBuild();
|
|
415
569
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
});
|
|
422
|
-
return {
|
|
423
|
-
content: [{
|
|
424
|
-
type: "text",
|
|
425
|
-
text: `Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`
|
|
426
|
-
}],
|
|
427
|
-
isError: true
|
|
428
|
-
};
|
|
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\`\`\``);
|
|
429
576
|
}
|
|
430
|
-
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files
|
|
431
|
-
return
|
|
432
|
-
type: "text",
|
|
433
|
-
text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`
|
|
434
|
-
}] };
|
|
577
|
+
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
578
|
+
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
435
579
|
} catch (caughtError) {
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
type: NotifyTypes.FATAL_ERROR,
|
|
439
|
-
message: error.message,
|
|
440
|
-
stack: error.stack,
|
|
441
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
442
|
-
});
|
|
443
|
-
return {
|
|
444
|
-
content: [{
|
|
445
|
-
type: "text",
|
|
446
|
-
text: `Build error: ${error.message}\n${error.stack || ""}`
|
|
447
|
-
}],
|
|
448
|
-
isError: true
|
|
449
|
-
};
|
|
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\`\`\``);
|
|
450
582
|
}
|
|
583
|
+
});
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region src/schemas/initSchema.ts
|
|
586
|
+
const initSchema = valibot.object({
|
|
587
|
+
input: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
588
|
+
output: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Output directory (default: ./src/gen)"))),
|
|
589
|
+
plugins: valibot.optional(valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Comma-separated list of plugins: plugin-ts,plugin-zod,...")))
|
|
590
|
+
});
|
|
591
|
+
//#endregion
|
|
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
|
+
*/
|
|
597
|
+
function resolvePlugins(pluginsFlag) {
|
|
598
|
+
if (!pluginsFlag) return [];
|
|
599
|
+
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
600
|
+
return availablePlugins.filter((p) => requested.includes(p.value));
|
|
451
601
|
}
|
|
602
|
+
const initTool = (0, tmcp_tool.defineTool)({
|
|
603
|
+
name: "init",
|
|
604
|
+
description: "Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.",
|
|
605
|
+
schema: initSchema
|
|
606
|
+
}, async ({ input = "./openapi.yaml", output = "./src/gen", plugins }) => {
|
|
607
|
+
const selected = resolvePlugins(plugins);
|
|
608
|
+
const content = generateConfigFile({
|
|
609
|
+
selectedPlugins: selected,
|
|
610
|
+
inputPath: input,
|
|
611
|
+
outputPath: output
|
|
612
|
+
});
|
|
613
|
+
const dest = node_path.default.join(node_process.default.cwd(), KUBB_CONFIG_FILENAME);
|
|
614
|
+
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.`);
|
|
615
|
+
node_fs.default.writeFileSync(dest, content, "utf-8");
|
|
616
|
+
const packageList = ["kubb", ...selected.map((p) => p.packageName)].join(" ");
|
|
617
|
+
return tmcp_utils.tool.text(`Created kubb.config.ts\n\nInstall packages:\n npm install ${packageList}\n\nThen run:\n npx kubb generate`);
|
|
618
|
+
});
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/schemas/validateSchema.ts
|
|
621
|
+
const validateSchema = valibot.object({ input: valibot.pipe(valibot.string(), valibot.minLength(1), valibot.description("Path or URL to the OpenAPI/Swagger specification")) });
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/tools/validate.ts
|
|
624
|
+
const validateTool = (0, tmcp_tool.defineTool)({
|
|
625
|
+
name: "validate",
|
|
626
|
+
description: "Validate an OpenAPI/Swagger specification file or URL",
|
|
627
|
+
schema: validateSchema
|
|
628
|
+
}, async ({ input }) => {
|
|
629
|
+
let mod;
|
|
630
|
+
try {
|
|
631
|
+
mod = await import("@kubb/adapter-oas");
|
|
632
|
+
} catch {
|
|
633
|
+
return tmcp_utils.tool.error("The validate tool requires @kubb/adapter-oas.\nInstall: npm install @kubb/adapter-oas");
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
637
|
+
return tmcp_utils.tool.text(`Validation successful: ${input}`);
|
|
638
|
+
} catch (err) {
|
|
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\`\`\``);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
452
643
|
//#endregion
|
|
453
644
|
//#region src/server.ts
|
|
454
645
|
/**
|
|
455
|
-
* Kubb MCP
|
|
646
|
+
* Builds the Kubb MCP server with the generate, validate, and init tools registered.
|
|
456
647
|
*
|
|
457
|
-
*
|
|
648
|
+
* @example
|
|
649
|
+
* `const server = createMcpServer()`
|
|
650
|
+
*/
|
|
651
|
+
function createMcpServer() {
|
|
652
|
+
const server = new tmcp.McpServer({
|
|
653
|
+
name: "Kubb",
|
|
654
|
+
version
|
|
655
|
+
}, {
|
|
656
|
+
adapter: new _tmcp_adapter_valibot.ValibotJsonSchemaAdapter(),
|
|
657
|
+
capabilities: { tools: {} }
|
|
658
|
+
});
|
|
659
|
+
server.tools([
|
|
660
|
+
generateTool,
|
|
661
|
+
validateTool,
|
|
662
|
+
initTool
|
|
663
|
+
]);
|
|
664
|
+
return server;
|
|
665
|
+
}
|
|
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.
|
|
458
669
|
*/
|
|
459
670
|
async function startServer() {
|
|
460
|
-
|
|
461
|
-
const transport = new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
|
|
462
|
-
const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
|
|
463
|
-
name: "Kubb",
|
|
464
|
-
version
|
|
465
|
-
});
|
|
466
|
-
server.tool("generate", "Generate OpenAPI spec helpers using Kubb configuration", generateSchema.shape, async (args) => {
|
|
467
|
-
return generate(args, { sendNotification: async (method, params) => {
|
|
468
|
-
try {
|
|
469
|
-
await transport.send({
|
|
470
|
-
jsonrpc: "2.0",
|
|
471
|
-
method,
|
|
472
|
-
params
|
|
473
|
-
});
|
|
474
|
-
} catch (error) {
|
|
475
|
-
console.error("Failed to send notification:", error);
|
|
476
|
-
}
|
|
477
|
-
} });
|
|
478
|
-
});
|
|
479
|
-
await server.connect(transport);
|
|
480
|
-
} catch (error) {
|
|
481
|
-
console.error("Failed to start MCP server:", error);
|
|
482
|
-
node_process.default.exit(1);
|
|
483
|
-
}
|
|
671
|
+
new _tmcp_transport_stdio.StdioTransport(createMcpServer()).listen();
|
|
484
672
|
}
|
|
485
673
|
//#endregion
|
|
486
674
|
//#region src/index.ts
|
|
675
|
+
/**
|
|
676
|
+
* Entry point that starts the MCP server over stdio. The argument is accepted
|
|
677
|
+
* for CLI parity but ignored.
|
|
678
|
+
*/
|
|
487
679
|
async function run(_argv) {
|
|
488
680
|
await startServer();
|
|
489
681
|
}
|
|
490
682
|
//#endregion
|
|
683
|
+
exports.createMcpServer = createMcpServer;
|
|
491
684
|
exports.run = run;
|
|
492
685
|
|
|
493
686
|
//# sourceMappingURL=index.cjs.map
|