@kubb/mcp 5.0.0-beta.2 → 5.0.0-beta.21
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/README.md +57 -20
- package/dist/index.cjs +325 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +319 -158
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- package/src/index.ts +5 -2
- package/src/schemas/generateSchema.ts +11 -10
- package/src/schemas/initSchema.ts +7 -0
- package/src/schemas/validateSchema.ts +5 -0
- package/src/server.ts +42 -41
- package/src/tools/generate.ts +113 -177
- package/src/tools/init.ts +37 -0
- package/src/tools/validate.ts +25 -0
- package/src/types.ts +1 -1
- package/src/utils/loadUserConfig.ts +32 -31
- package/src/utils/resolveUserConfig.ts +5 -20
package/dist/index.js
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
import "./chunk--u3MIqq1.js";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { createRequestListener } from "@remix-run/node-fetch-server";
|
|
4
|
+
import { ValibotJsonSchemaAdapter } from "@tmcp/adapter-valibot";
|
|
5
|
+
import { HttpTransport } from "@tmcp/transport-http";
|
|
6
|
+
import { StdioTransport } from "@tmcp/transport-stdio";
|
|
7
|
+
import { McpServer } from "tmcp";
|
|
6
8
|
import { EventEmitter } from "node:events";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
9
|
+
import fs, { existsSync } from "node:fs";
|
|
8
10
|
import path from "node:path";
|
|
9
11
|
import { createKubb } from "@kubb/core";
|
|
10
|
-
import {
|
|
12
|
+
import { defineTool } from "tmcp/tool";
|
|
13
|
+
import { tool } from "tmcp/utils";
|
|
14
|
+
import * as v from "valibot";
|
|
15
|
+
import { createJiti } from "jiti";
|
|
16
|
+
import process$1 from "node:process";
|
|
11
17
|
//#region package.json
|
|
12
|
-
var version = "5.0.0-beta.
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/schemas/generateSchema.ts
|
|
15
|
-
const generateSchema = z.object({
|
|
16
|
-
config: 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"),
|
|
17
|
-
input: z.string().optional().describe("Path to OpenAPI/Swagger spec file (overrides config)"),
|
|
18
|
-
output: z.string().optional().describe("Output directory path (overrides config)"),
|
|
19
|
-
logLevel: z.enum([
|
|
20
|
-
"silent",
|
|
21
|
-
"error",
|
|
22
|
-
"warn",
|
|
23
|
-
"info",
|
|
24
|
-
"verbose",
|
|
25
|
-
"debug"
|
|
26
|
-
]).optional().default("info").describe("Log level for build output")
|
|
27
|
-
});
|
|
18
|
+
var version = "5.0.0-beta.21";
|
|
28
19
|
//#endregion
|
|
29
20
|
//#region ../../internals/utils/src/errors.ts
|
|
30
21
|
/**
|
|
@@ -72,9 +63,12 @@ var AsyncEventEmitter = class {
|
|
|
72
63
|
* await emitter.emit('build', 'petstore')
|
|
73
64
|
* ```
|
|
74
65
|
*/
|
|
75
|
-
|
|
66
|
+
emit(eventName, ...eventArgs) {
|
|
76
67
|
const listeners = this.#emitter.listeners(eventName);
|
|
77
68
|
if (listeners.length === 0) return;
|
|
69
|
+
return this.#emitAll(eventName, listeners, eventArgs);
|
|
70
|
+
}
|
|
71
|
+
async #emitAll(eventName, listeners, eventArgs) {
|
|
78
72
|
for (const listener of listeners) try {
|
|
79
73
|
await listener(...eventArgs);
|
|
80
74
|
} catch (err) {
|
|
@@ -162,6 +156,21 @@ function isPromise(result) {
|
|
|
162
156
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
163
157
|
}
|
|
164
158
|
//#endregion
|
|
159
|
+
//#region src/schemas/generateSchema.ts
|
|
160
|
+
const generateSchema = v.object({
|
|
161
|
+
config: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Path to kubb.config file (supports .ts, .js, .cjs). If not provided, will look for kubb.config.{ts,js,cjs} in current directory"))),
|
|
162
|
+
input: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
|
|
163
|
+
output: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Output directory path (overrides config)"))),
|
|
164
|
+
logLevel: v.optional(v.pipe(v.picklist([
|
|
165
|
+
"silent",
|
|
166
|
+
"error",
|
|
167
|
+
"warn",
|
|
168
|
+
"info",
|
|
169
|
+
"verbose",
|
|
170
|
+
"debug"
|
|
171
|
+
]), v.description("Log level for build output")), "info")
|
|
172
|
+
});
|
|
173
|
+
//#endregion
|
|
165
174
|
//#region src/types.ts
|
|
166
175
|
const NotifyTypes = {
|
|
167
176
|
INFO: "INFO",
|
|
@@ -171,7 +180,7 @@ const NotifyTypes = {
|
|
|
171
180
|
PLUGIN_START: "PLUGIN_START",
|
|
172
181
|
PLUGIN_END: "PLUGIN_END",
|
|
173
182
|
FILES_START: "FILES_START",
|
|
174
|
-
|
|
183
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
175
184
|
FILES_END: "FILES_END",
|
|
176
185
|
GENERATION_START: "GENERATION_START",
|
|
177
186
|
GENERATION_END: "GENERATION_END",
|
|
@@ -198,18 +207,23 @@ const ALLOWED_CONFIG_EXTENSIONS = new Set([
|
|
|
198
207
|
]);
|
|
199
208
|
//#endregion
|
|
200
209
|
//#region src/utils/loadUserConfig.ts
|
|
210
|
+
const jiti = createJiti(import.meta.url, {
|
|
211
|
+
jsx: {
|
|
212
|
+
runtime: "automatic",
|
|
213
|
+
importSource: "@kubb/renderer-jsx"
|
|
214
|
+
},
|
|
215
|
+
moduleCache: false
|
|
216
|
+
});
|
|
201
217
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
202
218
|
async function loadModule(filePath) {
|
|
203
219
|
const ext = path.extname(filePath);
|
|
204
220
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
|
|
205
221
|
if (loadedModules.has(filePath)) return loadedModules.get(filePath);
|
|
206
|
-
const
|
|
207
|
-
loadedModules.set(filePath,
|
|
208
|
-
return
|
|
222
|
+
const mod = await jiti.import(filePath, { default: true });
|
|
223
|
+
loadedModules.set(filePath, mod);
|
|
224
|
+
return mod;
|
|
209
225
|
}
|
|
210
226
|
async function loadUserConfig(configPath, { notify }) {
|
|
211
|
-
let userConfig;
|
|
212
|
-
let cwd;
|
|
213
227
|
if (configPath) {
|
|
214
228
|
const ext = path.extname(configPath);
|
|
215
229
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
@@ -225,41 +239,44 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
225
239
|
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
226
240
|
throw new Error(msg);
|
|
227
241
|
}
|
|
228
|
-
cwd = path.dirname(resolvedConfigPath);
|
|
242
|
+
const cwd = path.dirname(resolvedConfigPath);
|
|
229
243
|
try {
|
|
230
|
-
userConfig = await loadModule(resolvedConfigPath);
|
|
244
|
+
const userConfig = await loadModule(resolvedConfigPath);
|
|
231
245
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
|
|
246
|
+
return {
|
|
247
|
+
userConfig,
|
|
248
|
+
cwd
|
|
249
|
+
};
|
|
232
250
|
} catch (error) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} else {
|
|
237
|
-
cwd = process.cwd();
|
|
238
|
-
const configFileNames = [
|
|
239
|
-
"kubb.config.ts",
|
|
240
|
-
"kubb.config.mts",
|
|
241
|
-
"kubb.config.cts",
|
|
242
|
-
"kubb.config.js",
|
|
243
|
-
"kubb.config.cjs"
|
|
244
|
-
];
|
|
245
|
-
for (const configFileName of configFileNames) {
|
|
246
|
-
const configFilePath = path.resolve(process.cwd(), configFileName);
|
|
247
|
-
if (!existsSync(configFilePath)) continue;
|
|
248
|
-
try {
|
|
249
|
-
userConfig = await loadModule(configFilePath);
|
|
250
|
-
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
251
|
-
break;
|
|
252
|
-
} catch {}
|
|
251
|
+
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
252
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
253
|
+
throw new Error(msg);
|
|
253
254
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
}
|
|
256
|
+
const cwd = process.cwd();
|
|
257
|
+
const configFileNames = [
|
|
258
|
+
"kubb.config.ts",
|
|
259
|
+
"kubb.config.mts",
|
|
260
|
+
"kubb.config.cts",
|
|
261
|
+
"kubb.config.js",
|
|
262
|
+
"kubb.config.cjs"
|
|
263
|
+
];
|
|
264
|
+
for (const configFileName of configFileNames) {
|
|
265
|
+
const configFilePath = path.resolve(process.cwd(), configFileName);
|
|
266
|
+
if (!existsSync(configFilePath)) continue;
|
|
267
|
+
try {
|
|
268
|
+
const userConfig = await loadModule(configFilePath);
|
|
269
|
+
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
270
|
+
return {
|
|
271
|
+
userConfig,
|
|
272
|
+
cwd
|
|
273
|
+
};
|
|
274
|
+
} catch (err) {
|
|
275
|
+
await notify(NotifyTypes.CONFIG_ERROR, `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
257
276
|
}
|
|
258
277
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
cwd
|
|
262
|
-
};
|
|
278
|
+
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
279
|
+
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
263
280
|
}
|
|
264
281
|
//#endregion
|
|
265
282
|
//#region src/utils/resolveCwd.ts
|
|
@@ -278,40 +295,27 @@ function resolveCwd(userConfig, cwd) {
|
|
|
278
295
|
}
|
|
279
296
|
//#endregion
|
|
280
297
|
//#region src/utils/resolveUserConfig.ts
|
|
281
|
-
/**
|
|
282
|
-
* Resolve the config by handling function configs and returning the final configuration
|
|
283
|
-
*/
|
|
284
298
|
async function resolveUserConfig(config, options) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (isPromise(possiblePromise)) kubbUserConfig = possiblePromise;
|
|
292
|
-
else kubbUserConfig = Promise.resolve(possiblePromise);
|
|
293
|
-
}
|
|
294
|
-
return await kubbUserConfig;
|
|
299
|
+
const result = typeof config === "function" ? config({
|
|
300
|
+
logLevel: options.logLevel,
|
|
301
|
+
config: options.configPath
|
|
302
|
+
}) : config;
|
|
303
|
+
const resolved = isPromise(result) ? await result : result;
|
|
304
|
+
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
295
305
|
}
|
|
296
306
|
//#endregion
|
|
297
307
|
//#region src/tools/generate.ts
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
async function generate(schema
|
|
308
|
+
const generateTool = defineTool({
|
|
309
|
+
name: "generate",
|
|
310
|
+
description: "Generate OpenAPI spec helpers using Kubb configuration",
|
|
311
|
+
schema: generateSchema
|
|
312
|
+
}, async function generate(schema) {
|
|
303
313
|
const { config: configPath, input, output, logLevel } = schema;
|
|
304
314
|
try {
|
|
305
315
|
const hooks = new AsyncEventEmitter();
|
|
306
316
|
const messages = [];
|
|
307
|
-
const notify = async (type, message,
|
|
317
|
+
const notify = async (type, message, _data) => {
|
|
308
318
|
messages.push(`${type}: ${message}`);
|
|
309
|
-
await handler.sendNotification("kubb/progress", {
|
|
310
|
-
type,
|
|
311
|
-
message,
|
|
312
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
313
|
-
...data
|
|
314
|
-
});
|
|
315
319
|
};
|
|
316
320
|
hooks.on("kubb:info", async ({ message }) => {
|
|
317
321
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -320,7 +324,7 @@ async function generate(schema, handler) {
|
|
|
320
324
|
await notify(NotifyTypes.SUCCESS, message);
|
|
321
325
|
});
|
|
322
326
|
hooks.on("kubb:error", async ({ error }) => {
|
|
323
|
-
await notify(NotifyTypes.ERROR, error.message
|
|
327
|
+
await notify(NotifyTypes.ERROR, error.message);
|
|
324
328
|
});
|
|
325
329
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
326
330
|
await notify(NotifyTypes.WARN, message);
|
|
@@ -334,8 +338,8 @@ async function generate(schema, handler) {
|
|
|
334
338
|
hooks.on("kubb:files:processing:start", async () => {
|
|
335
339
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
336
340
|
});
|
|
337
|
-
hooks.on("kubb:
|
|
338
|
-
await notify(NotifyTypes.
|
|
341
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
342
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
339
343
|
});
|
|
340
344
|
hooks.on("kubb:files:processing:end", async () => {
|
|
341
345
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -352,7 +356,7 @@ async function generate(schema, handler) {
|
|
|
352
356
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
353
357
|
userConfig = configResult.userConfig;
|
|
354
358
|
cwd = configResult.cwd;
|
|
355
|
-
if (Array.isArray(userConfig)
|
|
359
|
+
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.");
|
|
356
360
|
userConfig = await resolveUserConfig(userConfig, {
|
|
357
361
|
configPath,
|
|
358
362
|
logLevel
|
|
@@ -360,15 +364,9 @@ async function generate(schema, handler) {
|
|
|
360
364
|
} catch (error) {
|
|
361
365
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
362
366
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
|
|
363
|
-
return
|
|
364
|
-
content: [{
|
|
365
|
-
type: "text",
|
|
366
|
-
text: errorMessage
|
|
367
|
-
}],
|
|
368
|
-
isError: true
|
|
369
|
-
};
|
|
367
|
+
return tool.error(errorMessage);
|
|
370
368
|
}
|
|
371
|
-
const inputPath = input ?? ("path" in userConfig.input ? userConfig.input.path : void 0);
|
|
369
|
+
const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
|
|
372
370
|
const config = {
|
|
373
371
|
...userConfig,
|
|
374
372
|
root: resolveCwd(userConfig, cwd),
|
|
@@ -381,7 +379,7 @@ async function generate(schema, handler) {
|
|
|
381
379
|
path: output
|
|
382
380
|
} : userConfig.output
|
|
383
381
|
};
|
|
384
|
-
await notify(NotifyTypes.CONFIG_READY, "Configuration ready"
|
|
382
|
+
await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
|
|
385
383
|
await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
|
|
386
384
|
const kubb = createKubb(config, { hooks });
|
|
387
385
|
await kubb.setup();
|
|
@@ -391,79 +389,242 @@ async function generate(schema, handler) {
|
|
|
391
389
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
392
390
|
if (error || failedPlugins.size > 0) {
|
|
393
391
|
const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
|
|
394
|
-
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)
|
|
395
|
-
|
|
396
|
-
errors: allErrors.map((err) => err.message)
|
|
397
|
-
});
|
|
398
|
-
return {
|
|
399
|
-
content: [{
|
|
400
|
-
type: "text",
|
|
401
|
-
text: `Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`
|
|
402
|
-
}],
|
|
403
|
-
isError: true
|
|
404
|
-
};
|
|
392
|
+
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)`);
|
|
393
|
+
return tool.error(`Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`);
|
|
405
394
|
}
|
|
406
|
-
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files
|
|
407
|
-
return
|
|
408
|
-
type: "text",
|
|
409
|
-
text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`
|
|
410
|
-
}] };
|
|
395
|
+
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
396
|
+
return tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
411
397
|
} catch (caughtError) {
|
|
412
|
-
const error = caughtError;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
398
|
+
const error = toError(caughtError);
|
|
399
|
+
return tool.error(`Build error: ${error.message}\n${error.stack ?? ""}`);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region ../../internals/shared/src/constants.ts
|
|
404
|
+
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
405
|
+
const availablePlugins = [
|
|
406
|
+
{
|
|
407
|
+
value: "plugin-ts",
|
|
408
|
+
label: "TypeScript",
|
|
409
|
+
hint: "Recommended",
|
|
410
|
+
packageName: "@kubb/plugin-ts",
|
|
411
|
+
importName: "pluginTs",
|
|
412
|
+
category: "types"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
value: "plugin-client",
|
|
416
|
+
label: "Client (Fetch/Axios)",
|
|
417
|
+
packageName: "@kubb/plugin-client",
|
|
418
|
+
importName: "pluginClient",
|
|
419
|
+
category: "client"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
value: "plugin-react-query",
|
|
423
|
+
label: "React Query / TanStack Query",
|
|
424
|
+
packageName: "@kubb/plugin-react-query",
|
|
425
|
+
importName: "pluginReactQuery",
|
|
426
|
+
category: "framework"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
value: "plugin-vue-query",
|
|
430
|
+
label: "Vue Query",
|
|
431
|
+
packageName: "@kubb/plugin-vue-query",
|
|
432
|
+
importName: "pluginVueQuery",
|
|
433
|
+
category: "framework"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
value: "plugin-zod",
|
|
437
|
+
label: "Zod Schemas",
|
|
438
|
+
packageName: "@kubb/plugin-zod",
|
|
439
|
+
importName: "pluginZod",
|
|
440
|
+
category: "validation"
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
value: "plugin-faker",
|
|
444
|
+
label: "Faker.js Mocks",
|
|
445
|
+
packageName: "@kubb/plugin-faker",
|
|
446
|
+
importName: "pluginFaker",
|
|
447
|
+
category: "mocks"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
value: "plugin-msw",
|
|
451
|
+
label: "MSW Handlers",
|
|
452
|
+
packageName: "@kubb/plugin-msw",
|
|
453
|
+
importName: "pluginMsw",
|
|
454
|
+
category: "mocks"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
value: "plugin-cypress",
|
|
458
|
+
label: "Cypress Tests",
|
|
459
|
+
packageName: "@kubb/plugin-cypress",
|
|
460
|
+
importName: "pluginCypress",
|
|
461
|
+
category: "testing"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
value: "plugin-mcp",
|
|
465
|
+
label: "MCP Server (AI / Model Context Protocol)",
|
|
466
|
+
packageName: "@kubb/plugin-mcp",
|
|
467
|
+
importName: "pluginMcp",
|
|
468
|
+
category: "ai"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
value: "plugin-redoc",
|
|
472
|
+
label: "ReDoc Documentation",
|
|
473
|
+
packageName: "@kubb/plugin-redoc",
|
|
474
|
+
importName: "pluginRedoc",
|
|
475
|
+
category: "documentation"
|
|
426
476
|
}
|
|
477
|
+
];
|
|
478
|
+
const pluginDefaultConfigs = {
|
|
479
|
+
"plugin-ts": `pluginTs({
|
|
480
|
+
output: { path: 'models' },
|
|
481
|
+
})`,
|
|
482
|
+
"plugin-client": `pluginClient({
|
|
483
|
+
output: { path: 'clients' },
|
|
484
|
+
})`,
|
|
485
|
+
"plugin-react-query": `pluginReactQuery({
|
|
486
|
+
output: { path: 'hooks' },
|
|
487
|
+
})`,
|
|
488
|
+
"plugin-vue-query": `pluginVueQuery({
|
|
489
|
+
output: { path: 'hooks' },
|
|
490
|
+
})`,
|
|
491
|
+
"plugin-zod": `pluginZod({
|
|
492
|
+
output: { path: 'zod' },
|
|
493
|
+
})`,
|
|
494
|
+
"plugin-faker": `pluginFaker({
|
|
495
|
+
output: { path: 'mocks' },
|
|
496
|
+
})`,
|
|
497
|
+
"plugin-msw": `pluginMsw({
|
|
498
|
+
output: { path: 'msw' },
|
|
499
|
+
})`,
|
|
500
|
+
"plugin-cypress": `pluginCypress({
|
|
501
|
+
output: { path: 'cypress' },
|
|
502
|
+
})`,
|
|
503
|
+
"plugin-mcp": `pluginMcp({
|
|
504
|
+
output: { path: 'mcp' },
|
|
505
|
+
})`,
|
|
506
|
+
"plugin-redoc": `pluginRedoc({
|
|
507
|
+
output: { path: 'redoc' },
|
|
508
|
+
})`
|
|
509
|
+
};
|
|
510
|
+
//#endregion
|
|
511
|
+
//#region ../../internals/shared/src/init.ts
|
|
512
|
+
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
513
|
+
return `import { defineConfig } from 'kubb'
|
|
514
|
+
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
515
|
+
|
|
516
|
+
export default defineConfig({
|
|
517
|
+
root: '.',
|
|
518
|
+
input: {
|
|
519
|
+
path: '${inputPath}',
|
|
520
|
+
},
|
|
521
|
+
output: {
|
|
522
|
+
path: '${outputPath}',
|
|
523
|
+
clean: true,
|
|
524
|
+
},
|
|
525
|
+
plugins: [
|
|
526
|
+
${selectedPlugins.map((plugin) => {
|
|
527
|
+
return ` ${pluginDefaultConfigs[plugin.value] ?? `${plugin.importName}()`},`;
|
|
528
|
+
}).join("\n")}
|
|
529
|
+
],
|
|
530
|
+
})
|
|
531
|
+
`;
|
|
427
532
|
}
|
|
428
533
|
//#endregion
|
|
429
|
-
//#region src/
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
534
|
+
//#region src/schemas/initSchema.ts
|
|
535
|
+
const initSchema = v.object({
|
|
536
|
+
input: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
537
|
+
output: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Output directory (default: ./src/gen)"))),
|
|
538
|
+
plugins: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Comma-separated list of plugins: plugin-ts,plugin-zod,...")))
|
|
539
|
+
});
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/tools/init.ts
|
|
542
|
+
function resolvePlugins(pluginsFlag) {
|
|
543
|
+
if (!pluginsFlag) return [];
|
|
544
|
+
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
545
|
+
return availablePlugins.filter((p) => requested.includes(p.value));
|
|
546
|
+
}
|
|
547
|
+
const initTool = defineTool({
|
|
548
|
+
name: "init",
|
|
549
|
+
description: "Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.",
|
|
550
|
+
schema: initSchema
|
|
551
|
+
}, async ({ input = "./openapi.yaml", output = "./src/gen", plugins }) => {
|
|
552
|
+
const selected = resolvePlugins(plugins);
|
|
553
|
+
const content = generateConfigFile({
|
|
554
|
+
selectedPlugins: selected,
|
|
555
|
+
inputPath: input,
|
|
556
|
+
outputPath: output
|
|
557
|
+
});
|
|
558
|
+
const dest = path.join(process$1.cwd(), KUBB_CONFIG_FILENAME);
|
|
559
|
+
if (fs.existsSync(dest)) return tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`);
|
|
560
|
+
fs.writeFileSync(dest, content, "utf-8");
|
|
561
|
+
const packageList = ["kubb", ...selected.map((p) => p.packageName)].join(" ");
|
|
562
|
+
return tool.text(`Created kubb.config.ts\n\nInstall packages:\n npm install ${packageList}\n\nThen run:\n npx kubb generate`);
|
|
563
|
+
});
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/tools/validate.ts
|
|
566
|
+
const validateTool = defineTool({
|
|
567
|
+
name: "validate",
|
|
568
|
+
description: "Validate an OpenAPI/Swagger specification file or URL",
|
|
569
|
+
schema: v.object({ input: v.pipe(v.string(), v.minLength(1), v.description("Path or URL to the OpenAPI/Swagger specification")) })
|
|
570
|
+
}, async ({ input }) => {
|
|
571
|
+
let mod;
|
|
436
572
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
573
|
+
mod = await import("@kubb/adapter-oas");
|
|
574
|
+
} catch {
|
|
575
|
+
return tool.error("The validate tool requires @kubb/adapter-oas.\nInstall: npm install @kubb/adapter-oas");
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
579
|
+
return tool.text(`Validation successful: ${input}`);
|
|
580
|
+
} catch (err) {
|
|
581
|
+
return tool.error(`Validation failed:\n${err instanceof Error ? err.message : String(err)}`);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region src/server.ts
|
|
586
|
+
function createMcpServer() {
|
|
587
|
+
const server = new McpServer({
|
|
588
|
+
name: "Kubb",
|
|
589
|
+
version
|
|
590
|
+
}, {
|
|
591
|
+
adapter: new ValibotJsonSchemaAdapter(),
|
|
592
|
+
capabilities: { tools: {} }
|
|
593
|
+
});
|
|
594
|
+
server.tools([
|
|
595
|
+
generateTool,
|
|
596
|
+
validateTool,
|
|
597
|
+
initTool
|
|
598
|
+
]);
|
|
599
|
+
return server;
|
|
600
|
+
}
|
|
601
|
+
async function startServer({ port, host = "localhost" } = {}) {
|
|
602
|
+
const server = createMcpServer();
|
|
603
|
+
if (port === void 0) {
|
|
604
|
+
new StdioTransport(server).listen();
|
|
605
|
+
return;
|
|
459
606
|
}
|
|
607
|
+
const transport = new HttpTransport(server, { path: "/mcp" });
|
|
608
|
+
const httpServer = http.createServer(createRequestListener(async (request) => {
|
|
609
|
+
return await transport.respond(request) ?? new Response("Not Found", { status: 404 });
|
|
610
|
+
}));
|
|
611
|
+
httpServer.listen(port, host, () => {
|
|
612
|
+
console.log(`Kubb MCP server on http://${host}:${port}`);
|
|
613
|
+
});
|
|
614
|
+
const closeServer = () => httpServer.close();
|
|
615
|
+
process.once("SIGINT", closeServer);
|
|
616
|
+
process.once("SIGTERM", closeServer);
|
|
617
|
+
httpServer.once("close", () => {
|
|
618
|
+
process.off("SIGINT", closeServer);
|
|
619
|
+
process.off("SIGTERM", closeServer);
|
|
620
|
+
});
|
|
460
621
|
}
|
|
461
622
|
//#endregion
|
|
462
623
|
//#region src/index.ts
|
|
463
|
-
async function run(_argv) {
|
|
464
|
-
await startServer();
|
|
624
|
+
async function run(_argv, options) {
|
|
625
|
+
await startServer(options);
|
|
465
626
|
}
|
|
466
627
|
//#endregion
|
|
467
|
-
export { run };
|
|
628
|
+
export { createMcpServer, run };
|
|
468
629
|
|
|
469
630
|
//# sourceMappingURL=index.js.map
|