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