@kubb/mcp 5.0.0-beta.8 → 5.0.0-beta.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -10
- package/README.md +29 -19
- package/dist/index.cjs +254 -218
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +254 -217
- package/dist/index.js.map +1 -1
- package/package.json +15 -24
- package/src/constants.ts +0 -1
- package/src/index.ts +0 -8
- package/src/schemas/generateSchema.ts +0 -13
- package/src/schemas/initSchema.ts +0 -7
- package/src/schemas/validateSchema.ts +0 -5
- package/src/server.ts +0 -41
- package/src/tools/generate.ts +0 -146
- package/src/tools/init.ts +0 -37
- package/src/tools/validate.ts +0 -25
- package/src/types.ts +0 -23
- package/src/utils/loadUserConfig.ts +0 -78
- package/src/utils/resolveCwd.ts +0 -20
- package/src/utils/resolveUserConfig.ts +0 -13
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import http from "node:http";
|
|
3
|
-
import { createRequestListener } from "@remix-run/node-fetch-server";
|
|
1
|
+
import "./rolldown-runtime-C0LytTxp.js";
|
|
4
2
|
import { ValibotJsonSchemaAdapter } from "@tmcp/adapter-valibot";
|
|
5
|
-
import { HttpTransport } from "@tmcp/transport-http";
|
|
6
3
|
import { StdioTransport } from "@tmcp/transport-stdio";
|
|
7
4
|
import { McpServer } from "tmcp";
|
|
8
5
|
import { EventEmitter } from "node:events";
|
|
9
|
-
import
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { createKubb } from "@kubb/core";
|
|
6
|
+
import { Diagnostics, createKubb } from "@kubb/core";
|
|
12
7
|
import { defineTool } from "tmcp/tool";
|
|
13
8
|
import { tool } from "tmcp/utils";
|
|
14
9
|
import * as v from "valibot";
|
|
10
|
+
import fs, { existsSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
15
12
|
import { createJiti } from "jiti";
|
|
16
13
|
import process$1 from "node:process";
|
|
17
14
|
//#region package.json
|
|
18
|
-
var version = "5.0.0-beta.
|
|
15
|
+
var version = "5.0.0-beta.80";
|
|
19
16
|
//#endregion
|
|
20
17
|
//#region ../../internals/utils/src/errors.ts
|
|
21
18
|
/**
|
|
@@ -63,9 +60,12 @@ var AsyncEventEmitter = class {
|
|
|
63
60
|
* await emitter.emit('build', 'petstore')
|
|
64
61
|
* ```
|
|
65
62
|
*/
|
|
66
|
-
|
|
63
|
+
emit(eventName, ...eventArgs) {
|
|
67
64
|
const listeners = this.#emitter.listeners(eventName);
|
|
68
65
|
if (listeners.length === 0) return;
|
|
66
|
+
return this.#emitAll(eventName, listeners, eventArgs);
|
|
67
|
+
}
|
|
68
|
+
async #emitAll(eventName, listeners, eventArgs) {
|
|
69
69
|
for (const listener of listeners) try {
|
|
70
70
|
await listener(...eventArgs);
|
|
71
71
|
} catch (err) {
|
|
@@ -90,21 +90,6 @@ var AsyncEventEmitter = class {
|
|
|
90
90
|
this.#emitter.on(eventName, handler);
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
* Registers a one-shot listener that removes itself after the first invocation.
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```ts
|
|
97
|
-
* emitter.onOnce('build', async (name) => { console.log(name) })
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
onOnce(eventName, handler) {
|
|
101
|
-
const wrapper = (...args) => {
|
|
102
|
-
this.off(eventName, wrapper);
|
|
103
|
-
return handler(...args);
|
|
104
|
-
};
|
|
105
|
-
this.on(eventName, wrapper);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
93
|
* Removes a previously registered listener.
|
|
109
94
|
*
|
|
110
95
|
* @example
|
|
@@ -128,6 +113,18 @@ var AsyncEventEmitter = class {
|
|
|
128
113
|
return this.#emitter.listenerCount(eventName);
|
|
129
114
|
}
|
|
130
115
|
/**
|
|
116
|
+
* Raises or lowers the per-event listener ceiling before Node warns about a memory leak.
|
|
117
|
+
* Set this above the expected listener count when many listeners attach by design.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* emitter.setMaxListeners(40)
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
setMaxListeners(max) {
|
|
125
|
+
this.#emitter.setMaxListeners(max);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
131
128
|
* Removes all listeners from every event channel.
|
|
132
129
|
*
|
|
133
130
|
* @example
|
|
@@ -153,31 +150,33 @@ function isPromise(result) {
|
|
|
153
150
|
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
154
151
|
}
|
|
155
152
|
//#endregion
|
|
156
|
-
//#region src/
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
153
|
+
//#region src/constants.ts
|
|
154
|
+
/**
|
|
155
|
+
* File extensions a Kubb config is allowed to use. A config path with any other
|
|
156
|
+
* extension is rejected before it is loaded.
|
|
157
|
+
*/
|
|
158
|
+
const ALLOWED_CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
159
|
+
".ts",
|
|
160
|
+
".mts",
|
|
161
|
+
".cts",
|
|
162
|
+
".js",
|
|
163
|
+
".mjs",
|
|
164
|
+
".cjs"
|
|
165
|
+
]);
|
|
166
|
+
/**
|
|
167
|
+
* Notification kinds reported back to the MCP client while loading config and
|
|
168
|
+
* running generation, covering progress, results, and errors.
|
|
169
|
+
*/
|
|
172
170
|
const NotifyTypes = {
|
|
173
171
|
INFO: "INFO",
|
|
174
172
|
SUCCESS: "SUCCESS",
|
|
175
173
|
ERROR: "ERROR",
|
|
176
174
|
WARN: "WARN",
|
|
175
|
+
DIAGNOSTIC: "DIAGNOSTIC",
|
|
177
176
|
PLUGIN_START: "PLUGIN_START",
|
|
178
177
|
PLUGIN_END: "PLUGIN_END",
|
|
179
178
|
FILES_START: "FILES_START",
|
|
180
|
-
|
|
179
|
+
FILES_UPDATE: "FILES_UPDATE",
|
|
181
180
|
FILES_END: "FILES_END",
|
|
182
181
|
GENERATION_START: "GENERATION_START",
|
|
183
182
|
GENERATION_END: "GENERATION_END",
|
|
@@ -189,37 +188,190 @@ const NotifyTypes = {
|
|
|
189
188
|
BUILD_START: "BUILD_START",
|
|
190
189
|
BUILD_END: "BUILD_END",
|
|
191
190
|
BUILD_FAILED: "BUILD_FAILED",
|
|
192
|
-
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
193
|
-
FATAL_ERROR: "FATAL_ERROR"
|
|
191
|
+
BUILD_SUCCESS: "BUILD_SUCCESS"
|
|
194
192
|
};
|
|
195
193
|
//#endregion
|
|
196
|
-
//#region src/
|
|
197
|
-
const
|
|
198
|
-
".ts",
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
194
|
+
//#region src/schemas/generateSchema.ts
|
|
195
|
+
const generateSchema = v.object({
|
|
196
|
+
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"))),
|
|
197
|
+
input: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Path to OpenAPI/Swagger spec file (overrides config)"))),
|
|
198
|
+
output: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Output directory path (overrides config)"))),
|
|
199
|
+
logLevel: v.optional(v.pipe(v.picklist([
|
|
200
|
+
"silent",
|
|
201
|
+
"info",
|
|
202
|
+
"verbose"
|
|
203
|
+
]), v.description("Log level for build output")), "info")
|
|
204
|
+
});
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region ../../internals/shared/src/constants.ts
|
|
207
|
+
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
208
|
+
const availablePlugins = [
|
|
209
|
+
{
|
|
210
|
+
value: "plugin-ts",
|
|
211
|
+
label: "TypeScript",
|
|
212
|
+
hint: "Recommended",
|
|
213
|
+
packageName: "@kubb/plugin-ts",
|
|
214
|
+
importName: "pluginTs",
|
|
215
|
+
category: "types"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
value: "plugin-axios",
|
|
219
|
+
label: "Axios Client",
|
|
220
|
+
packageName: "@kubb/plugin-axios",
|
|
221
|
+
importName: "pluginAxios",
|
|
222
|
+
category: "client"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
value: "plugin-fetch",
|
|
226
|
+
label: "Fetch Client",
|
|
227
|
+
packageName: "@kubb/plugin-fetch",
|
|
228
|
+
importName: "pluginFetch",
|
|
229
|
+
category: "client"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
value: "plugin-react-query",
|
|
233
|
+
label: "React Query / TanStack Query",
|
|
234
|
+
packageName: "@kubb/plugin-react-query",
|
|
235
|
+
importName: "pluginReactQuery",
|
|
236
|
+
category: "framework"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
value: "plugin-vue-query",
|
|
240
|
+
label: "Vue Query",
|
|
241
|
+
packageName: "@kubb/plugin-vue-query",
|
|
242
|
+
importName: "pluginVueQuery",
|
|
243
|
+
category: "framework"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
value: "plugin-zod",
|
|
247
|
+
label: "Zod Schemas",
|
|
248
|
+
packageName: "@kubb/plugin-zod",
|
|
249
|
+
importName: "pluginZod",
|
|
250
|
+
category: "validation"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
value: "plugin-faker",
|
|
254
|
+
label: "Faker.js Mocks",
|
|
255
|
+
packageName: "@kubb/plugin-faker",
|
|
256
|
+
importName: "pluginFaker",
|
|
257
|
+
category: "mocks"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
value: "plugin-msw",
|
|
261
|
+
label: "MSW Handlers",
|
|
262
|
+
packageName: "@kubb/plugin-msw",
|
|
263
|
+
importName: "pluginMsw",
|
|
264
|
+
category: "mocks"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
value: "plugin-cypress",
|
|
268
|
+
label: "Cypress Tests",
|
|
269
|
+
packageName: "@kubb/plugin-cypress",
|
|
270
|
+
importName: "pluginCypress",
|
|
271
|
+
category: "testing"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
value: "plugin-mcp",
|
|
275
|
+
label: "MCP Server (AI / Model Context Protocol)",
|
|
276
|
+
packageName: "@kubb/plugin-mcp",
|
|
277
|
+
importName: "pluginMcp",
|
|
278
|
+
category: "ai"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
value: "plugin-redoc",
|
|
282
|
+
label: "ReDoc Documentation",
|
|
283
|
+
packageName: "@kubb/plugin-redoc",
|
|
284
|
+
importName: "pluginRedoc",
|
|
285
|
+
category: "documentation"
|
|
286
|
+
}
|
|
287
|
+
];
|
|
205
288
|
//#endregion
|
|
206
|
-
//#region src/
|
|
207
|
-
|
|
289
|
+
//#region ../../internals/shared/src/init.ts
|
|
290
|
+
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
291
|
+
return `import { defineConfig } from 'kubb'
|
|
292
|
+
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
293
|
+
|
|
294
|
+
export default defineConfig({
|
|
295
|
+
root: '.',
|
|
296
|
+
input: {
|
|
297
|
+
path: '${inputPath}',
|
|
298
|
+
},
|
|
299
|
+
output: {
|
|
300
|
+
path: '${outputPath}',
|
|
301
|
+
clean: true,
|
|
302
|
+
},
|
|
303
|
+
plugins: [
|
|
304
|
+
${selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join("\n")}
|
|
305
|
+
],
|
|
306
|
+
})
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region ../../internals/shared/src/loader.ts
|
|
311
|
+
/**
|
|
312
|
+
* jiti options for loading Kubb config modules: the automatic JSX runtime pointed at
|
|
313
|
+
* `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.
|
|
314
|
+
*/
|
|
315
|
+
const JITI_OPTIONS = {
|
|
208
316
|
jsx: {
|
|
209
317
|
runtime: "automatic",
|
|
210
318
|
importSource: "@kubb/renderer-jsx"
|
|
211
319
|
},
|
|
212
320
|
moduleCache: false
|
|
213
|
-
}
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.
|
|
324
|
+
*
|
|
325
|
+
* jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
function createModuleLoader() {
|
|
333
|
+
const jiti = createJiti(import.meta.url, JITI_OPTIONS);
|
|
334
|
+
return { load(filePath, options) {
|
|
335
|
+
return options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath);
|
|
336
|
+
} };
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/utils.ts
|
|
340
|
+
/**
|
|
341
|
+
* Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry
|
|
342
|
+
* keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so
|
|
343
|
+
* the agent can act on the problem rather than parsing a bare message. No ANSI styling,
|
|
344
|
+
* unlike the CLI renderer.
|
|
345
|
+
*/
|
|
346
|
+
function formatDiagnostics(diagnostics) {
|
|
347
|
+
return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join("\n\n");
|
|
348
|
+
}
|
|
349
|
+
function formatDiagnostic(diagnostic) {
|
|
350
|
+
const { code, message, location, help, plugin, docsUrl } = diagnostic;
|
|
351
|
+
const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`];
|
|
352
|
+
if (location && "pointer" in location) lines.push(` at: ${location.pointer}`);
|
|
353
|
+
if (help) lines.push(` fix: ${help}`);
|
|
354
|
+
if (docsUrl) lines.push(` see: ${docsUrl}`);
|
|
355
|
+
return lines.join("\n");
|
|
356
|
+
}
|
|
357
|
+
const loader = createModuleLoader();
|
|
214
358
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
215
359
|
async function loadModule(filePath) {
|
|
216
360
|
const ext = path.extname(filePath);
|
|
217
361
|
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(", ")}`);
|
|
218
362
|
if (loadedModules.has(filePath)) return loadedModules.get(filePath);
|
|
219
|
-
const mod = await
|
|
363
|
+
const mod = await loader.load(filePath, { default: true });
|
|
220
364
|
loadedModules.set(filePath, mod);
|
|
221
365
|
return mod;
|
|
222
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Loads the user's Kubb config and returns it with the directory it was found in.
|
|
369
|
+
*
|
|
370
|
+
* When `configPath` is given it must use an allowed extension and resolve inside
|
|
371
|
+
* the current working directory, otherwise loading throws. When omitted, the
|
|
372
|
+
* known `kubb.config.*` file names are tried in the current directory. Every
|
|
373
|
+
* outcome is reported through `notify` before the function returns or throws.
|
|
374
|
+
*/
|
|
223
375
|
async function loadUserConfig(configPath, { notify }) {
|
|
224
376
|
if (configPath) {
|
|
225
377
|
const ext = path.extname(configPath);
|
|
@@ -275,8 +427,6 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
275
427
|
await notify(NotifyTypes.CONFIG_ERROR, "No config file found");
|
|
276
428
|
throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(", ")}`);
|
|
277
429
|
}
|
|
278
|
-
//#endregion
|
|
279
|
-
//#region src/utils/resolveCwd.ts
|
|
280
430
|
/**
|
|
281
431
|
* Determine the root directory based on userConfig.root and resolvedConfigDir
|
|
282
432
|
* 1. If userConfig.root exists and is absolute, use it as-is
|
|
@@ -290,8 +440,12 @@ function resolveCwd(userConfig, cwd) {
|
|
|
290
440
|
}
|
|
291
441
|
return cwd;
|
|
292
442
|
}
|
|
293
|
-
|
|
294
|
-
|
|
443
|
+
/**
|
|
444
|
+
* Normalizes a possible config into a single resolved `Config`.
|
|
445
|
+
*
|
|
446
|
+
* Calls the config when it is a function, awaits it when it is a promise, and
|
|
447
|
+
* picks the first entry when it resolves to an array.
|
|
448
|
+
*/
|
|
295
449
|
async function resolveUserConfig(config, options) {
|
|
296
450
|
const result = typeof config === "function" ? config({
|
|
297
451
|
logLevel: options.logLevel,
|
|
@@ -311,8 +465,8 @@ const generateTool = defineTool({
|
|
|
311
465
|
try {
|
|
312
466
|
const hooks = new AsyncEventEmitter();
|
|
313
467
|
const messages = [];
|
|
314
|
-
const notify = async (type, message,
|
|
315
|
-
messages.push(`${type}: ${message}`);
|
|
468
|
+
const notify = async (type, message, data) => {
|
|
469
|
+
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
316
470
|
};
|
|
317
471
|
hooks.on("kubb:info", async ({ message }) => {
|
|
318
472
|
await notify(NotifyTypes.INFO, message);
|
|
@@ -326,6 +480,9 @@ const generateTool = defineTool({
|
|
|
326
480
|
hooks.on("kubb:warn", async ({ message }) => {
|
|
327
481
|
await notify(NotifyTypes.WARN, message);
|
|
328
482
|
});
|
|
483
|
+
hooks.on("kubb:diagnostic", async ({ diagnostic }) => {
|
|
484
|
+
await notify(NotifyTypes.DIAGNOSTIC, diagnostic.message, Diagnostics.serialize(diagnostic));
|
|
485
|
+
});
|
|
329
486
|
hooks.on("kubb:plugin:start", async ({ plugin }) => {
|
|
330
487
|
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`);
|
|
331
488
|
});
|
|
@@ -335,8 +492,8 @@ const generateTool = defineTool({
|
|
|
335
492
|
hooks.on("kubb:files:processing:start", async () => {
|
|
336
493
|
await notify(NotifyTypes.FILES_START, "Starting file processing");
|
|
337
494
|
});
|
|
338
|
-
hooks.on("kubb:
|
|
339
|
-
await notify(NotifyTypes.
|
|
495
|
+
hooks.on("kubb:files:processing:update", async ({ files }) => {
|
|
496
|
+
await notify(NotifyTypes.FILES_UPDATE, `Processing ${files.length} files`);
|
|
340
497
|
});
|
|
341
498
|
hooks.on("kubb:files:processing:end", async () => {
|
|
342
499
|
await notify(NotifyTypes.FILES_END, "File processing complete");
|
|
@@ -382,152 +539,23 @@ const generateTool = defineTool({
|
|
|
382
539
|
await kubb.setup();
|
|
383
540
|
await notify(NotifyTypes.SETUP_END, "Kubb setup complete");
|
|
384
541
|
await notify(NotifyTypes.BUILD_START, "Starting build");
|
|
385
|
-
const { files,
|
|
542
|
+
const { files, diagnostics } = await kubb.safeBuild();
|
|
386
543
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
544
|
+
const problems = diagnostics.filter(Diagnostics.isProblem);
|
|
545
|
+
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
546
|
+
if (errors.length > 0) {
|
|
547
|
+
await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${errors.length} diagnostic(s)`);
|
|
548
|
+
const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic));
|
|
549
|
+
return tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
391
550
|
}
|
|
392
551
|
await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`);
|
|
393
552
|
return tool.text(`Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join("\n")}`);
|
|
394
553
|
} catch (caughtError) {
|
|
395
|
-
const
|
|
396
|
-
return tool.error(`Build error
|
|
554
|
+
const serialized = Diagnostics.serialize(Diagnostics.from(caughtError));
|
|
555
|
+
return tool.error(`Build error:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
397
556
|
}
|
|
398
557
|
});
|
|
399
558
|
//#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"
|
|
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
|
-
`;
|
|
529
|
-
}
|
|
530
|
-
//#endregion
|
|
531
559
|
//#region src/schemas/initSchema.ts
|
|
532
560
|
const initSchema = v.object({
|
|
533
561
|
input: v.optional(v.pipe(v.string(), v.minLength(1), v.description("Path to OpenAPI spec (default: ./openapi.yaml)"))),
|
|
@@ -536,6 +564,10 @@ const initSchema = v.object({
|
|
|
536
564
|
});
|
|
537
565
|
//#endregion
|
|
538
566
|
//#region src/tools/init.ts
|
|
567
|
+
/**
|
|
568
|
+
* Resolves a comma-separated plugin flag into the matching known plugin options.
|
|
569
|
+
* Unrecognized names are dropped, and a missing flag yields an empty list.
|
|
570
|
+
*/
|
|
539
571
|
function resolvePlugins(pluginsFlag) {
|
|
540
572
|
if (!pluginsFlag) return [];
|
|
541
573
|
const requested = pluginsFlag.split(",").map((v) => v.trim()).filter(Boolean);
|
|
@@ -575,11 +607,18 @@ const validateTool = defineTool({
|
|
|
575
607
|
await mod.adapterOas().validate(input, { throwOnError: true });
|
|
576
608
|
return tool.text(`Validation successful: ${input}`);
|
|
577
609
|
} catch (err) {
|
|
578
|
-
|
|
610
|
+
const serialized = Diagnostics.serialize(Diagnostics.from(err));
|
|
611
|
+
return tool.error(`Validation failed:\n${formatDiagnostics([serialized])}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
579
612
|
}
|
|
580
613
|
});
|
|
581
614
|
//#endregion
|
|
582
615
|
//#region src/server.ts
|
|
616
|
+
/**
|
|
617
|
+
* Builds the Kubb MCP server with the generate, validate, and init tools registered.
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* `const server = createMcpServer()`
|
|
621
|
+
*/
|
|
583
622
|
function createMcpServer() {
|
|
584
623
|
const server = new McpServer({
|
|
585
624
|
name: "Kubb",
|
|
@@ -595,23 +634,21 @@ function createMcpServer() {
|
|
|
595
634
|
]);
|
|
596
635
|
return server;
|
|
597
636
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
});
|
|
637
|
+
/**
|
|
638
|
+
* Starts the Kubb MCP server over stdio, the transport every local MCP client
|
|
639
|
+
* (Claude, Copilot, editors) uses when it launches the server as a subprocess.
|
|
640
|
+
*/
|
|
641
|
+
async function startServer() {
|
|
642
|
+
new StdioTransport(createMcpServer()).listen();
|
|
610
643
|
}
|
|
611
644
|
//#endregion
|
|
612
645
|
//#region src/index.ts
|
|
613
|
-
|
|
614
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Entry point that starts the MCP server over stdio. The argument is accepted
|
|
648
|
+
* for CLI parity but ignored.
|
|
649
|
+
*/
|
|
650
|
+
async function run(_argv) {
|
|
651
|
+
await startServer();
|
|
615
652
|
}
|
|
616
653
|
//#endregion
|
|
617
654
|
export { createMcpServer, run };
|