@kubb/mcp 5.0.0-beta.5 → 5.0.0-beta.7
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 +298 -148
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +292 -149
- package/dist/index.js.map +1 -1
- package/package.json +14 -10
- 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 +20 -27
- 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";
|
|
12
|
+
import { defineTool } from "tmcp/tool";
|
|
13
|
+
import { tool } from "tmcp/utils";
|
|
14
|
+
import * as v from "valibot";
|
|
10
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.7";
|
|
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",
|
|
@@ -215,8 +221,6 @@ async function loadModule(filePath) {
|
|
|
215
221
|
return mod;
|
|
216
222
|
}
|
|
217
223
|
async function loadUserConfig(configPath, { notify }) {
|
|
218
|
-
let userConfig;
|
|
219
|
-
let cwd;
|
|
220
224
|
if (configPath) {
|
|
221
225
|
const ext = path.extname(configPath);
|
|
222
226
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
@@ -232,41 +236,44 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
232
236
|
await notify(NotifyTypes.CONFIG_ERROR, msg);
|
|
233
237
|
throw new Error(msg);
|
|
234
238
|
}
|
|
235
|
-
cwd = path.dirname(resolvedConfigPath);
|
|
239
|
+
const cwd = path.dirname(resolvedConfigPath);
|
|
236
240
|
try {
|
|
237
|
-
userConfig = await loadModule(resolvedConfigPath);
|
|
241
|
+
const userConfig = await loadModule(resolvedConfigPath);
|
|
238
242
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`);
|
|
243
|
+
return {
|
|
244
|
+
userConfig,
|
|
245
|
+
cwd
|
|
246
|
+
};
|
|
239
247
|
} catch (error) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} else {
|
|
244
|
-
cwd = process.cwd();
|
|
245
|
-
const configFileNames = [
|
|
246
|
-
"kubb.config.ts",
|
|
247
|
-
"kubb.config.mts",
|
|
248
|
-
"kubb.config.cts",
|
|
249
|
-
"kubb.config.js",
|
|
250
|
-
"kubb.config.cjs"
|
|
251
|
-
];
|
|
252
|
-
for (const configFileName of configFileNames) {
|
|
253
|
-
const configFilePath = path.resolve(process.cwd(), configFileName);
|
|
254
|
-
if (!existsSync(configFilePath)) continue;
|
|
255
|
-
try {
|
|
256
|
-
userConfig = await loadModule(configFilePath);
|
|
257
|
-
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`);
|
|
258
|
-
break;
|
|
259
|
-
} 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);
|
|
260
251
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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)}`);
|
|
264
273
|
}
|
|
265
274
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
cwd
|
|
269
|
-
};
|
|
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(", ")}`);
|
|
270
277
|
}
|
|
271
278
|
//#endregion
|
|
272
279
|
//#region src/utils/resolveCwd.ts
|
|
@@ -285,40 +292,27 @@ function resolveCwd(userConfig, cwd) {
|
|
|
285
292
|
}
|
|
286
293
|
//#endregion
|
|
287
294
|
//#region src/utils/resolveUserConfig.ts
|
|
288
|
-
/**
|
|
289
|
-
* Resolve the config by handling function configs and returning the final configuration
|
|
290
|
-
*/
|
|
291
295
|
async function resolveUserConfig(config, options) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (isPromise(possiblePromise)) kubbUserConfig = possiblePromise;
|
|
299
|
-
else kubbUserConfig = Promise.resolve(possiblePromise);
|
|
300
|
-
}
|
|
301
|
-
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;
|
|
302
302
|
}
|
|
303
303
|
//#endregion
|
|
304
304
|
//#region src/tools/generate.ts
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
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) {
|
|
310
310
|
const { config: configPath, input, output, logLevel } = schema;
|
|
311
311
|
try {
|
|
312
312
|
const hooks = new AsyncEventEmitter();
|
|
313
313
|
const messages = [];
|
|
314
|
-
const notify = async (type, message,
|
|
314
|
+
const notify = async (type, message, _data) => {
|
|
315
315
|
messages.push(`${type}: ${message}`);
|
|
316
|
-
await handler.sendNotification("kubb/progress", {
|
|
317
|
-
type,
|
|
318
|
-
message,
|
|
319
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
320
|
-
...data
|
|
321
|
-
});
|
|
322
316
|
};
|
|
323
317
|
hooks.on("kubb:info", async ({ message }) => {
|
|
324
318
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -327,7 +321,7 @@ async function generate(schema, handler) {
|
|
|
327
321
|
await notify(NotifyTypes.SUCCESS, message);
|
|
328
322
|
});
|
|
329
323
|
hooks.on("kubb:error", async ({ error }) => {
|
|
330
|
-
await notify(NotifyTypes.ERROR, error.message
|
|
324
|
+
await notify(NotifyTypes.ERROR, error.message);
|
|
331
325
|
});
|
|
332
326
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
333
327
|
await notify(NotifyTypes.WARN, message);
|
|
@@ -359,7 +353,7 @@ async function generate(schema, handler) {
|
|
|
359
353
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
360
354
|
userConfig = configResult.userConfig;
|
|
361
355
|
cwd = configResult.cwd;
|
|
362
|
-
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.");
|
|
363
357
|
userConfig = await resolveUserConfig(userConfig, {
|
|
364
358
|
configPath,
|
|
365
359
|
logLevel
|
|
@@ -367,13 +361,7 @@ async function generate(schema, handler) {
|
|
|
367
361
|
} catch (error) {
|
|
368
362
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
369
363
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage);
|
|
370
|
-
return
|
|
371
|
-
content: [{
|
|
372
|
-
type: "text",
|
|
373
|
-
text: errorMessage
|
|
374
|
-
}],
|
|
375
|
-
isError: true
|
|
376
|
-
};
|
|
364
|
+
return tool.error(errorMessage);
|
|
377
365
|
}
|
|
378
366
|
const inputPath = input ?? (userConfig.input && "path" in userConfig.input ? userConfig.input.path : void 0);
|
|
379
367
|
const config = {
|
|
@@ -388,7 +376,7 @@ async function generate(schema, handler) {
|
|
|
388
376
|
path: output
|
|
389
377
|
} : userConfig.output
|
|
390
378
|
};
|
|
391
|
-
await notify(NotifyTypes.CONFIG_READY, "Configuration ready"
|
|
379
|
+
await notify(NotifyTypes.CONFIG_READY, "Configuration ready");
|
|
392
380
|
await notify(NotifyTypes.SETUP_START, "Setting up Kubb");
|
|
393
381
|
const kubb = createKubb(config, { hooks });
|
|
394
382
|
await kubb.setup();
|
|
@@ -398,79 +386,234 @@ async function generate(schema, handler) {
|
|
|
398
386
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
399
387
|
if (error || failedPlugins.size > 0) {
|
|
400
388
|
const allErrors = [error, ...Array.from(failedPlugins).filter((it) => it.error).map((it) => it.error)].filter(Boolean);
|
|
401
|
-
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)
|
|
402
|
-
|
|
403
|
-
errors: allErrors.map((err) => err.message)
|
|
404
|
-
});
|
|
405
|
-
return {
|
|
406
|
-
content: [{
|
|
407
|
-
type: "text",
|
|
408
|
-
text: `Build failed:\n${allErrors.map((err) => err.message).join("\n")}\n\n${messages.join("\n")}`
|
|
409
|
-
}],
|
|
410
|
-
isError: true
|
|
411
|
-
};
|
|
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")}`);
|
|
412
391
|
}
|
|
413
|
-
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files
|
|
414
|
-
return
|
|
415
|
-
type: "text",
|
|
416
|
-
text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`
|
|
417
|
-
}] };
|
|
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")}`);
|
|
418
394
|
} catch (caughtError) {
|
|
419
|
-
const error = caughtError;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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"
|
|
433
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
|
+
`;
|
|
434
529
|
}
|
|
435
530
|
//#endregion
|
|
436
|
-
//#region src/
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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;
|
|
443
569
|
try {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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;
|
|
466
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
|
+
});
|
|
467
610
|
}
|
|
468
611
|
//#endregion
|
|
469
612
|
//#region src/index.ts
|
|
470
|
-
async function run(_argv) {
|
|
471
|
-
await startServer();
|
|
613
|
+
async function run(_argv, options) {
|
|
614
|
+
await startServer(options);
|
|
472
615
|
}
|
|
473
616
|
//#endregion
|
|
474
|
-
export { run };
|
|
617
|
+
export { createMcpServer, run };
|
|
475
618
|
|
|
476
619
|
//# sourceMappingURL=index.js.map
|