@kubb/mcp 5.8.4 → 5.9.1
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 +41 -18
- package/dist/index.cjs +51 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +51 -109
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
### Model Context Protocol server for Kubb
|
|
26
26
|
|
|
27
|
-
MCP server for Kubb.
|
|
27
|
+
`@kubb/mcp` is the MCP server for Kubb. It exposes Kubb code generation as tools over the [Model Context Protocol](https://modelcontextprotocol.io), so AI assistants such as [Claude](https://claude.ai) and [Cursor](https://cursor.sh) can validate a spec and generate TypeScript types and API clients from the chat.
|
|
28
28
|
|
|
29
|
-
The server
|
|
29
|
+
The server has three tools. `generate` runs a full Kubb build from a `kubb.config.ts` and returns its build events when the build succeeds. A failed build returns its diagnostics instead. `validate` checks an OpenAPI spec, and `init` scaffolds a `kubb.config.ts`.
|
|
30
30
|
|
|
31
31
|
## Installation
|
|
32
32
|
|
|
@@ -41,11 +41,11 @@ npm install -D @kubb/mcp
|
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
> [!IMPORTANT]
|
|
44
|
-
>
|
|
44
|
+
> The `kubb mcp` command comes from `@kubb/cli`, which the `kubb` package includes. Install `kubb` to use it.
|
|
45
45
|
|
|
46
46
|
## Usage
|
|
47
47
|
|
|
48
|
-
### Start the MCP
|
|
48
|
+
### Start the MCP server
|
|
49
49
|
|
|
50
50
|
Start the server using the Kubb CLI:
|
|
51
51
|
|
|
@@ -59,11 +59,11 @@ Or run it directly as a standalone package:
|
|
|
59
59
|
npx @kubb/mcp
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Both commands start the same server over stdio.
|
|
63
63
|
|
|
64
|
-
### Configure MCP
|
|
64
|
+
### Configure an MCP client
|
|
65
65
|
|
|
66
|
-
Add to your MCP client
|
|
66
|
+
Add one of these entries to your MCP client's config, such as `claude_desktop_config.json` for [Claude Desktop](https://claude.ai/download).
|
|
67
67
|
|
|
68
68
|
Using `kubb mcp`:
|
|
69
69
|
|
|
@@ -91,34 +91,39 @@ Using the standalone package:
|
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
The server looks for `kubb.config.*` in the directory the client starts it in, and rejects a `config` path outside that directory. Claude Code starts it in the folder you run `claude` from, so register it from the project root:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
claude mcp add kubb -- npx kubb mcp
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Clients such as Claude Code ignore a `cwd` key in this config. When a client starts the server in another directory, change into the project first (macOS and Linux):
|
|
95
101
|
|
|
96
102
|
```json
|
|
97
103
|
{
|
|
98
104
|
"mcpServers": {
|
|
99
105
|
"kubb-petstore": {
|
|
100
|
-
"command": "
|
|
101
|
-
"args": ["
|
|
102
|
-
"cwd": "/path/to/your/project"
|
|
106
|
+
"command": "sh",
|
|
107
|
+
"args": ["-c", "cd '/path/to/your/project' && npx kubb mcp"]
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
```
|
|
107
112
|
|
|
108
|
-
### Available
|
|
113
|
+
### Available tools
|
|
109
114
|
|
|
110
115
|
#### `generate`
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
Runs a Kubb build from your config. A successful build returns the build events, and a failed build returns its diagnostics.
|
|
113
118
|
|
|
114
|
-
|
|
119
|
+
Parameters:
|
|
115
120
|
|
|
116
|
-
- `config` (string, optional): Path to
|
|
121
|
+
- `config` (string, optional): Path to a config file inside the working directory. Defaults to `kubb.config.ts` (or `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`) in the working directory
|
|
117
122
|
- `input` (string, optional): Path to OpenAPI/Swagger spec file (overrides config file setting)
|
|
118
123
|
- `output` (string, optional): Output directory path (overrides config file setting)
|
|
119
|
-
- `logLevel` (enum, optional):
|
|
124
|
+
- `logLevel` (enum, optional): One of `'silent'`, `'info'`, `'verbose'` (default: 'info'), passed to a config that exports a function
|
|
120
125
|
|
|
121
|
-
|
|
126
|
+
Examples:
|
|
122
127
|
|
|
123
128
|
Using default config file:
|
|
124
129
|
|
|
@@ -147,9 +152,27 @@ With verbose logging:
|
|
|
147
152
|
}
|
|
148
153
|
```
|
|
149
154
|
|
|
155
|
+
#### `validate`
|
|
156
|
+
|
|
157
|
+
Validates an OpenAPI/Swagger specification from a path or URL.
|
|
158
|
+
|
|
159
|
+
Parameters:
|
|
160
|
+
|
|
161
|
+
- `input` (string, required): Path or URL to the OpenAPI/Swagger specification
|
|
162
|
+
|
|
163
|
+
#### `init`
|
|
164
|
+
|
|
165
|
+
Writes a `kubb.config.ts` in the working directory without prompts. It does not install packages, and it fails when `kubb.config.ts` already exists.
|
|
166
|
+
|
|
167
|
+
Parameters:
|
|
168
|
+
|
|
169
|
+
- `input` (string, optional): Path to the OpenAPI spec (default: `./openapi.yaml`)
|
|
170
|
+
- `output` (string, optional): Output directory (default: `./src/gen`)
|
|
171
|
+
- `plugins` (string, optional): Comma-separated list of plugins, such as `plugin-ts,plugin-zod`
|
|
172
|
+
|
|
150
173
|
## Configuration
|
|
151
174
|
|
|
152
|
-
The
|
|
175
|
+
The `generate` tool looks for `kubb.config.ts` in the working directory by default. Pass `config` to use another config file in that directory.
|
|
153
176
|
|
|
154
177
|
Example `kubb.config.ts`:
|
|
155
178
|
|
package/dist/index.cjs
CHANGED
|
@@ -40,7 +40,7 @@ let node_process = require("node:process");
|
|
|
40
40
|
node_process = __toESM(node_process, 1);
|
|
41
41
|
let _kubb_adapter_oas = require("@kubb/adapter-oas");
|
|
42
42
|
//#region package.json
|
|
43
|
-
var version = "5.
|
|
43
|
+
var version = "5.9.1";
|
|
44
44
|
//#endregion
|
|
45
45
|
//#region ../../internals/shared/src/constants.ts
|
|
46
46
|
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
@@ -146,8 +146,7 @@ function resolvePlugins(pluginsFlag) {
|
|
|
146
146
|
return availablePlugins.filter((plugin) => requested.includes(plugin.value));
|
|
147
147
|
}
|
|
148
148
|
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
149
|
-
return `import { defineConfig } from 'kubb/config'
|
|
150
|
-
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
149
|
+
return `${[`import { defineConfig } from 'kubb/config'`, ...selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`)].join("\n")}
|
|
151
150
|
|
|
152
151
|
export default defineConfig({
|
|
153
152
|
input: '${inputPath}',
|
|
@@ -179,28 +178,26 @@ const JITI_OPTIONS = {
|
|
|
179
178
|
* lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.
|
|
180
179
|
*/
|
|
181
180
|
const JAVASCRIPT_EXTENSION = /\.[cm]?jsx?$/;
|
|
181
|
+
const jiti$1 = (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, JITI_OPTIONS);
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
183
|
+
* Loads a Kubb TypeScript or JavaScript config module via jiti.
|
|
184
184
|
*
|
|
185
185
|
* jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load
|
|
186
186
|
* re-evaluates the file, so a config edited on disk is picked up by the next load.
|
|
187
187
|
*
|
|
188
188
|
* @example
|
|
189
189
|
* ```ts
|
|
190
|
-
* const config = await
|
|
190
|
+
* const config = await loadModule('/abs/kubb.config.ts', { default: true })
|
|
191
191
|
* ```
|
|
192
192
|
*/
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
return options?.default ? mod?.default ?? mod : mod;
|
|
203
|
-
} };
|
|
193
|
+
async function loadModule(filePath, options) {
|
|
194
|
+
if (!JAVASCRIPT_EXTENSION.test(filePath)) return options?.default ? jiti$1.import(filePath, { default: true }) : jiti$1.import(filePath);
|
|
195
|
+
const mod = await jiti$1.evalModule(await (0, node_fs_promises.readFile)(filePath, "utf8"), {
|
|
196
|
+
filename: filePath,
|
|
197
|
+
async: true,
|
|
198
|
+
forceTranspile: true
|
|
199
|
+
});
|
|
200
|
+
return options?.default ? mod?.default ?? mod : mod;
|
|
204
201
|
}
|
|
205
202
|
//#endregion
|
|
206
203
|
//#region ../../internals/utils/src/fs.ts
|
|
@@ -226,19 +223,6 @@ function isPathInside(path, parent) {
|
|
|
226
223
|
return rel !== "" && !rel.startsWith("..") && !(0, node_path.isAbsolute)(rel);
|
|
227
224
|
}
|
|
228
225
|
//#endregion
|
|
229
|
-
//#region ../../internals/utils/src/promise.ts
|
|
230
|
-
/** Returns `true` when `result` is a thenable `Promise`.
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* ```ts
|
|
234
|
-
* isPromise(Promise.resolve(1)) // true
|
|
235
|
-
* isPromise(42) // false
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
function isPromise(result) {
|
|
239
|
-
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
240
|
-
}
|
|
241
|
-
//#endregion
|
|
242
226
|
//#region src/utils.ts
|
|
243
227
|
const CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`);
|
|
244
228
|
/**
|
|
@@ -258,10 +242,6 @@ function formatDiagnostic(diagnostic) {
|
|
|
258
242
|
if (docsUrl) lines.push(` see: ${docsUrl}`);
|
|
259
243
|
return lines.join("\n");
|
|
260
244
|
}
|
|
261
|
-
const loader = createModuleLoader();
|
|
262
|
-
function loadModule(filePath) {
|
|
263
|
-
return loader.load(filePath, { default: true });
|
|
264
|
-
}
|
|
265
245
|
/**
|
|
266
246
|
* Loads the user's Kubb config and returns it with the directory it was found in.
|
|
267
247
|
*
|
|
@@ -275,27 +255,27 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
275
255
|
const ext = node_path.default.extname(configPath);
|
|
276
256
|
if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {
|
|
277
257
|
const msg = `Invalid config file extension "${ext}". Allowed: ${CONFIG_EXTENSIONS.join(", ")}`;
|
|
278
|
-
|
|
258
|
+
notify("CONFIG_ERROR", msg);
|
|
279
259
|
throw new Error(msg);
|
|
280
260
|
}
|
|
281
261
|
const base = node_path.default.resolve(process.cwd());
|
|
282
262
|
const resolvedConfigPath = node_path.default.resolve(base, configPath);
|
|
283
263
|
if (!isPathInside(resolvedConfigPath, base)) {
|
|
284
264
|
const msg = "Invalid config file path: must be within the current working directory";
|
|
285
|
-
|
|
265
|
+
notify("CONFIG_ERROR", msg);
|
|
286
266
|
throw new Error(msg);
|
|
287
267
|
}
|
|
288
268
|
const cwd = node_path.default.dirname(resolvedConfigPath);
|
|
289
269
|
try {
|
|
290
|
-
const userConfig = await loadModule(resolvedConfigPath);
|
|
291
|
-
|
|
270
|
+
const userConfig = await loadModule(resolvedConfigPath, { default: true });
|
|
271
|
+
notify("CONFIG_LOADED", `Loaded config from ${resolvedConfigPath}`);
|
|
292
272
|
return {
|
|
293
273
|
userConfig,
|
|
294
274
|
cwd
|
|
295
275
|
};
|
|
296
276
|
} catch (error) {
|
|
297
277
|
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
298
|
-
|
|
278
|
+
notify("CONFIG_ERROR", msg);
|
|
299
279
|
throw new Error(msg);
|
|
300
280
|
}
|
|
301
281
|
}
|
|
@@ -304,33 +284,19 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
304
284
|
const configFilePath = node_path.default.resolve(process.cwd(), configFileName);
|
|
305
285
|
if (!(0, node_fs.existsSync)(configFilePath)) continue;
|
|
306
286
|
try {
|
|
307
|
-
const userConfig = await loadModule(configFilePath);
|
|
308
|
-
|
|
287
|
+
const userConfig = await loadModule(configFilePath, { default: true });
|
|
288
|
+
notify("CONFIG_LOADED", `Loaded ${configFileName} from current directory`);
|
|
309
289
|
return {
|
|
310
290
|
userConfig,
|
|
311
291
|
cwd
|
|
312
292
|
};
|
|
313
293
|
} catch (err) {
|
|
314
|
-
|
|
294
|
+
notify("CONFIG_ERROR", `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
315
295
|
}
|
|
316
296
|
}
|
|
317
|
-
|
|
297
|
+
notify("CONFIG_ERROR", "No config file found");
|
|
318
298
|
throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(", ")}`);
|
|
319
299
|
}
|
|
320
|
-
/**
|
|
321
|
-
* Normalizes a possible config into a single resolved `Config`.
|
|
322
|
-
*
|
|
323
|
-
* Calls the config when it is a function, awaits it when it is a promise, and
|
|
324
|
-
* picks the first entry when it resolves to an array.
|
|
325
|
-
*/
|
|
326
|
-
async function resolveUserConfig(config, options) {
|
|
327
|
-
const result = typeof config === "function" ? config({
|
|
328
|
-
logLevel: options.logLevel,
|
|
329
|
-
config: options.configPath
|
|
330
|
-
}) : config;
|
|
331
|
-
const resolved = isPromise(result) ? await result : result;
|
|
332
|
-
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
333
|
-
}
|
|
334
300
|
//#endregion
|
|
335
301
|
//#region src/tools/generate.ts
|
|
336
302
|
const generateSchema = zod_mini.object({
|
|
@@ -352,63 +318,39 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
352
318
|
try {
|
|
353
319
|
const hooks = new _kubb_core.Hookable();
|
|
354
320
|
const messages = [];
|
|
355
|
-
const notify =
|
|
321
|
+
const notify = (type, message, data) => {
|
|
356
322
|
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
357
323
|
};
|
|
358
|
-
hooks.hook("kubb:info",
|
|
359
|
-
|
|
360
|
-
});
|
|
361
|
-
hooks.hook("kubb:
|
|
362
|
-
|
|
363
|
-
});
|
|
364
|
-
hooks.hook("kubb:
|
|
365
|
-
|
|
366
|
-
});
|
|
367
|
-
hooks.hook("kubb:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
hooks.hook("kubb:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
hooks.hook("kubb:plugin:start", async ({ plugin }) => {
|
|
374
|
-
await notify("PLUGIN_START", `Plugin starting: ${plugin.name}`);
|
|
375
|
-
});
|
|
376
|
-
hooks.hook("kubb:plugin:end", async ({ plugin, duration }) => {
|
|
377
|
-
await notify("PLUGIN_END", `Plugin finished: ${plugin.name}`, { duration });
|
|
378
|
-
});
|
|
379
|
-
hooks.hook("kubb:files:processing:start", async () => {
|
|
380
|
-
await notify("FILES_START", "Starting file processing");
|
|
381
|
-
});
|
|
382
|
-
hooks.hook("kubb:files:processing:update", async ({ files }) => {
|
|
383
|
-
await notify("FILES_UPDATE", `Processing ${files.length} files`);
|
|
384
|
-
});
|
|
385
|
-
hooks.hook("kubb:files:processing:end", async () => {
|
|
386
|
-
await notify("FILES_END", "File processing complete");
|
|
387
|
-
});
|
|
388
|
-
hooks.hook("kubb:generation:start", async () => {
|
|
389
|
-
await notify("GENERATION_START", "Generation started");
|
|
390
|
-
});
|
|
391
|
-
hooks.hook("kubb:setup:start", async () => {
|
|
392
|
-
await notify("SETUP_START", "Setting up Kubb");
|
|
393
|
-
});
|
|
394
|
-
hooks.hook("kubb:setup:end", async () => {
|
|
395
|
-
await notify("SETUP_END", "Kubb setup complete");
|
|
396
|
-
await notify("BUILD_START", "Starting build");
|
|
397
|
-
});
|
|
398
|
-
hooks.hook("kubb:generation:end", async () => {
|
|
399
|
-
await notify("GENERATION_END", "Generation ended");
|
|
324
|
+
hooks.hook("kubb:info", ({ message }) => notify("INFO", message));
|
|
325
|
+
hooks.hook("kubb:success", ({ message }) => notify("SUCCESS", message));
|
|
326
|
+
hooks.hook("kubb:error", ({ error }) => notify("ERROR", error.message));
|
|
327
|
+
hooks.hook("kubb:warn", ({ message }) => notify("WARN", message));
|
|
328
|
+
hooks.hook("kubb:diagnostic", ({ diagnostic }) => notify("DIAGNOSTIC", diagnostic.message, _kubb_core.Diagnostics.serialize(diagnostic)));
|
|
329
|
+
hooks.hook("kubb:plugin:start", ({ plugin }) => notify("PLUGIN_START", `Plugin starting: ${plugin.name}`));
|
|
330
|
+
hooks.hook("kubb:plugin:end", ({ plugin, duration }) => notify("PLUGIN_END", `Plugin finished: ${plugin.name}`, { duration }));
|
|
331
|
+
hooks.hook("kubb:files:processing:start", () => notify("FILES_START", "Starting file processing"));
|
|
332
|
+
hooks.hook("kubb:files:processing:update", ({ files }) => notify("FILES_UPDATE", `Processing ${files.length} files`));
|
|
333
|
+
hooks.hook("kubb:files:processing:end", () => notify("FILES_END", "File processing complete"));
|
|
334
|
+
hooks.hook("kubb:generation:start", () => notify("GENERATION_START", "Generation started"));
|
|
335
|
+
hooks.hook("kubb:setup:start", () => notify("SETUP_START", "Setting up Kubb"));
|
|
336
|
+
hooks.hook("kubb:setup:end", () => {
|
|
337
|
+
notify("SETUP_END", "Kubb setup complete");
|
|
338
|
+
notify("BUILD_START", "Starting build");
|
|
400
339
|
});
|
|
340
|
+
hooks.hook("kubb:generation:end", () => notify("GENERATION_END", "Generation ended"));
|
|
401
341
|
let userConfig;
|
|
402
342
|
let cwd;
|
|
403
343
|
try {
|
|
404
344
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
405
|
-
|
|
345
|
+
const loaded = configResult.userConfig;
|
|
406
346
|
cwd = configResult.cwd;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
configPath
|
|
410
|
-
|
|
411
|
-
|
|
347
|
+
const resolved = await (typeof loaded === "function" ? loaded({
|
|
348
|
+
logLevel,
|
|
349
|
+
config: configPath
|
|
350
|
+
}) : loaded);
|
|
351
|
+
const first = Array.isArray(resolved) ? resolved[0] : resolved;
|
|
352
|
+
if (!first) throw new Error("The config file exports an empty array of configurations");
|
|
353
|
+
userConfig = first;
|
|
412
354
|
} catch (error) {
|
|
413
355
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
414
356
|
return tmcp_utils.tool.error(errorMessage);
|
|
@@ -422,17 +364,17 @@ const generateTool = (0, tmcp_tool.defineTool)({
|
|
|
422
364
|
path: output
|
|
423
365
|
} : userConfig.output
|
|
424
366
|
};
|
|
425
|
-
|
|
367
|
+
notify("CONFIG_READY", "Configuration ready");
|
|
426
368
|
const result = await (0, _kubb_core.createKubb)(config, { hooks }).generate();
|
|
427
|
-
|
|
369
|
+
notify("BUILD_END", `Build complete - Generated ${result.files.length} files`);
|
|
428
370
|
const problems = result.diagnostics.filter(_kubb_core.Diagnostics.isProblem);
|
|
429
371
|
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
430
372
|
if (errors.length > 0) {
|
|
431
|
-
|
|
373
|
+
notify("BUILD_FAILED", `Build failed with ${errors.length} diagnostic(s)`);
|
|
432
374
|
const serialized = problems.map((diagnostic) => _kubb_core.Diagnostics.serialize(diagnostic));
|
|
433
375
|
return tmcp_utils.tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
434
376
|
}
|
|
435
|
-
|
|
377
|
+
notify("BUILD_SUCCESS", `Build completed successfully - Generated ${result.files.length} files`);
|
|
436
378
|
return tmcp_utils.tool.text(`Build completed successfully!\n\nGenerated ${result.files.length} files\n\n${messages.join("\n")}`);
|
|
437
379
|
} catch (caughtError) {
|
|
438
380
|
const serialized = _kubb_core.Diagnostics.serialize(_kubb_core.Diagnostics.from(caughtError));
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["jiti","createJiti","readFile","resolve","relative","isAbsolute","path","existsSync","z","defineTool","Hookable","Diagnostics","tool","path","createKubb","z","defineTool","path","process","fs","tool","z","defineTool","adapterOas","tool","Diagnostics","JsonSchemaAdapter","z","en","McpServer","StdioTransport"],"sources":["../package.json","../../../internals/shared/src/constants.ts","../../../internals/shared/src/init.ts","../../../internals/shared/src/loader.ts","../../../internals/utils/src/fs.ts","../../../internals/utils/src/promise.ts","../src/utils.ts","../src/tools/generate.ts","../src/tools/init.ts","../src/tools/validate.ts","../src/server.ts"],"sourcesContent":["","import type { PluginOption } from './types.ts'\n\nexport const KUBB_CONFIG_FILENAME = 'kubb.config.ts' as const\n\n/** File extensions a Kubb config file may use. */\nexport const CONFIG_EXTENSIONS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const\n\nexport const KUBB_PACKAGE_NAME = 'kubb' as const\n\nexport const initDefaults = {\n inputPath: './openapi.yaml',\n outputPath: './src/gen',\n plugins: ['plugin-ts'],\n} as const\n\nexport const availablePlugins: Array<PluginOption> = [\n {\n value: 'plugin-ts',\n label: 'TypeScript',\n hint: 'Recommended',\n packageName: '@kubb/plugin-ts',\n importName: 'pluginTs',\n category: 'types',\n },\n {\n value: 'plugin-axios',\n label: 'Axios Client',\n packageName: '@kubb/plugin-axios',\n importName: 'pluginAxios',\n category: 'client',\n },\n {\n value: 'plugin-fetch',\n label: 'Fetch Client',\n packageName: '@kubb/plugin-fetch',\n importName: 'pluginFetch',\n category: 'client',\n },\n {\n value: 'plugin-react-query',\n label: 'React Query / TanStack Query',\n packageName: '@kubb/plugin-react-query',\n importName: 'pluginReactQuery',\n category: 'framework',\n },\n {\n value: 'plugin-vue-query',\n label: 'Vue Query',\n packageName: '@kubb/plugin-vue-query',\n importName: 'pluginVueQuery',\n category: 'framework',\n },\n {\n value: 'plugin-zod',\n label: 'Zod Schemas',\n packageName: '@kubb/plugin-zod',\n importName: 'pluginZod',\n category: 'validation',\n },\n {\n value: 'plugin-faker',\n label: 'Faker.js Mocks',\n packageName: '@kubb/plugin-faker',\n importName: 'pluginFaker',\n category: 'mocks',\n },\n {\n value: 'plugin-msw',\n label: 'MSW Handlers',\n packageName: '@kubb/plugin-msw',\n importName: 'pluginMsw',\n category: 'mocks',\n },\n {\n value: 'plugin-cypress',\n label: 'Cypress Tests',\n packageName: '@kubb/plugin-cypress',\n importName: 'pluginCypress',\n category: 'testing',\n },\n {\n value: 'plugin-mcp',\n label: 'MCP Server (AI / Model Context Protocol)',\n packageName: '@kubb/plugin-mcp',\n importName: 'pluginMcp',\n category: 'ai',\n },\n {\n value: 'plugin-redoc',\n label: 'ReDoc Documentation',\n packageName: '@kubb/plugin-redoc',\n importName: 'pluginRedoc',\n category: 'documentation',\n },\n]\n","import { availablePlugins, KUBB_PACKAGE_NAME } from './constants.ts'\nimport type { PluginOption } from './types.ts'\n\n/**\n * Resolves a comma-separated plugin flag (e.g. `--plugins plugin-ts,plugin-zod`) into the\n * matching known plugin options. Unrecognized names are dropped, and a missing flag yields\n * an empty list.\n */\nexport function resolvePlugins(pluginsFlag: string | undefined): Array<PluginOption> {\n if (!pluginsFlag) {\n return []\n }\n const requested = pluginsFlag\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n return availablePlugins.filter((plugin) => requested.includes(plugin.value))\n}\n\nexport function generateConfigFile({\n selectedPlugins,\n inputPath,\n outputPath,\n}: {\n selectedPlugins: Array<PluginOption>\n inputPath: string\n outputPath: string\n}): string {\n const imports = selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join('\\n')\n\n const pluginConfigs = selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join('\\n')\n\n return `import { defineConfig } from 'kubb/config'\n${imports}\n\nexport default defineConfig({\n input: '${inputPath}',\n output: {\n path: '${outputPath}',\n clean: true,\n },\n plugins: [\n${pluginConfigs}\n ],\n})\n`\n}\n\n/**\n * Turns package names into install specifiers for the wizard.\n *\n * `kubb` is pinned to the exact version of the running CLI, since both ship from the same release\n * and resolving `kubb@beta` separately can land on a different version than the CLI doing the\n * scaffolding. Plugins release from their own repo on their own cadence, so they follow the\n * release channel of the CLI through its dist-tag.\n */\nexport function resolveInstallVersions({ packages, version }: { packages: Array<string>; version: string }): Array<string> {\n const prerelease = version.match(/-([a-z]+)/)?.[1]\n const tag = prerelease ?? 'latest'\n\n return packages.map((name) => (name === KUBB_PACKAGE_NAME ? `${name}@${version}` : `${name}@${tag}`))\n}\n","import { readFile } from 'node:fs/promises'\nimport { createJiti } from 'jiti'\n\n/**\n * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at\n * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.\n */\nconst JITI_OPTIONS = {\n jsx: { runtime: 'automatic', importSource: '@kubb/renderer-jsx' },\n moduleCache: false,\n} satisfies Parameters<typeof createJiti>[1]\n\n/**\n * Plain JavaScript files. jiti hands these to Node's native `import()`, whose module cache never\n * lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.\n */\nconst JAVASCRIPT_EXTENSION = /\\.[cm]?jsx?$/\n\n/**\n * Per-call options for {@link ModuleLoader.load}.\n */\ntype LoadModuleOptions = {\n /**\n * Return the module's default export instead of the full namespace.\n */\n default?: boolean\n}\n\n/**\n * Loads `.ts`/`.js` modules via jiti.\n */\ntype ModuleLoader = {\n load<T = unknown>(filePath: string, options?: LoadModuleOptions): Promise<T>\n}\n\n/**\n * Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.\n *\n * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load\n * re-evaluates the file, so a config edited on disk is picked up by the next load.\n *\n * @example\n * ```ts\n * const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })\n * ```\n */\nexport function createModuleLoader(): ModuleLoader {\n const jiti = createJiti(import.meta.url, JITI_OPTIONS)\n\n return {\n async load<T>(filePath: string, options?: LoadModuleOptions) {\n if (!JAVASCRIPT_EXTENSION.test(filePath)) {\n return (options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath)) as Promise<T>\n }\n\n // Transpiled through jiti instead, so every load re-evaluates the file as it is on disk now.\n // A TypeScript file it imports reloads too. A plain JavaScript file it imports still goes\n // through Node's `import()` and stays cached until the process restarts.\n const mod = (await jiti.evalModule(await readFile(filePath, 'utf8'), { filename: filePath, async: true, forceTranspile: true })) as\n | { default?: unknown }\n | undefined\n return (options?.default ? (mod?.default ?? mod) : mod) as T\n },\n }\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport { CONFIG_EXTENSIONS, createModuleLoader } from '@internals/shared'\nimport { isPathInside, isPromise } from '@internals/utils'\nimport type { CLIOptions, Config, PossibleConfig, SerializedDiagnostic } from '@kubb/core'\n\nconst CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`)\n\n/**\n * Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry\n * keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so\n * the agent can act on the problem rather than parsing a bare message. No ANSI styling,\n * unlike the CLI renderer.\n */\nexport function formatDiagnostics(diagnostics: ReadonlyArray<SerializedDiagnostic>): string {\n return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join('\\n\\n')\n}\n\nfunction formatDiagnostic(diagnostic: SerializedDiagnostic): string {\n const { code, message, location, help, plugin, docsUrl } = diagnostic\n const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`]\n\n if (location && 'pointer' in location) {\n lines.push(` at: ${location.pointer}`)\n }\n if (help) {\n lines.push(` fix: ${help}`)\n }\n if (docsUrl) {\n lines.push(` see: ${docsUrl}`)\n }\n\n return lines.join('\\n')\n}\n\ntype NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => Promise<void>\n\nconst loader = createModuleLoader()\n\n// No cache: the MCP server is long-running, so every tool call re-reads the config to pick up edits.\nfunction loadModule(filePath: string): Promise<unknown> {\n return loader.load(filePath, { default: true })\n}\n\n/**\n * Loads the user's Kubb config and returns it with the directory it was found in.\n *\n * When `configPath` is given it must use an allowed extension and resolve inside\n * the current working directory, otherwise loading throws. When omitted, the\n * known `kubb.config.*` file names are tried in the current directory. Every\n * outcome is reported through `notify` before the function returns or throws.\n */\nexport async function loadUserConfig(configPath: string | undefined, { notify }: { notify: NotifyFunction }): Promise<{ userConfig: Config; cwd: string }> {\n if (configPath) {\n const ext = path.extname(configPath)\n if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {\n const msg = `Invalid config file extension \"${ext}\". Allowed: ${CONFIG_EXTENSIONS.join(', ')}`\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const base = path.resolve(process.cwd())\n const resolvedConfigPath = path.resolve(base, configPath)\n if (!isPathInside(resolvedConfigPath, base)) {\n const msg = 'Invalid config file path: must be within the current working directory'\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const cwd = path.dirname(resolvedConfigPath)\n try {\n const userConfig = (await loadModule(resolvedConfigPath)) as Config\n await notify('CONFIG_LOADED', `Loaded config from ${resolvedConfigPath}`)\n return { userConfig, cwd }\n } catch (error) {\n const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n }\n\n const cwd = process.cwd()\n\n for (const configFileName of CONFIG_FILE_NAMES) {\n const configFilePath = path.resolve(process.cwd(), configFileName)\n if (!existsSync(configFilePath)) continue\n try {\n const userConfig = (await loadModule(configFilePath)) as Config\n await notify('CONFIG_LOADED', `Loaded ${configFileName} from current directory`)\n return { userConfig, cwd }\n } catch (err) {\n await notify('CONFIG_ERROR', `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n await notify('CONFIG_ERROR', 'No config file found')\n throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(', ')}`)\n}\n\n/**\n * Inputs forwarded to a config when it is defined as a function.\n */\ntype ResolveUserConfigOptions = {\n /**\n * Path of the loaded config, passed through to the config function as `config`.\n */\n configPath?: string\n /**\n * Log level passed through to the config function.\n */\n logLevel?: CLIOptions['logLevel']\n}\n\n/**\n * Normalizes a possible config into a single resolved `Config`.\n *\n * Calls the config when it is a function, awaits it when it is a promise, and\n * picks the first entry when it resolves to an array.\n */\nexport async function resolveUserConfig(config: PossibleConfig<CLIOptions>, options: ResolveUserConfigOptions): Promise<Config> {\n const result = typeof config === 'function' ? config({ logLevel: options.logLevel, config: options.configPath }) : config\n const resolved = isPromise(result) ? await result : result\n\n return (Array.isArray(resolved) ? resolved[0] : resolved) as Config\n}\n","import path from 'node:path'\nimport { CONFIG_EXTENSIONS } from '@internals/shared'\nimport { type Config, createKubb, type Diagnostic, Diagnostics, type KubbHooks, Hookable } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics, loadUserConfig, resolveUserConfig } from '../utils.ts'\n\nconst generateSchema = z.object({\n config: z.optional(\n z\n .string()\n .check(\n z.minLength(1),\n z.describe(`Path to kubb.config file (supports ${CONFIG_EXTENSIONS.join(', ')}). If not provided, will look for kubb.config.* in current directory`),\n ),\n ),\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI/Swagger spec file (overrides config)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory path (overrides config)'))),\n logLevel: z._default(z.enum(['silent', 'info', 'verbose']).check(z.describe('Log level for build output')), 'info'),\n})\n\nexport const generateTool = defineTool(\n {\n name: 'generate',\n description: 'Generate OpenAPI spec helpers using Kubb configuration',\n schema: generateSchema,\n },\n async function generate(schema: z.input<typeof generateSchema>) {\n const { config: configPath, input, output, logLevel } = schema\n\n try {\n const hooks = new Hookable<KubbHooks>()\n const messages: Array<string> = []\n\n const notify = async (type: string, message: string, data?: Record<string, unknown>) => {\n messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`)\n }\n\n hooks.hook('kubb:info', async ({ message }: { message: string }) => {\n await notify('INFO', message)\n })\n\n hooks.hook('kubb:success', async ({ message }: { message: string }) => {\n await notify('SUCCESS', message)\n })\n\n hooks.hook('kubb:error', async ({ error }: { error: Error }) => {\n await notify('ERROR', error.message)\n })\n\n hooks.hook('kubb:warn', async ({ message }: { message: string }) => {\n await notify('WARN', message)\n })\n\n hooks.hook('kubb:diagnostic', async ({ diagnostic }: { diagnostic: Diagnostic }) => {\n await notify('DIAGNOSTIC', diagnostic.message, Diagnostics.serialize(diagnostic))\n })\n\n hooks.hook('kubb:plugin:start', async ({ plugin }) => {\n await notify('PLUGIN_START', `Plugin starting: ${plugin.name}`)\n })\n\n hooks.hook('kubb:plugin:end', async ({ plugin, duration }) => {\n await notify('PLUGIN_END', `Plugin finished: ${plugin.name}`, { duration })\n })\n\n hooks.hook('kubb:files:processing:start', async () => {\n await notify('FILES_START', 'Starting file processing')\n })\n\n hooks.hook('kubb:files:processing:update', async ({ files }: { files: Array<{ file: { name: string } }> }) => {\n await notify('FILES_UPDATE', `Processing ${files.length} files`)\n })\n\n hooks.hook('kubb:files:processing:end', async () => {\n await notify('FILES_END', 'File processing complete')\n })\n\n hooks.hook('kubb:generation:start', async () => {\n await notify('GENERATION_START', 'Generation started')\n })\n\n hooks.hook('kubb:setup:start', async () => {\n await notify('SETUP_START', 'Setting up Kubb')\n })\n\n hooks.hook('kubb:setup:end', async () => {\n await notify('SETUP_END', 'Kubb setup complete')\n await notify('BUILD_START', 'Starting build')\n })\n\n hooks.hook('kubb:generation:end', async () => {\n await notify('GENERATION_END', 'Generation ended')\n })\n\n let userConfig: Config\n let cwd: string\n\n try {\n const configResult = await loadUserConfig(configPath, { notify })\n userConfig = configResult.userConfig\n cwd = configResult.cwd\n\n if (Array.isArray(userConfig)) {\n throw new Error('Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.')\n }\n\n userConfig = await resolveUserConfig(userConfig, {\n configPath,\n logLevel,\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return tool.error(errorMessage)\n }\n\n const config: Config = {\n ...userConfig,\n root: path.resolve(cwd, userConfig.root ?? '.'),\n input: input ?? userConfig.input,\n output: output\n ? {\n ...userConfig.output,\n path: output,\n }\n : userConfig.output,\n }\n\n await notify('CONFIG_READY', 'Configuration ready')\n\n const kubb = createKubb(config, { hooks })\n const result = await kubb.generate()\n await notify('BUILD_END', `Build complete - Generated ${result.files.length} files`)\n\n const problems = result.diagnostics.filter(Diagnostics.isProblem)\n const errors = problems.filter((diagnostic) => diagnostic.severity === 'error')\n if (errors.length > 0) {\n await notify('BUILD_FAILED', `Build failed with ${errors.length} diagnostic(s)`)\n\n const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic))\n return tool.error(`Build failed:\\n${formatDiagnostics(serialized)}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n\n await notify('BUILD_SUCCESS', `Build completed successfully - Generated ${result.files.length} files`)\n\n return tool.text(`Build completed successfully!\\n\\nGenerated ${result.files.length} files\\n\\n${messages.join('\\n')}`)\n } catch (caughtError) {\n const serialized = Diagnostics.serialize(Diagnostics.from(caughtError))\n return tool.error(`Build error:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { generateConfigFile, KUBB_CONFIG_FILENAME, resolvePlugins } from '@internals/shared'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\n\nconst initSchema = z.object({\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI spec (default: ./openapi.yaml)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory (default: ./src/gen)'))),\n plugins: z.optional(z.string().check(z.minLength(1), z.describe('Comma-separated list of plugins: plugin-ts,plugin-zod,...'))),\n})\n\nexport const initTool = defineTool(\n {\n name: 'init',\n description: 'Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.',\n schema: initSchema,\n },\n async ({ input = './openapi.yaml', output = './src/gen', plugins }) => {\n const selected = resolvePlugins(plugins)\n const content = generateConfigFile({ selectedPlugins: selected, inputPath: input, outputPath: output })\n const dest = path.join(process.cwd(), KUBB_CONFIG_FILENAME)\n if (fs.existsSync(dest)) {\n return tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`)\n }\n fs.writeFileSync(dest, content, 'utf-8')\n const packageList = ['kubb', ...selected.map((p) => p.packageName)].join(' ')\n return tool.text(`Created kubb.config.ts\\n\\nInstall packages:\\n npm install ${packageList}\\n\\nThen run:\\n npx kubb generate`)\n },\n)\n","import { adapterOas } from '@kubb/adapter-oas'\nimport { Diagnostics } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics } from '../utils.ts'\n\nconst validateSchema = z.object({\n input: z.string().check(z.minLength(1), z.describe('Path or URL to the OpenAPI/Swagger specification')),\n})\n\nexport const validateTool = defineTool(\n {\n name: 'validate',\n description: 'Validate an OpenAPI/Swagger specification file or URL',\n schema: validateSchema,\n },\n async ({ input }) => {\n try {\n await adapterOas().validate(input, { throwOnError: true })\n return tool.text(`Validation successful: ${input}`)\n } catch (err) {\n const serialized = Diagnostics.serialize(Diagnostics.from(err))\n return tool.error(`Validation failed:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import { StdioTransport } from '@tmcp/transport-stdio'\nimport { McpServer } from 'tmcp'\nimport { JsonSchemaAdapter } from 'tmcp/adapter'\nimport { en } from 'zod/locales'\nimport * as z from 'zod/mini'\nimport { version } from '../package.json'\nimport { generateTool } from './tools/generate.ts'\nimport { initTool } from './tools/init.ts'\nimport { validateTool } from './tools/validate.ts'\n\n/**\n * Describes each tool's input to MCP clients. `@tmcp/adapter-zod` imports the full `zod` build, so\n * this converts with Zod Mini instead. `io: 'input'` keeps fields with a default optional.\n */\nclass ZodMiniJsonSchemaAdapter extends JsonSchemaAdapter<z.ZodMiniType> {\n async toJsonSchema(schema: z.ZodMiniType) {\n // Zod's type also allows draft-4's boolean `exclusiveMaximum`, which the draft-7 target never emits.\n return z.toJSONSchema(schema, { target: 'draft-7', io: 'input' }) as Awaited<ReturnType<JsonSchemaAdapter<z.ZodMiniType>['toJsonSchema']>>\n }\n}\n\n/**\n * Builds the Kubb MCP server with the generate, validate, and init tools registered.\n *\n * @example\n * `const server = createMcpServer()`\n */\nexport function createMcpServer() {\n // Zod Mini ships without a locale, so validation errors would only say \"Invalid input\".\n z.config(en())\n const server = new McpServer({ name: 'Kubb', version }, { adapter: new ZodMiniJsonSchemaAdapter(), capabilities: { tools: {} } })\n server.tools([generateTool, validateTool, initTool])\n return server\n}\n\n/**\n * Starts the Kubb MCP server over stdio, the transport every local MCP client\n * (Claude, Copilot, editors) uses when it launches the server as a subprocess.\n */\nexport function startServer(): void {\n new StdioTransport(createMcpServer()).listen()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACEA,MAAa,uBAAuB;;AAGpC,MAAa,oBAAoB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;AAU9E,MAAa,mBAAwC;CACnD;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;AACF;;;;;;;;ACtFA,SAAgB,eAAe,aAAsD;CACnF,IAAI,CAAC,aACH,OAAO,CAAC;CAEV,MAAM,YAAY,YACf,MAAM,GAAG,CAAC,CACV,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO;CACjB,OAAO,iBAAiB,QAAQ,WAAW,UAAU,SAAS,OAAO,KAAK,CAAC;AAC7E;AAEA,SAAgB,mBAAmB,EACjC,iBACA,WACA,cAKS;CAKT,OAAO;EAJS,gBAAgB,KAAK,WAAW,YAAY,OAAO,WAAW,WAAW,OAAO,YAAY,EAAE,CAAC,CAAC,KAAK,IAK/G,EAAE;;;YAGE,UAAU;;aAET,WAAW;;;;EARA,gBAAgB,KAAK,WAAW,OAAO,OAAO,WAAW,IAAI,CAAC,CAAC,KAAK,IAY9E,EAAE;;;;AAIhB;;;;;;;ACvCA,MAAM,eAAe;CACnB,KAAK;EAAE,SAAS;EAAa,cAAc;CAAqB;CAChE,aAAa;AACf;;;;;AAMA,MAAM,uBAAuB;;;;;;;;;;;;AA8B7B,SAAgB,qBAAmC;CACjD,MAAMA,UAAAA,GAAOC,KAAAA,WAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAA4B,YAAY;CAErD,OAAO,EACL,MAAM,KAAQ,UAAkB,SAA6B;EAC3D,IAAI,CAAC,qBAAqB,KAAK,QAAQ,GACrC,OAAQ,SAAS,UAAUD,OAAK,OAAO,UAAU,EAAE,SAAS,KAAK,CAAC,IAAIA,OAAK,OAAO,QAAQ;EAM5F,MAAM,MAAO,MAAMA,OAAK,WAAW,OAAA,GAAME,iBAAAA,SAAAA,CAAS,UAAU,MAAM,GAAG;GAAE,UAAU;GAAU,OAAO;GAAM,gBAAgB;EAAK,CAAC;EAG9H,OAAQ,SAAS,UAAW,KAAK,WAAW,MAAO;CACrD,EACF;AACF;;;;;;;;;;;;;;;;;ACmIA,SAAgB,aAAa,MAAc,QAAyB;CAClE,MAAM,gBAAA,GAAeC,UAAAA,QAAAA,CAAQ,IAAI;CACjC,MAAM,kBAAA,GAAiBA,UAAAA,QAAAA,CAAQ,MAAM;CACrC,IAAI,iBAAiB,gBACnB,OAAO;CAGT,MAAM,OAAA,GAAMC,UAAAA,SAAAA,CAAS,gBAAgB,YAAY;CAEjD,OAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,EAAA,GAACC,UAAAA,WAAAA,CAAW,GAAG;AAC/D;;;;;;;;;;;AC1LA,SAAgB,UAAa,QAAkD;CAC7E,OAAO,WAAW,QAAQ,WAAW,KAAA,KAAa,OAAQ,OAAmC,YAAY;AAC3G;;;ACfA,MAAM,oBAAoB,kBAAkB,KAAK,cAAc,cAAc,WAAW;;;;;;;AAQxF,SAAgB,kBAAkB,aAA0D;CAC1F,OAAO,YAAY,KAAK,eAAe,iBAAiB,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;AAClF;AAEA,SAAS,iBAAiB,YAA0C;CAClE,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,QAAQ,YAAY;CAC3D,MAAM,QAAQ,CAAC,SAAS,IAAI,KAAK,IAAI,OAAO,IAAI,YAAY,IAAI,KAAK,KAAK,SAAS;CAEnF,IAAI,YAAY,aAAa,UAC3B,MAAM,KAAK,SAAS,SAAS,SAAS;CAExC,IAAI,MACF,MAAM,KAAK,UAAU,MAAM;CAE7B,IAAI,SACF,MAAM,KAAK,UAAU,SAAS;CAGhC,OAAO,MAAM,KAAK,IAAI;AACxB;AAIA,MAAM,SAAS,mBAAmB;AAGlC,SAAS,WAAW,UAAoC;CACtD,OAAO,OAAO,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAChD;;;;;;;;;AAUA,eAAsB,eAAe,YAAgC,EAAE,UAAoF;CACzJ,IAAI,YAAY;EACd,MAAM,MAAMC,UAAAA,QAAK,QAAQ,UAAU;EACnC,IAAI,CAAC,kBAAkB,MAAM,cAAc,cAAc,GAAG,GAAG;GAC7D,MAAM,MAAM,kCAAkC,IAAI,cAAc,kBAAkB,KAAK,IAAI;GAC3F,MAAM,OAAO,gBAAgB,GAAG;GAChC,MAAM,IAAI,MAAM,GAAG;EACrB;EACA,MAAM,OAAOA,UAAAA,QAAK,QAAQ,QAAQ,IAAI,CAAC;EACvC,MAAM,qBAAqBA,UAAAA,QAAK,QAAQ,MAAM,UAAU;EACxD,IAAI,CAAC,aAAa,oBAAoB,IAAI,GAAG;GAC3C,MAAM,MAAM;GACZ,MAAM,OAAO,gBAAgB,GAAG;GAChC,MAAM,IAAI,MAAM,GAAG;EACrB;EACA,MAAM,MAAMA,UAAAA,QAAK,QAAQ,kBAAkB;EAC3C,IAAI;GACF,MAAM,aAAc,MAAM,WAAW,kBAAkB;GACvD,MAAM,OAAO,iBAAiB,sBAAsB,oBAAoB;GACxE,OAAO;IAAE;IAAY;GAAI;EAC3B,SAAS,OAAO;GACd,MAAM,MAAM,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC3F,MAAM,OAAO,gBAAgB,GAAG;GAChC,MAAM,IAAI,MAAM,GAAG;EACrB;CACF;CAEA,MAAM,MAAM,QAAQ,IAAI;CAExB,KAAK,MAAM,kBAAkB,mBAAmB;EAC9C,MAAM,iBAAiBA,UAAAA,QAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc;EACjE,IAAI,EAAA,GAACC,QAAAA,WAAAA,CAAW,cAAc,GAAG;EACjC,IAAI;GACF,MAAM,aAAc,MAAM,WAAW,cAAc;GACnD,MAAM,OAAO,iBAAiB,UAAU,eAAe,wBAAwB;GAC/E,OAAO;IAAE;IAAY;GAAI;EAC3B,SAAS,KAAK;GACZ,MAAM,OAAO,gBAAgB,kBAAkB,eAAe,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAAG;EACtH;CACF;CAEA,MAAM,OAAO,gBAAgB,sBAAsB;CACnD,MAAM,IAAI,MAAM,wEAAwE,kBAAkB,KAAK,IAAI,GAAG;AACxH;;;;;;;AAsBA,eAAsB,kBAAkB,QAAoC,SAAoD;CAC9H,MAAM,SAAS,OAAO,WAAW,aAAa,OAAO;EAAE,UAAU,QAAQ;EAAU,QAAQ,QAAQ;CAAW,CAAC,IAAI;CACnH,MAAM,WAAW,UAAU,MAAM,IAAI,MAAM,SAAS;CAEpD,OAAQ,MAAM,QAAQ,QAAQ,IAAI,SAAS,KAAK;AAClD;;;AClHA,MAAM,iBAAiBC,SAAE,OAAO;CAC9B,QAAQA,SAAE,SACRA,SACG,OAAO,CAAC,CACR,MACCA,SAAE,UAAU,CAAC,GACbA,SAAE,SAAS,sCAAsC,kBAAkB,KAAK,IAAI,EAAE,qEAAqE,CACrJ,CACJ;CACA,OAAOA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,sDAAsD,CAAC,CAAC;CACtH,QAAQA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,0CAA0C,CAAC,CAAC;CAC3G,UAAUA,SAAE,SAASA,SAAE,KAAK;EAAC;EAAU;EAAQ;CAAS,CAAC,CAAC,CAAC,MAAMA,SAAE,SAAS,4BAA4B,CAAC,GAAG,MAAM;AACpH,CAAC;AAED,MAAa,gBAAA,GAAeC,UAAAA,WAAAA,CAC1B;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,eAAe,SAAS,QAAwC;CAC9D,MAAM,EAAE,QAAQ,YAAY,OAAO,QAAQ,aAAa;CAExD,IAAI;EACF,MAAM,QAAQ,IAAIC,WAAAA,SAAoB;EACtC,MAAM,WAA0B,CAAC;EAEjC,MAAM,SAAS,OAAO,MAAc,SAAiB,SAAmC;GACtF,SAAS,KAAK,OAAO,GAAG,KAAK,IAAI,QAAQ,GAAG,KAAK,UAAU,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS;EAC5F;EAEA,MAAM,KAAK,aAAa,OAAO,EAAE,cAAmC;GAClE,MAAM,OAAO,QAAQ,OAAO;EAC9B,CAAC;EAED,MAAM,KAAK,gBAAgB,OAAO,EAAE,cAAmC;GACrE,MAAM,OAAO,WAAW,OAAO;EACjC,CAAC;EAED,MAAM,KAAK,cAAc,OAAO,EAAE,YAA8B;GAC9D,MAAM,OAAO,SAAS,MAAM,OAAO;EACrC,CAAC;EAED,MAAM,KAAK,aAAa,OAAO,EAAE,cAAmC;GAClE,MAAM,OAAO,QAAQ,OAAO;EAC9B,CAAC;EAED,MAAM,KAAK,mBAAmB,OAAO,EAAE,iBAA6C;GAClF,MAAM,OAAO,cAAc,WAAW,SAASC,WAAAA,YAAY,UAAU,UAAU,CAAC;EAClF,CAAC;EAED,MAAM,KAAK,qBAAqB,OAAO,EAAE,aAAa;GACpD,MAAM,OAAO,gBAAgB,oBAAoB,OAAO,MAAM;EAChE,CAAC;EAED,MAAM,KAAK,mBAAmB,OAAO,EAAE,QAAQ,eAAe;GAC5D,MAAM,OAAO,cAAc,oBAAoB,OAAO,QAAQ,EAAE,SAAS,CAAC;EAC5E,CAAC;EAED,MAAM,KAAK,+BAA+B,YAAY;GACpD,MAAM,OAAO,eAAe,0BAA0B;EACxD,CAAC;EAED,MAAM,KAAK,gCAAgC,OAAO,EAAE,YAA0D;GAC5G,MAAM,OAAO,gBAAgB,cAAc,MAAM,OAAO,OAAO;EACjE,CAAC;EAED,MAAM,KAAK,6BAA6B,YAAY;GAClD,MAAM,OAAO,aAAa,0BAA0B;EACtD,CAAC;EAED,MAAM,KAAK,yBAAyB,YAAY;GAC9C,MAAM,OAAO,oBAAoB,oBAAoB;EACvD,CAAC;EAED,MAAM,KAAK,oBAAoB,YAAY;GACzC,MAAM,OAAO,eAAe,iBAAiB;EAC/C,CAAC;EAED,MAAM,KAAK,kBAAkB,YAAY;GACvC,MAAM,OAAO,aAAa,qBAAqB;GAC/C,MAAM,OAAO,eAAe,gBAAgB;EAC9C,CAAC;EAED,MAAM,KAAK,uBAAuB,YAAY;GAC5C,MAAM,OAAO,kBAAkB,kBAAkB;EACnD,CAAC;EAED,IAAI;EACJ,IAAI;EAEJ,IAAI;GACF,MAAM,eAAe,MAAM,eAAe,YAAY,EAAE,OAAO,CAAC;GAChE,aAAa,aAAa;GAC1B,MAAM,aAAa;GAEnB,IAAI,MAAM,QAAQ,UAAU,GAC1B,MAAM,IAAI,MAAM,2GAA2G;GAG7H,aAAa,MAAM,kBAAkB,YAAY;IAC/C;IACA;GACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC1E,OAAOC,WAAAA,KAAK,MAAM,YAAY;EAChC;EAEA,MAAM,SAAiB;GACrB,GAAG;GACH,MAAMC,UAAAA,QAAK,QAAQ,KAAK,WAAW,QAAQ,GAAG;GAC9C,OAAO,SAAS,WAAW;GAC3B,QAAQ,SACJ;IACE,GAAG,WAAW;IACd,MAAM;GACR,IACA,WAAW;EACjB;EAEA,MAAM,OAAO,gBAAgB,qBAAqB;EAGlD,MAAM,SAAS,OAAA,GADFC,WAAAA,WAAAA,CAAW,QAAQ,EAAE,MAAM,CAChB,CAAC,CAAC,SAAS;EACnC,MAAM,OAAO,aAAa,8BAA8B,OAAO,MAAM,OAAO,OAAO;EAEnF,MAAM,WAAW,OAAO,YAAY,OAAOH,WAAAA,YAAY,SAAS;EAChE,MAAM,SAAS,SAAS,QAAQ,eAAe,WAAW,aAAa,OAAO;EAC9E,IAAI,OAAO,SAAS,GAAG;GACrB,MAAM,OAAO,gBAAgB,qBAAqB,OAAO,OAAO,eAAe;GAE/E,MAAM,aAAa,SAAS,KAAK,eAAeA,WAAAA,YAAY,UAAU,UAAU,CAAC;GACjF,OAAOC,WAAAA,KAAK,MAAM,kBAAkB,kBAAkB,UAAU,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;EACnI;EAEA,MAAM,OAAO,iBAAiB,4CAA4C,OAAO,MAAM,OAAO,OAAO;EAErG,OAAOA,WAAAA,KAAK,KAAK,8CAA8C,OAAO,MAAM,OAAO,YAAY,SAAS,KAAK,IAAI,GAAG;CACtH,SAAS,aAAa;EACpB,MAAM,aAAaD,WAAAA,YAAY,UAAUA,WAAAA,YAAY,KAAK,WAAW,CAAC;EACtE,OAAOC,WAAAA,KAAK,MAAM,iBAAiB,kBAAkB,CAAC,UAAU,CAAC,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;CACpI;AACF,CACF;;;AChJA,MAAM,aAAaG,SAAE,OAAO;CAC1B,OAAOA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,gDAAgD,CAAC,CAAC;CAChH,QAAQA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,uCAAuC,CAAC,CAAC;CACxG,SAASA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,2DAA2D,CAAC,CAAC;AAC/H,CAAC;AAED,MAAa,YAAA,GAAWC,UAAAA,WAAAA,CACtB;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,OAAO,EAAE,QAAQ,kBAAkB,SAAS,aAAa,cAAc;CACrE,MAAM,WAAW,eAAe,OAAO;CACvC,MAAM,UAAU,mBAAmB;EAAE,iBAAiB;EAAU,WAAW;EAAO,YAAY;CAAO,CAAC;CACtG,MAAM,OAAOC,UAAAA,QAAK,KAAKC,aAAAA,QAAQ,IAAI,GAAG,oBAAoB;CAC1D,IAAIC,QAAAA,QAAG,WAAW,IAAI,GACpB,OAAOC,WAAAA,KAAK,MAAM,GAAG,qBAAqB,qBAAqB,KAAK,6CAA6C;CAEnH,QAAA,QAAG,cAAc,MAAM,SAAS,OAAO;CACvC,MAAM,cAAc,CAAC,QAAQ,GAAG,SAAS,KAAK,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG;CAC5E,OAAOA,WAAAA,KAAK,KAAK,8DAA8D,YAAY,mCAAmC;AAChI,CACF;;;ACxBA,MAAM,iBAAiBC,SAAE,OAAO,EAC9B,OAAOA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,kDAAkD,CAAC,EACxG,CAAC;AAED,MAAa,gBAAA,GAAeC,UAAAA,WAAAA,CAC1B;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,OAAO,EAAE,YAAY;CACnB,IAAI;EACF,OAAA,GAAMC,kBAAAA,WAAAA,CAAW,CAAC,CAAC,SAAS,OAAO,EAAE,cAAc,KAAK,CAAC;EACzD,OAAOC,WAAAA,KAAK,KAAK,0BAA0B,OAAO;CACpD,SAAS,KAAK;EACZ,MAAM,aAAaC,WAAAA,YAAY,UAAUA,WAAAA,YAAY,KAAK,GAAG,CAAC;EAC9D,OAAOD,WAAAA,KAAK,MAAM,uBAAuB,kBAAkB,CAAC,UAAU,CAAC,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;CAC1I;AACF,CACF;;;;;;;ACZA,IAAM,2BAAN,cAAuCE,aAAAA,kBAAiC;CACtE,MAAM,aAAa,QAAuB;EAExC,OAAOC,SAAE,aAAa,QAAQ;GAAE,QAAQ;GAAW,IAAI;EAAQ,CAAC;CAClE;AACF;;;;;;;AAQA,SAAgB,kBAAkB;CAEhC,SAAE,QAAA,GAAOC,YAAAA,GAAAA,CAAG,CAAC;CACb,MAAM,SAAS,IAAIC,KAAAA,UAAU;EAAE,MAAM;EAAQ;CAAQ,GAAG;EAAE,SAAS,IAAI,yBAAyB;EAAG,cAAc,EAAE,OAAO,CAAC,EAAE;CAAE,CAAC;CAChI,OAAO,MAAM;EAAC;EAAc;EAAc;CAAQ,CAAC;CACnD,OAAO;AACT;;;;;AAMA,SAAgB,cAAoB;CAClC,IAAIC,sBAAAA,eAAe,gBAAgB,CAAC,CAAC,CAAC,OAAO;AAC/C"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["jiti","createJiti","readFile","resolve","relative","isAbsolute","path","existsSync","z","defineTool","Hookable","Diagnostics","tool","path","createKubb","z","defineTool","path","process","fs","tool","z","defineTool","adapterOas","tool","Diagnostics","JsonSchemaAdapter","z","en","McpServer","StdioTransport"],"sources":["../package.json","../../../internals/shared/src/constants.ts","../../../internals/shared/src/init.ts","../../../internals/shared/src/loader.ts","../../../internals/utils/src/fs.ts","../src/utils.ts","../src/tools/generate.ts","../src/tools/init.ts","../src/tools/validate.ts","../src/server.ts"],"sourcesContent":["","export type PluginOption = {\n value: string\n label: string\n hint?: string\n packageName: string\n importName: string\n category: 'types' | 'client' | 'framework' | 'validation' | 'testing' | 'mocks' | 'documentation' | 'ai'\n}\n\nexport const KUBB_CONFIG_FILENAME = 'kubb.config.ts' as const\n\n/** File extensions a Kubb config file may use. */\nexport const CONFIG_EXTENSIONS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const\n\nexport const KUBB_PACKAGE_NAME = 'kubb' as const\n\nexport const initDefaults = {\n inputPath: './openapi.yaml',\n outputPath: './src/gen',\n plugins: ['plugin-ts'],\n} as const\n\nexport const availablePlugins: Array<PluginOption> = [\n {\n value: 'plugin-ts',\n label: 'TypeScript',\n hint: 'Recommended',\n packageName: '@kubb/plugin-ts',\n importName: 'pluginTs',\n category: 'types',\n },\n {\n value: 'plugin-axios',\n label: 'Axios Client',\n packageName: '@kubb/plugin-axios',\n importName: 'pluginAxios',\n category: 'client',\n },\n {\n value: 'plugin-fetch',\n label: 'Fetch Client',\n packageName: '@kubb/plugin-fetch',\n importName: 'pluginFetch',\n category: 'client',\n },\n {\n value: 'plugin-react-query',\n label: 'React Query / TanStack Query',\n packageName: '@kubb/plugin-react-query',\n importName: 'pluginReactQuery',\n category: 'framework',\n },\n {\n value: 'plugin-vue-query',\n label: 'Vue Query',\n packageName: '@kubb/plugin-vue-query',\n importName: 'pluginVueQuery',\n category: 'framework',\n },\n {\n value: 'plugin-zod',\n label: 'Zod Schemas',\n packageName: '@kubb/plugin-zod',\n importName: 'pluginZod',\n category: 'validation',\n },\n {\n value: 'plugin-faker',\n label: 'Faker.js Mocks',\n packageName: '@kubb/plugin-faker',\n importName: 'pluginFaker',\n category: 'mocks',\n },\n {\n value: 'plugin-msw',\n label: 'MSW Handlers',\n packageName: '@kubb/plugin-msw',\n importName: 'pluginMsw',\n category: 'mocks',\n },\n {\n value: 'plugin-cypress',\n label: 'Cypress Tests',\n packageName: '@kubb/plugin-cypress',\n importName: 'pluginCypress',\n category: 'testing',\n },\n {\n value: 'plugin-mcp',\n label: 'MCP Server (AI / Model Context Protocol)',\n packageName: '@kubb/plugin-mcp',\n importName: 'pluginMcp',\n category: 'ai',\n },\n {\n value: 'plugin-redoc',\n label: 'ReDoc Documentation',\n packageName: '@kubb/plugin-redoc',\n importName: 'pluginRedoc',\n category: 'documentation',\n },\n]\n","import { availablePlugins, type PluginOption } from './constants.ts'\n\n/**\n * Resolves a comma-separated plugin flag (e.g. `--plugins plugin-ts,plugin-zod`) into the\n * matching known plugin options. Unrecognized names are dropped, and a missing flag yields\n * an empty list.\n */\nexport function resolvePlugins(pluginsFlag: string | undefined): Array<PluginOption> {\n if (!pluginsFlag) {\n return []\n }\n const requested = pluginsFlag\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n return availablePlugins.filter((plugin) => requested.includes(plugin.value))\n}\n\nexport function generateConfigFile({\n selectedPlugins,\n inputPath,\n outputPath,\n}: {\n selectedPlugins: Array<PluginOption>\n inputPath: string\n outputPath: string\n}): string {\n const imports = [\n `import { defineConfig } from 'kubb/config'`,\n ...selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`),\n ].join('\\n')\n\n const pluginConfigs = selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join('\\n')\n\n return `${imports}\n\nexport default defineConfig({\n input: '${inputPath}',\n output: {\n path: '${outputPath}',\n clean: true,\n },\n plugins: [\n${pluginConfigs}\n ],\n})\n`\n}\n","import { readFile } from 'node:fs/promises'\nimport { createJiti } from 'jiti'\n\n/**\n * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at\n * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.\n */\nconst JITI_OPTIONS = {\n jsx: { runtime: 'automatic', importSource: '@kubb/renderer-jsx' },\n moduleCache: false,\n} satisfies Parameters<typeof createJiti>[1]\n\n/**\n * Plain JavaScript files. jiti hands these to Node's native `import()`, whose module cache never\n * lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.\n */\nconst JAVASCRIPT_EXTENSION = /\\.[cm]?jsx?$/\n\n/**\n * Per-call options for {@link loadModule}.\n */\ntype LoadModuleOptions = {\n /**\n * Return the module's default export instead of the full namespace.\n */\n default?: boolean\n}\n\nconst jiti = createJiti(import.meta.url, JITI_OPTIONS)\n\n/**\n * Loads a Kubb TypeScript or JavaScript config module via jiti.\n *\n * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load\n * re-evaluates the file, so a config edited on disk is picked up by the next load.\n *\n * @example\n * ```ts\n * const config = await loadModule('/abs/kubb.config.ts', { default: true })\n * ```\n */\nexport async function loadModule<T = unknown>(filePath: string, options?: LoadModuleOptions): Promise<T> {\n if (!JAVASCRIPT_EXTENSION.test(filePath)) {\n return (options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath)) as Promise<T>\n }\n\n // Transpiled through jiti instead, so every load re-evaluates the file as it is on disk now.\n // A TypeScript file it imports reloads too. A plain JavaScript file it imports still goes\n // through Node's `import()` and stays cached until the process restarts.\n const mod = (await jiti.evalModule(await readFile(filePath, 'utf8'), { filename: filePath, async: true, forceTranspile: true })) as\n | { default?: unknown }\n | undefined\n return (options?.default ? (mod?.default ?? mod) : mod) as T\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport { CONFIG_EXTENSIONS, loadModule } from '@internals/shared'\nimport { isPathInside } from '@internals/utils'\nimport type { CLIOptions, PossibleConfig, SerializedDiagnostic } from '@kubb/core'\n\nconst CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`)\n\n/**\n * Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry\n * keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so\n * the agent can act on the problem rather than parsing a bare message. No ANSI styling,\n * unlike the CLI renderer.\n */\nexport function formatDiagnostics(diagnostics: ReadonlyArray<SerializedDiagnostic>): string {\n return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join('\\n\\n')\n}\n\nfunction formatDiagnostic(diagnostic: SerializedDiagnostic): string {\n const { code, message, location, help, plugin, docsUrl } = diagnostic\n const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`]\n\n if (location && 'pointer' in location) {\n lines.push(` at: ${location.pointer}`)\n }\n if (help) {\n lines.push(` fix: ${help}`)\n }\n if (docsUrl) {\n lines.push(` see: ${docsUrl}`)\n }\n\n return lines.join('\\n')\n}\n\ntype NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => void\n\n/**\n * Loads the user's Kubb config and returns it with the directory it was found in.\n *\n * When `configPath` is given it must use an allowed extension and resolve inside\n * the current working directory, otherwise loading throws. When omitted, the\n * known `kubb.config.*` file names are tried in the current directory. Every\n * outcome is reported through `notify` before the function returns or throws.\n */\nexport async function loadUserConfig(\n configPath: string | undefined,\n { notify }: { notify: NotifyFunction },\n): Promise<{ userConfig: PossibleConfig<CLIOptions>; cwd: string }> {\n if (configPath) {\n const ext = path.extname(configPath)\n if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {\n const msg = `Invalid config file extension \"${ext}\". Allowed: ${CONFIG_EXTENSIONS.join(', ')}`\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const base = path.resolve(process.cwd())\n const resolvedConfigPath = path.resolve(base, configPath)\n if (!isPathInside(resolvedConfigPath, base)) {\n const msg = 'Invalid config file path: must be within the current working directory'\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const cwd = path.dirname(resolvedConfigPath)\n try {\n const userConfig = await loadModule<PossibleConfig<CLIOptions>>(resolvedConfigPath, { default: true })\n notify('CONFIG_LOADED', `Loaded config from ${resolvedConfigPath}`)\n return { userConfig, cwd }\n } catch (error) {\n const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n }\n\n const cwd = process.cwd()\n\n for (const configFileName of CONFIG_FILE_NAMES) {\n const configFilePath = path.resolve(process.cwd(), configFileName)\n if (!existsSync(configFilePath)) continue\n try {\n const userConfig = await loadModule<PossibleConfig<CLIOptions>>(configFilePath, { default: true })\n notify('CONFIG_LOADED', `Loaded ${configFileName} from current directory`)\n return { userConfig, cwd }\n } catch (err) {\n notify('CONFIG_ERROR', `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n notify('CONFIG_ERROR', 'No config file found')\n throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(', ')}`)\n}\n","import path from 'node:path'\nimport { CONFIG_EXTENSIONS } from '@internals/shared'\nimport { type Config, createKubb, Diagnostics, type KubbHooks, Hookable } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics, loadUserConfig } from '../utils.ts'\n\nconst generateSchema = z.object({\n config: z.optional(\n z\n .string()\n .check(\n z.minLength(1),\n z.describe(`Path to kubb.config file (supports ${CONFIG_EXTENSIONS.join(', ')}). If not provided, will look for kubb.config.* in current directory`),\n ),\n ),\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI/Swagger spec file (overrides config)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory path (overrides config)'))),\n logLevel: z._default(z.enum(['silent', 'info', 'verbose']).check(z.describe('Log level for build output')), 'info'),\n})\n\nexport const generateTool = defineTool(\n {\n name: 'generate',\n description: 'Generate OpenAPI spec helpers using Kubb configuration',\n schema: generateSchema,\n },\n async function generate(schema: z.input<typeof generateSchema>) {\n const { config: configPath, input, output, logLevel } = schema\n\n try {\n const hooks = new Hookable<KubbHooks>()\n const messages: Array<string> = []\n\n const notify = (type: string, message: string, data?: Record<string, unknown>) => {\n messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`)\n }\n\n hooks.hook('kubb:info', ({ message }) => notify('INFO', message))\n hooks.hook('kubb:success', ({ message }) => notify('SUCCESS', message))\n hooks.hook('kubb:error', ({ error }) => notify('ERROR', error.message))\n hooks.hook('kubb:warn', ({ message }) => notify('WARN', message))\n hooks.hook('kubb:diagnostic', ({ diagnostic }) => notify('DIAGNOSTIC', diagnostic.message, Diagnostics.serialize(diagnostic)))\n hooks.hook('kubb:plugin:start', ({ plugin }) => notify('PLUGIN_START', `Plugin starting: ${plugin.name}`))\n hooks.hook('kubb:plugin:end', ({ plugin, duration }) => notify('PLUGIN_END', `Plugin finished: ${plugin.name}`, { duration }))\n hooks.hook('kubb:files:processing:start', () => notify('FILES_START', 'Starting file processing'))\n hooks.hook('kubb:files:processing:update', ({ files }) => notify('FILES_UPDATE', `Processing ${files.length} files`))\n hooks.hook('kubb:files:processing:end', () => notify('FILES_END', 'File processing complete'))\n hooks.hook('kubb:generation:start', () => notify('GENERATION_START', 'Generation started'))\n hooks.hook('kubb:setup:start', () => notify('SETUP_START', 'Setting up Kubb'))\n hooks.hook('kubb:setup:end', () => {\n notify('SETUP_END', 'Kubb setup complete')\n notify('BUILD_START', 'Starting build')\n })\n hooks.hook('kubb:generation:end', () => notify('GENERATION_END', 'Generation ended'))\n\n let userConfig: Config\n let cwd: string\n\n try {\n const configResult = await loadUserConfig(configPath, { notify })\n const loaded = configResult.userConfig\n cwd = configResult.cwd\n\n const resolved = await (typeof loaded === 'function' ? loaded({ logLevel, config: configPath }) : loaded)\n const first = Array.isArray(resolved) ? resolved[0] : resolved\n if (!first) throw new Error('The config file exports an empty array of configurations')\n userConfig = first\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return tool.error(errorMessage)\n }\n\n const config: Config = {\n ...userConfig,\n root: path.resolve(cwd, userConfig.root ?? '.'),\n input: input ?? userConfig.input,\n output: output\n ? {\n ...userConfig.output,\n path: output,\n }\n : userConfig.output,\n }\n\n notify('CONFIG_READY', 'Configuration ready')\n\n const kubb = createKubb(config, { hooks })\n const result = await kubb.generate()\n notify('BUILD_END', `Build complete - Generated ${result.files.length} files`)\n\n const problems = result.diagnostics.filter(Diagnostics.isProblem)\n const errors = problems.filter((diagnostic) => diagnostic.severity === 'error')\n if (errors.length > 0) {\n notify('BUILD_FAILED', `Build failed with ${errors.length} diagnostic(s)`)\n\n const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic))\n return tool.error(`Build failed:\\n${formatDiagnostics(serialized)}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n\n notify('BUILD_SUCCESS', `Build completed successfully - Generated ${result.files.length} files`)\n\n return tool.text(`Build completed successfully!\\n\\nGenerated ${result.files.length} files\\n\\n${messages.join('\\n')}`)\n } catch (caughtError) {\n const serialized = Diagnostics.serialize(Diagnostics.from(caughtError))\n return tool.error(`Build error:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { generateConfigFile, KUBB_CONFIG_FILENAME, resolvePlugins } from '@internals/shared'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\n\nconst initSchema = z.object({\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI spec (default: ./openapi.yaml)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory (default: ./src/gen)'))),\n plugins: z.optional(z.string().check(z.minLength(1), z.describe('Comma-separated list of plugins: plugin-ts,plugin-zod,...'))),\n})\n\nexport const initTool = defineTool(\n {\n name: 'init',\n description: 'Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.',\n schema: initSchema,\n },\n async ({ input = './openapi.yaml', output = './src/gen', plugins }) => {\n const selected = resolvePlugins(plugins)\n const content = generateConfigFile({ selectedPlugins: selected, inputPath: input, outputPath: output })\n const dest = path.join(process.cwd(), KUBB_CONFIG_FILENAME)\n if (fs.existsSync(dest)) {\n return tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`)\n }\n fs.writeFileSync(dest, content, 'utf-8')\n const packageList = ['kubb', ...selected.map((p) => p.packageName)].join(' ')\n return tool.text(`Created kubb.config.ts\\n\\nInstall packages:\\n npm install ${packageList}\\n\\nThen run:\\n npx kubb generate`)\n },\n)\n","import { adapterOas } from '@kubb/adapter-oas'\nimport { Diagnostics } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics } from '../utils.ts'\n\nconst validateSchema = z.object({\n input: z.string().check(z.minLength(1), z.describe('Path or URL to the OpenAPI/Swagger specification')),\n})\n\nexport const validateTool = defineTool(\n {\n name: 'validate',\n description: 'Validate an OpenAPI/Swagger specification file or URL',\n schema: validateSchema,\n },\n async ({ input }) => {\n try {\n await adapterOas().validate(input, { throwOnError: true })\n return tool.text(`Validation successful: ${input}`)\n } catch (err) {\n const serialized = Diagnostics.serialize(Diagnostics.from(err))\n return tool.error(`Validation failed:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import { StdioTransport } from '@tmcp/transport-stdio'\nimport { McpServer } from 'tmcp'\nimport { JsonSchemaAdapter } from 'tmcp/adapter'\nimport { en } from 'zod/locales'\nimport * as z from 'zod/mini'\nimport { version } from '../package.json'\nimport { generateTool } from './tools/generate.ts'\nimport { initTool } from './tools/init.ts'\nimport { validateTool } from './tools/validate.ts'\n\n/**\n * Describes each tool's input to MCP clients. `@tmcp/adapter-zod` imports the full `zod` build, so\n * this converts with Zod Mini instead. `io: 'input'` keeps fields with a default optional.\n */\nclass ZodMiniJsonSchemaAdapter extends JsonSchemaAdapter<z.ZodMiniType> {\n async toJsonSchema(schema: z.ZodMiniType) {\n // Zod's type also allows draft-4's boolean `exclusiveMaximum`, which the draft-7 target never emits.\n return z.toJSONSchema(schema, { target: 'draft-7', io: 'input' }) as Awaited<ReturnType<JsonSchemaAdapter<z.ZodMiniType>['toJsonSchema']>>\n }\n}\n\n/**\n * Builds the Kubb MCP server with the generate, validate, and init tools registered.\n *\n * @example\n * `const server = createMcpServer()`\n */\nexport function createMcpServer() {\n // Zod Mini ships without a locale, so validation errors would only say \"Invalid input\".\n z.config(en())\n const server = new McpServer({ name: 'Kubb', version }, { adapter: new ZodMiniJsonSchemaAdapter(), capabilities: { tools: {} } })\n server.tools([generateTool, validateTool, initTool])\n return server\n}\n\n/**\n * Starts the Kubb MCP server over stdio, the transport every local MCP client\n * (Claude, Copilot, editors) uses when it launches the server as a subprocess.\n */\nexport function startServer(): void {\n new StdioTransport(createMcpServer()).listen()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACSA,MAAa,uBAAuB;;AAGpC,MAAa,oBAAoB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;AAU9E,MAAa,mBAAwC;CACnD;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;CACA;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,YAAY;EACZ,UAAU;CACZ;AACF;;;;;;;;AC9FA,SAAgB,eAAe,aAAsD;CACnF,IAAI,CAAC,aACH,OAAO,CAAC;CAEV,MAAM,YAAY,YACf,MAAM,GAAG,CAAC,CACV,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,OAAO,OAAO;CACjB,OAAO,iBAAiB,QAAQ,WAAW,UAAU,SAAS,OAAO,KAAK,CAAC;AAC7E;AAEA,SAAgB,mBAAmB,EACjC,iBACA,WACA,cAKS;CAQT,OAAO,GAPS,CACd,8CACA,GAAG,gBAAgB,KAAK,WAAW,YAAY,OAAO,WAAW,WAAW,OAAO,YAAY,EAAE,CACnG,CAAC,CAAC,KAAK,IAIS,EAAE;;;YAGR,UAAU;;aAET,WAAW;;;;EAPA,gBAAgB,KAAK,WAAW,OAAO,OAAO,WAAW,IAAI,CAAC,CAAC,KAAK,IAW9E,EAAE;;;;AAIhB;;;;;;;ACxCA,MAAM,eAAe;CACnB,KAAK;EAAE,SAAS;EAAa,cAAc;CAAqB;CAChE,aAAa;AACf;;;;;AAMA,MAAM,uBAAuB;AAY7B,MAAMA,UAAAA,GAAOC,KAAAA,WAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAA4B,YAAY;;;;;;;;;;;;AAarD,eAAsB,WAAwB,UAAkB,SAAyC;CACvG,IAAI,CAAC,qBAAqB,KAAK,QAAQ,GACrC,OAAQ,SAAS,UAAUD,OAAK,OAAO,UAAU,EAAE,SAAS,KAAK,CAAC,IAAIA,OAAK,OAAO,QAAQ;CAM5F,MAAM,MAAO,MAAMA,OAAK,WAAW,OAAA,GAAME,iBAAAA,SAAAA,CAAS,UAAU,MAAM,GAAG;EAAE,UAAU;EAAU,OAAO;EAAM,gBAAgB;CAAK,CAAC;CAG9H,OAAQ,SAAS,UAAW,KAAK,WAAW,MAAO;AACrD;;;;;;;;;;;;;;;;;AC8IA,SAAgB,aAAa,MAAc,QAAyB;CAClE,MAAM,gBAAA,GAAeC,UAAAA,QAAAA,CAAQ,IAAI;CACjC,MAAM,kBAAA,GAAiBA,UAAAA,QAAAA,CAAQ,MAAM;CACrC,IAAI,iBAAiB,gBACnB,OAAO;CAGT,MAAM,OAAA,GAAMC,UAAAA,SAAAA,CAAS,gBAAgB,YAAY;CAEjD,OAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,EAAA,GAACC,UAAAA,WAAAA,CAAW,GAAG;AAC/D;;;ACvMA,MAAM,oBAAoB,kBAAkB,KAAK,cAAc,cAAc,WAAW;;;;;;;AAQxF,SAAgB,kBAAkB,aAA0D;CAC1F,OAAO,YAAY,KAAK,eAAe,iBAAiB,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;AAClF;AAEA,SAAS,iBAAiB,YAA0C;CAClE,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,QAAQ,YAAY;CAC3D,MAAM,QAAQ,CAAC,SAAS,IAAI,KAAK,IAAI,OAAO,IAAI,YAAY,IAAI,KAAK,KAAK,SAAS;CAEnF,IAAI,YAAY,aAAa,UAC3B,MAAM,KAAK,SAAS,SAAS,SAAS;CAExC,IAAI,MACF,MAAM,KAAK,UAAU,MAAM;CAE7B,IAAI,SACF,MAAM,KAAK,UAAU,SAAS;CAGhC,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;;;;AAYA,eAAsB,eACpB,YACA,EAAE,UACgE;CAClE,IAAI,YAAY;EACd,MAAM,MAAMC,UAAAA,QAAK,QAAQ,UAAU;EACnC,IAAI,CAAC,kBAAkB,MAAM,cAAc,cAAc,GAAG,GAAG;GAC7D,MAAM,MAAM,kCAAkC,IAAI,cAAc,kBAAkB,KAAK,IAAI;GAC3F,OAAO,gBAAgB,GAAG;GAC1B,MAAM,IAAI,MAAM,GAAG;EACrB;EACA,MAAM,OAAOA,UAAAA,QAAK,QAAQ,QAAQ,IAAI,CAAC;EACvC,MAAM,qBAAqBA,UAAAA,QAAK,QAAQ,MAAM,UAAU;EACxD,IAAI,CAAC,aAAa,oBAAoB,IAAI,GAAG;GAC3C,MAAM,MAAM;GACZ,OAAO,gBAAgB,GAAG;GAC1B,MAAM,IAAI,MAAM,GAAG;EACrB;EACA,MAAM,MAAMA,UAAAA,QAAK,QAAQ,kBAAkB;EAC3C,IAAI;GACF,MAAM,aAAa,MAAM,WAAuC,oBAAoB,EAAE,SAAS,KAAK,CAAC;GACrG,OAAO,iBAAiB,sBAAsB,oBAAoB;GAClE,OAAO;IAAE;IAAY;GAAI;EAC3B,SAAS,OAAO;GACd,MAAM,MAAM,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC3F,OAAO,gBAAgB,GAAG;GAC1B,MAAM,IAAI,MAAM,GAAG;EACrB;CACF;CAEA,MAAM,MAAM,QAAQ,IAAI;CAExB,KAAK,MAAM,kBAAkB,mBAAmB;EAC9C,MAAM,iBAAiBA,UAAAA,QAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc;EACjE,IAAI,EAAA,GAACC,QAAAA,WAAAA,CAAW,cAAc,GAAG;EACjC,IAAI;GACF,MAAM,aAAa,MAAM,WAAuC,gBAAgB,EAAE,SAAS,KAAK,CAAC;GACjG,OAAO,iBAAiB,UAAU,eAAe,wBAAwB;GACzE,OAAO;IAAE;IAAY;GAAI;EAC3B,SAAS,KAAK;GACZ,OAAO,gBAAgB,kBAAkB,eAAe,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAAG;EAChH;CACF;CAEA,OAAO,gBAAgB,sBAAsB;CAC7C,MAAM,IAAI,MAAM,wEAAwE,kBAAkB,KAAK,IAAI,GAAG;AACxH;;;ACnFA,MAAM,iBAAiBC,SAAE,OAAO;CAC9B,QAAQA,SAAE,SACRA,SACG,OAAO,CAAC,CACR,MACCA,SAAE,UAAU,CAAC,GACbA,SAAE,SAAS,sCAAsC,kBAAkB,KAAK,IAAI,EAAE,qEAAqE,CACrJ,CACJ;CACA,OAAOA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,sDAAsD,CAAC,CAAC;CACtH,QAAQA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,0CAA0C,CAAC,CAAC;CAC3G,UAAUA,SAAE,SAASA,SAAE,KAAK;EAAC;EAAU;EAAQ;CAAS,CAAC,CAAC,CAAC,MAAMA,SAAE,SAAS,4BAA4B,CAAC,GAAG,MAAM;AACpH,CAAC;AAED,MAAa,gBAAA,GAAeC,UAAAA,WAAAA,CAC1B;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,eAAe,SAAS,QAAwC;CAC9D,MAAM,EAAE,QAAQ,YAAY,OAAO,QAAQ,aAAa;CAExD,IAAI;EACF,MAAM,QAAQ,IAAIC,WAAAA,SAAoB;EACtC,MAAM,WAA0B,CAAC;EAEjC,MAAM,UAAU,MAAc,SAAiB,SAAmC;GAChF,SAAS,KAAK,OAAO,GAAG,KAAK,IAAI,QAAQ,GAAG,KAAK,UAAU,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS;EAC5F;EAEA,MAAM,KAAK,cAAc,EAAE,cAAc,OAAO,QAAQ,OAAO,CAAC;EAChE,MAAM,KAAK,iBAAiB,EAAE,cAAc,OAAO,WAAW,OAAO,CAAC;EACtE,MAAM,KAAK,eAAe,EAAE,YAAY,OAAO,SAAS,MAAM,OAAO,CAAC;EACtE,MAAM,KAAK,cAAc,EAAE,cAAc,OAAO,QAAQ,OAAO,CAAC;EAChE,MAAM,KAAK,oBAAoB,EAAE,iBAAiB,OAAO,cAAc,WAAW,SAASC,WAAAA,YAAY,UAAU,UAAU,CAAC,CAAC;EAC7H,MAAM,KAAK,sBAAsB,EAAE,aAAa,OAAO,gBAAgB,oBAAoB,OAAO,MAAM,CAAC;EACzG,MAAM,KAAK,oBAAoB,EAAE,QAAQ,eAAe,OAAO,cAAc,oBAAoB,OAAO,QAAQ,EAAE,SAAS,CAAC,CAAC;EAC7H,MAAM,KAAK,qCAAqC,OAAO,eAAe,0BAA0B,CAAC;EACjG,MAAM,KAAK,iCAAiC,EAAE,YAAY,OAAO,gBAAgB,cAAc,MAAM,OAAO,OAAO,CAAC;EACpH,MAAM,KAAK,mCAAmC,OAAO,aAAa,0BAA0B,CAAC;EAC7F,MAAM,KAAK,+BAA+B,OAAO,oBAAoB,oBAAoB,CAAC;EAC1F,MAAM,KAAK,0BAA0B,OAAO,eAAe,iBAAiB,CAAC;EAC7E,MAAM,KAAK,wBAAwB;GACjC,OAAO,aAAa,qBAAqB;GACzC,OAAO,eAAe,gBAAgB;EACxC,CAAC;EACD,MAAM,KAAK,6BAA6B,OAAO,kBAAkB,kBAAkB,CAAC;EAEpF,IAAI;EACJ,IAAI;EAEJ,IAAI;GACF,MAAM,eAAe,MAAM,eAAe,YAAY,EAAE,OAAO,CAAC;GAChE,MAAM,SAAS,aAAa;GAC5B,MAAM,aAAa;GAEnB,MAAM,WAAW,OAAO,OAAO,WAAW,aAAa,OAAO;IAAE;IAAU,QAAQ;GAAW,CAAC,IAAI;GAClG,MAAM,QAAQ,MAAM,QAAQ,QAAQ,IAAI,SAAS,KAAK;GACtD,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,0DAA0D;GACtF,aAAa;EACf,SAAS,OAAO;GACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC1E,OAAOC,WAAAA,KAAK,MAAM,YAAY;EAChC;EAEA,MAAM,SAAiB;GACrB,GAAG;GACH,MAAMC,UAAAA,QAAK,QAAQ,KAAK,WAAW,QAAQ,GAAG;GAC9C,OAAO,SAAS,WAAW;GAC3B,QAAQ,SACJ;IACE,GAAG,WAAW;IACd,MAAM;GACR,IACA,WAAW;EACjB;EAEA,OAAO,gBAAgB,qBAAqB;EAG5C,MAAM,SAAS,OAAA,GADFC,WAAAA,WAAAA,CAAW,QAAQ,EAAE,MAAM,CAChB,CAAC,CAAC,SAAS;EACnC,OAAO,aAAa,8BAA8B,OAAO,MAAM,OAAO,OAAO;EAE7E,MAAM,WAAW,OAAO,YAAY,OAAOH,WAAAA,YAAY,SAAS;EAChE,MAAM,SAAS,SAAS,QAAQ,eAAe,WAAW,aAAa,OAAO;EAC9E,IAAI,OAAO,SAAS,GAAG;GACrB,OAAO,gBAAgB,qBAAqB,OAAO,OAAO,eAAe;GAEzE,MAAM,aAAa,SAAS,KAAK,eAAeA,WAAAA,YAAY,UAAU,UAAU,CAAC;GACjF,OAAOC,WAAAA,KAAK,MAAM,kBAAkB,kBAAkB,UAAU,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;EACnI;EAEA,OAAO,iBAAiB,4CAA4C,OAAO,MAAM,OAAO,OAAO;EAE/F,OAAOA,WAAAA,KAAK,KAAK,8CAA8C,OAAO,MAAM,OAAO,YAAY,SAAS,KAAK,IAAI,GAAG;CACtH,SAAS,aAAa;EACpB,MAAM,aAAaD,WAAAA,YAAY,UAAUA,WAAAA,YAAY,KAAK,WAAW,CAAC;EACtE,OAAOC,WAAAA,KAAK,MAAM,iBAAiB,kBAAkB,CAAC,UAAU,CAAC,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;CACpI;AACF,CACF;;;ACrGA,MAAM,aAAaG,SAAE,OAAO;CAC1B,OAAOA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,gDAAgD,CAAC,CAAC;CAChH,QAAQA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,uCAAuC,CAAC,CAAC;CACxG,SAASA,SAAE,SAASA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,2DAA2D,CAAC,CAAC;AAC/H,CAAC;AAED,MAAa,YAAA,GAAWC,UAAAA,WAAAA,CACtB;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,OAAO,EAAE,QAAQ,kBAAkB,SAAS,aAAa,cAAc;CACrE,MAAM,WAAW,eAAe,OAAO;CACvC,MAAM,UAAU,mBAAmB;EAAE,iBAAiB;EAAU,WAAW;EAAO,YAAY;CAAO,CAAC;CACtG,MAAM,OAAOC,UAAAA,QAAK,KAAKC,aAAAA,QAAQ,IAAI,GAAG,oBAAoB;CAC1D,IAAIC,QAAAA,QAAG,WAAW,IAAI,GACpB,OAAOC,WAAAA,KAAK,MAAM,GAAG,qBAAqB,qBAAqB,KAAK,6CAA6C;CAEnH,QAAA,QAAG,cAAc,MAAM,SAAS,OAAO;CACvC,MAAM,cAAc,CAAC,QAAQ,GAAG,SAAS,KAAK,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG;CAC5E,OAAOA,WAAAA,KAAK,KAAK,8DAA8D,YAAY,mCAAmC;AAChI,CACF;;;ACxBA,MAAM,iBAAiBC,SAAE,OAAO,EAC9B,OAAOA,SAAE,OAAO,CAAC,CAAC,MAAMA,SAAE,UAAU,CAAC,GAAGA,SAAE,SAAS,kDAAkD,CAAC,EACxG,CAAC;AAED,MAAa,gBAAA,GAAeC,UAAAA,WAAAA,CAC1B;CACE,MAAM;CACN,aAAa;CACb,QAAQ;AACV,GACA,OAAO,EAAE,YAAY;CACnB,IAAI;EACF,OAAA,GAAMC,kBAAAA,WAAAA,CAAW,CAAC,CAAC,SAAS,OAAO,EAAE,cAAc,KAAK,CAAC;EACzD,OAAOC,WAAAA,KAAK,KAAK,0BAA0B,OAAO;CACpD,SAAS,KAAK;EACZ,MAAM,aAAaC,WAAAA,YAAY,UAAUA,WAAAA,YAAY,KAAK,GAAG,CAAC;EAC9D,OAAOD,WAAAA,KAAK,MAAM,uBAAuB,kBAAkB,CAAC,UAAU,CAAC,EAAE,kBAAkB,KAAK,UAAU,YAAY,MAAM,CAAC,EAAE,SAAS;CAC1I;AACF,CACF;;;;;;;ACZA,IAAM,2BAAN,cAAuCE,aAAAA,kBAAiC;CACtE,MAAM,aAAa,QAAuB;EAExC,OAAOC,SAAE,aAAa,QAAQ;GAAE,QAAQ;GAAW,IAAI;EAAQ,CAAC;CAClE;AACF;;;;;;;AAQA,SAAgB,kBAAkB;CAEhC,SAAE,QAAA,GAAOC,YAAAA,GAAAA,CAAG,CAAC;CACb,MAAM,SAAS,IAAIC,KAAAA,UAAU;EAAE,MAAM;EAAQ;CAAQ,GAAG;EAAE,SAAS,IAAI,yBAAyB;EAAG,cAAc,EAAE,OAAO,CAAC,EAAE;CAAE,CAAC;CAChI,OAAO,MAAM;EAAC;EAAc;EAAc;CAAQ,CAAC;CACnD,OAAO;AACT;;;;;AAMA,SAAgB,cAAoB;CAClC,IAAIC,sBAAAA,eAAe,gBAAgB,CAAC,CAAC,CAAC,OAAO;AAC/C"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import fs, { existsSync } from "node:fs";
|
|
|
14
14
|
import process$1 from "node:process";
|
|
15
15
|
import { adapterOas } from "@kubb/adapter-oas";
|
|
16
16
|
//#region package.json
|
|
17
|
-
var version = "5.
|
|
17
|
+
var version = "5.9.1";
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region ../../internals/shared/src/constants.ts
|
|
20
20
|
const KUBB_CONFIG_FILENAME = "kubb.config.ts";
|
|
@@ -120,8 +120,7 @@ function resolvePlugins(pluginsFlag) {
|
|
|
120
120
|
return availablePlugins.filter((plugin) => requested.includes(plugin.value));
|
|
121
121
|
}
|
|
122
122
|
function generateConfigFile({ selectedPlugins, inputPath, outputPath }) {
|
|
123
|
-
return `import { defineConfig } from 'kubb/config'
|
|
124
|
-
${selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join("\n")}
|
|
123
|
+
return `${[`import { defineConfig } from 'kubb/config'`, ...selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`)].join("\n")}
|
|
125
124
|
|
|
126
125
|
export default defineConfig({
|
|
127
126
|
input: '${inputPath}',
|
|
@@ -153,28 +152,26 @@ const JITI_OPTIONS = {
|
|
|
153
152
|
* lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.
|
|
154
153
|
*/
|
|
155
154
|
const JAVASCRIPT_EXTENSION = /\.[cm]?jsx?$/;
|
|
155
|
+
const jiti = createJiti(import.meta.url, JITI_OPTIONS);
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
157
|
+
* Loads a Kubb TypeScript or JavaScript config module via jiti.
|
|
158
158
|
*
|
|
159
159
|
* jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load
|
|
160
160
|
* re-evaluates the file, so a config edited on disk is picked up by the next load.
|
|
161
161
|
*
|
|
162
162
|
* @example
|
|
163
163
|
* ```ts
|
|
164
|
-
* const config = await
|
|
164
|
+
* const config = await loadModule('/abs/kubb.config.ts', { default: true })
|
|
165
165
|
* ```
|
|
166
166
|
*/
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
});
|
|
176
|
-
return options?.default ? mod?.default ?? mod : mod;
|
|
177
|
-
} };
|
|
167
|
+
async function loadModule(filePath, options) {
|
|
168
|
+
if (!JAVASCRIPT_EXTENSION.test(filePath)) return options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath);
|
|
169
|
+
const mod = await jiti.evalModule(await readFile(filePath, "utf8"), {
|
|
170
|
+
filename: filePath,
|
|
171
|
+
async: true,
|
|
172
|
+
forceTranspile: true
|
|
173
|
+
});
|
|
174
|
+
return options?.default ? mod?.default ?? mod : mod;
|
|
178
175
|
}
|
|
179
176
|
//#endregion
|
|
180
177
|
//#region ../../internals/utils/src/fs.ts
|
|
@@ -200,19 +197,6 @@ function isPathInside(path, parent) {
|
|
|
200
197
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
201
198
|
}
|
|
202
199
|
//#endregion
|
|
203
|
-
//#region ../../internals/utils/src/promise.ts
|
|
204
|
-
/** Returns `true` when `result` is a thenable `Promise`.
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```ts
|
|
208
|
-
* isPromise(Promise.resolve(1)) // true
|
|
209
|
-
* isPromise(42) // false
|
|
210
|
-
* ```
|
|
211
|
-
*/
|
|
212
|
-
function isPromise(result) {
|
|
213
|
-
return result !== null && result !== void 0 && typeof result["then"] === "function";
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
200
|
//#region src/utils.ts
|
|
217
201
|
const CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`);
|
|
218
202
|
/**
|
|
@@ -232,10 +216,6 @@ function formatDiagnostic(diagnostic) {
|
|
|
232
216
|
if (docsUrl) lines.push(` see: ${docsUrl}`);
|
|
233
217
|
return lines.join("\n");
|
|
234
218
|
}
|
|
235
|
-
const loader = createModuleLoader();
|
|
236
|
-
function loadModule(filePath) {
|
|
237
|
-
return loader.load(filePath, { default: true });
|
|
238
|
-
}
|
|
239
219
|
/**
|
|
240
220
|
* Loads the user's Kubb config and returns it with the directory it was found in.
|
|
241
221
|
*
|
|
@@ -249,27 +229,27 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
249
229
|
const ext = path.extname(configPath);
|
|
250
230
|
if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {
|
|
251
231
|
const msg = `Invalid config file extension "${ext}". Allowed: ${CONFIG_EXTENSIONS.join(", ")}`;
|
|
252
|
-
|
|
232
|
+
notify("CONFIG_ERROR", msg);
|
|
253
233
|
throw new Error(msg);
|
|
254
234
|
}
|
|
255
235
|
const base = path.resolve(process.cwd());
|
|
256
236
|
const resolvedConfigPath = path.resolve(base, configPath);
|
|
257
237
|
if (!isPathInside(resolvedConfigPath, base)) {
|
|
258
238
|
const msg = "Invalid config file path: must be within the current working directory";
|
|
259
|
-
|
|
239
|
+
notify("CONFIG_ERROR", msg);
|
|
260
240
|
throw new Error(msg);
|
|
261
241
|
}
|
|
262
242
|
const cwd = path.dirname(resolvedConfigPath);
|
|
263
243
|
try {
|
|
264
|
-
const userConfig = await loadModule(resolvedConfigPath);
|
|
265
|
-
|
|
244
|
+
const userConfig = await loadModule(resolvedConfigPath, { default: true });
|
|
245
|
+
notify("CONFIG_LOADED", `Loaded config from ${resolvedConfigPath}`);
|
|
266
246
|
return {
|
|
267
247
|
userConfig,
|
|
268
248
|
cwd
|
|
269
249
|
};
|
|
270
250
|
} catch (error) {
|
|
271
251
|
const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`;
|
|
272
|
-
|
|
252
|
+
notify("CONFIG_ERROR", msg);
|
|
273
253
|
throw new Error(msg);
|
|
274
254
|
}
|
|
275
255
|
}
|
|
@@ -278,33 +258,19 @@ async function loadUserConfig(configPath, { notify }) {
|
|
|
278
258
|
const configFilePath = path.resolve(process.cwd(), configFileName);
|
|
279
259
|
if (!existsSync(configFilePath)) continue;
|
|
280
260
|
try {
|
|
281
|
-
const userConfig = await loadModule(configFilePath);
|
|
282
|
-
|
|
261
|
+
const userConfig = await loadModule(configFilePath, { default: true });
|
|
262
|
+
notify("CONFIG_LOADED", `Loaded ${configFileName} from current directory`);
|
|
283
263
|
return {
|
|
284
264
|
userConfig,
|
|
285
265
|
cwd
|
|
286
266
|
};
|
|
287
267
|
} catch (err) {
|
|
288
|
-
|
|
268
|
+
notify("CONFIG_ERROR", `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
289
269
|
}
|
|
290
270
|
}
|
|
291
|
-
|
|
271
|
+
notify("CONFIG_ERROR", "No config file found");
|
|
292
272
|
throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(", ")}`);
|
|
293
273
|
}
|
|
294
|
-
/**
|
|
295
|
-
* Normalizes a possible config into a single resolved `Config`.
|
|
296
|
-
*
|
|
297
|
-
* Calls the config when it is a function, awaits it when it is a promise, and
|
|
298
|
-
* picks the first entry when it resolves to an array.
|
|
299
|
-
*/
|
|
300
|
-
async function resolveUserConfig(config, options) {
|
|
301
|
-
const result = typeof config === "function" ? config({
|
|
302
|
-
logLevel: options.logLevel,
|
|
303
|
-
config: options.configPath
|
|
304
|
-
}) : config;
|
|
305
|
-
const resolved = isPromise(result) ? await result : result;
|
|
306
|
-
return Array.isArray(resolved) ? resolved[0] : resolved;
|
|
307
|
-
}
|
|
308
274
|
//#endregion
|
|
309
275
|
//#region src/tools/generate.ts
|
|
310
276
|
const generateSchema = z.object({
|
|
@@ -326,63 +292,39 @@ const generateTool = defineTool({
|
|
|
326
292
|
try {
|
|
327
293
|
const hooks = new Hookable();
|
|
328
294
|
const messages = [];
|
|
329
|
-
const notify =
|
|
295
|
+
const notify = (type, message, data) => {
|
|
330
296
|
messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`);
|
|
331
297
|
};
|
|
332
|
-
hooks.hook("kubb:info",
|
|
333
|
-
|
|
334
|
-
});
|
|
335
|
-
hooks.hook("kubb:
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
hooks.hook("kubb:
|
|
339
|
-
|
|
340
|
-
});
|
|
341
|
-
hooks.hook("kubb:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
hooks.hook("kubb:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
hooks.hook("kubb:plugin:start", async ({ plugin }) => {
|
|
348
|
-
await notify("PLUGIN_START", `Plugin starting: ${plugin.name}`);
|
|
349
|
-
});
|
|
350
|
-
hooks.hook("kubb:plugin:end", async ({ plugin, duration }) => {
|
|
351
|
-
await notify("PLUGIN_END", `Plugin finished: ${plugin.name}`, { duration });
|
|
352
|
-
});
|
|
353
|
-
hooks.hook("kubb:files:processing:start", async () => {
|
|
354
|
-
await notify("FILES_START", "Starting file processing");
|
|
355
|
-
});
|
|
356
|
-
hooks.hook("kubb:files:processing:update", async ({ files }) => {
|
|
357
|
-
await notify("FILES_UPDATE", `Processing ${files.length} files`);
|
|
358
|
-
});
|
|
359
|
-
hooks.hook("kubb:files:processing:end", async () => {
|
|
360
|
-
await notify("FILES_END", "File processing complete");
|
|
361
|
-
});
|
|
362
|
-
hooks.hook("kubb:generation:start", async () => {
|
|
363
|
-
await notify("GENERATION_START", "Generation started");
|
|
364
|
-
});
|
|
365
|
-
hooks.hook("kubb:setup:start", async () => {
|
|
366
|
-
await notify("SETUP_START", "Setting up Kubb");
|
|
367
|
-
});
|
|
368
|
-
hooks.hook("kubb:setup:end", async () => {
|
|
369
|
-
await notify("SETUP_END", "Kubb setup complete");
|
|
370
|
-
await notify("BUILD_START", "Starting build");
|
|
371
|
-
});
|
|
372
|
-
hooks.hook("kubb:generation:end", async () => {
|
|
373
|
-
await notify("GENERATION_END", "Generation ended");
|
|
298
|
+
hooks.hook("kubb:info", ({ message }) => notify("INFO", message));
|
|
299
|
+
hooks.hook("kubb:success", ({ message }) => notify("SUCCESS", message));
|
|
300
|
+
hooks.hook("kubb:error", ({ error }) => notify("ERROR", error.message));
|
|
301
|
+
hooks.hook("kubb:warn", ({ message }) => notify("WARN", message));
|
|
302
|
+
hooks.hook("kubb:diagnostic", ({ diagnostic }) => notify("DIAGNOSTIC", diagnostic.message, Diagnostics.serialize(diagnostic)));
|
|
303
|
+
hooks.hook("kubb:plugin:start", ({ plugin }) => notify("PLUGIN_START", `Plugin starting: ${plugin.name}`));
|
|
304
|
+
hooks.hook("kubb:plugin:end", ({ plugin, duration }) => notify("PLUGIN_END", `Plugin finished: ${plugin.name}`, { duration }));
|
|
305
|
+
hooks.hook("kubb:files:processing:start", () => notify("FILES_START", "Starting file processing"));
|
|
306
|
+
hooks.hook("kubb:files:processing:update", ({ files }) => notify("FILES_UPDATE", `Processing ${files.length} files`));
|
|
307
|
+
hooks.hook("kubb:files:processing:end", () => notify("FILES_END", "File processing complete"));
|
|
308
|
+
hooks.hook("kubb:generation:start", () => notify("GENERATION_START", "Generation started"));
|
|
309
|
+
hooks.hook("kubb:setup:start", () => notify("SETUP_START", "Setting up Kubb"));
|
|
310
|
+
hooks.hook("kubb:setup:end", () => {
|
|
311
|
+
notify("SETUP_END", "Kubb setup complete");
|
|
312
|
+
notify("BUILD_START", "Starting build");
|
|
374
313
|
});
|
|
314
|
+
hooks.hook("kubb:generation:end", () => notify("GENERATION_END", "Generation ended"));
|
|
375
315
|
let userConfig;
|
|
376
316
|
let cwd;
|
|
377
317
|
try {
|
|
378
318
|
const configResult = await loadUserConfig(configPath, { notify });
|
|
379
|
-
|
|
319
|
+
const loaded = configResult.userConfig;
|
|
380
320
|
cwd = configResult.cwd;
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
configPath
|
|
384
|
-
|
|
385
|
-
|
|
321
|
+
const resolved = await (typeof loaded === "function" ? loaded({
|
|
322
|
+
logLevel,
|
|
323
|
+
config: configPath
|
|
324
|
+
}) : loaded);
|
|
325
|
+
const first = Array.isArray(resolved) ? resolved[0] : resolved;
|
|
326
|
+
if (!first) throw new Error("The config file exports an empty array of configurations");
|
|
327
|
+
userConfig = first;
|
|
386
328
|
} catch (error) {
|
|
387
329
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
388
330
|
return tool.error(errorMessage);
|
|
@@ -396,17 +338,17 @@ const generateTool = defineTool({
|
|
|
396
338
|
path: output
|
|
397
339
|
} : userConfig.output
|
|
398
340
|
};
|
|
399
|
-
|
|
341
|
+
notify("CONFIG_READY", "Configuration ready");
|
|
400
342
|
const result = await createKubb(config, { hooks }).generate();
|
|
401
|
-
|
|
343
|
+
notify("BUILD_END", `Build complete - Generated ${result.files.length} files`);
|
|
402
344
|
const problems = result.diagnostics.filter(Diagnostics.isProblem);
|
|
403
345
|
const errors = problems.filter((diagnostic) => diagnostic.severity === "error");
|
|
404
346
|
if (errors.length > 0) {
|
|
405
|
-
|
|
347
|
+
notify("BUILD_FAILED", `Build failed with ${errors.length} diagnostic(s)`);
|
|
406
348
|
const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic));
|
|
407
349
|
return tool.error(`Build failed:\n${formatDiagnostics(serialized)}\n\n\`\`\`json\n${JSON.stringify(serialized, null, 2)}\n\`\`\``);
|
|
408
350
|
}
|
|
409
|
-
|
|
351
|
+
notify("BUILD_SUCCESS", `Build completed successfully - Generated ${result.files.length} files`);
|
|
410
352
|
return tool.text(`Build completed successfully!\n\nGenerated ${result.files.length} files\n\n${messages.join("\n")}`);
|
|
411
353
|
} catch (caughtError) {
|
|
412
354
|
const serialized = Diagnostics.serialize(Diagnostics.from(caughtError));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["process"],"sources":["../package.json","../../../internals/shared/src/constants.ts","../../../internals/shared/src/init.ts","../../../internals/shared/src/loader.ts","../../../internals/utils/src/fs.ts","../../../internals/utils/src/promise.ts","../src/utils.ts","../src/tools/generate.ts","../src/tools/init.ts","../src/tools/validate.ts","../src/server.ts"],"sourcesContent":["","import type { PluginOption } from './types.ts'\n\nexport const KUBB_CONFIG_FILENAME = 'kubb.config.ts' as const\n\n/** File extensions a Kubb config file may use. */\nexport const CONFIG_EXTENSIONS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const\n\nexport const KUBB_PACKAGE_NAME = 'kubb' as const\n\nexport const initDefaults = {\n inputPath: './openapi.yaml',\n outputPath: './src/gen',\n plugins: ['plugin-ts'],\n} as const\n\nexport const availablePlugins: Array<PluginOption> = [\n {\n value: 'plugin-ts',\n label: 'TypeScript',\n hint: 'Recommended',\n packageName: '@kubb/plugin-ts',\n importName: 'pluginTs',\n category: 'types',\n },\n {\n value: 'plugin-axios',\n label: 'Axios Client',\n packageName: '@kubb/plugin-axios',\n importName: 'pluginAxios',\n category: 'client',\n },\n {\n value: 'plugin-fetch',\n label: 'Fetch Client',\n packageName: '@kubb/plugin-fetch',\n importName: 'pluginFetch',\n category: 'client',\n },\n {\n value: 'plugin-react-query',\n label: 'React Query / TanStack Query',\n packageName: '@kubb/plugin-react-query',\n importName: 'pluginReactQuery',\n category: 'framework',\n },\n {\n value: 'plugin-vue-query',\n label: 'Vue Query',\n packageName: '@kubb/plugin-vue-query',\n importName: 'pluginVueQuery',\n category: 'framework',\n },\n {\n value: 'plugin-zod',\n label: 'Zod Schemas',\n packageName: '@kubb/plugin-zod',\n importName: 'pluginZod',\n category: 'validation',\n },\n {\n value: 'plugin-faker',\n label: 'Faker.js Mocks',\n packageName: '@kubb/plugin-faker',\n importName: 'pluginFaker',\n category: 'mocks',\n },\n {\n value: 'plugin-msw',\n label: 'MSW Handlers',\n packageName: '@kubb/plugin-msw',\n importName: 'pluginMsw',\n category: 'mocks',\n },\n {\n value: 'plugin-cypress',\n label: 'Cypress Tests',\n packageName: '@kubb/plugin-cypress',\n importName: 'pluginCypress',\n category: 'testing',\n },\n {\n value: 'plugin-mcp',\n label: 'MCP Server (AI / Model Context Protocol)',\n packageName: '@kubb/plugin-mcp',\n importName: 'pluginMcp',\n category: 'ai',\n },\n {\n value: 'plugin-redoc',\n label: 'ReDoc Documentation',\n packageName: '@kubb/plugin-redoc',\n importName: 'pluginRedoc',\n category: 'documentation',\n },\n]\n","import { availablePlugins, KUBB_PACKAGE_NAME } from './constants.ts'\nimport type { PluginOption } from './types.ts'\n\n/**\n * Resolves a comma-separated plugin flag (e.g. `--plugins plugin-ts,plugin-zod`) into the\n * matching known plugin options. Unrecognized names are dropped, and a missing flag yields\n * an empty list.\n */\nexport function resolvePlugins(pluginsFlag: string | undefined): Array<PluginOption> {\n if (!pluginsFlag) {\n return []\n }\n const requested = pluginsFlag\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n return availablePlugins.filter((plugin) => requested.includes(plugin.value))\n}\n\nexport function generateConfigFile({\n selectedPlugins,\n inputPath,\n outputPath,\n}: {\n selectedPlugins: Array<PluginOption>\n inputPath: string\n outputPath: string\n}): string {\n const imports = selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`).join('\\n')\n\n const pluginConfigs = selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join('\\n')\n\n return `import { defineConfig } from 'kubb/config'\n${imports}\n\nexport default defineConfig({\n input: '${inputPath}',\n output: {\n path: '${outputPath}',\n clean: true,\n },\n plugins: [\n${pluginConfigs}\n ],\n})\n`\n}\n\n/**\n * Turns package names into install specifiers for the wizard.\n *\n * `kubb` is pinned to the exact version of the running CLI, since both ship from the same release\n * and resolving `kubb@beta` separately can land on a different version than the CLI doing the\n * scaffolding. Plugins release from their own repo on their own cadence, so they follow the\n * release channel of the CLI through its dist-tag.\n */\nexport function resolveInstallVersions({ packages, version }: { packages: Array<string>; version: string }): Array<string> {\n const prerelease = version.match(/-([a-z]+)/)?.[1]\n const tag = prerelease ?? 'latest'\n\n return packages.map((name) => (name === KUBB_PACKAGE_NAME ? `${name}@${version}` : `${name}@${tag}`))\n}\n","import { readFile } from 'node:fs/promises'\nimport { createJiti } from 'jiti'\n\n/**\n * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at\n * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.\n */\nconst JITI_OPTIONS = {\n jsx: { runtime: 'automatic', importSource: '@kubb/renderer-jsx' },\n moduleCache: false,\n} satisfies Parameters<typeof createJiti>[1]\n\n/**\n * Plain JavaScript files. jiti hands these to Node's native `import()`, whose module cache never\n * lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.\n */\nconst JAVASCRIPT_EXTENSION = /\\.[cm]?jsx?$/\n\n/**\n * Per-call options for {@link ModuleLoader.load}.\n */\ntype LoadModuleOptions = {\n /**\n * Return the module's default export instead of the full namespace.\n */\n default?: boolean\n}\n\n/**\n * Loads `.ts`/`.js` modules via jiti.\n */\ntype ModuleLoader = {\n load<T = unknown>(filePath: string, options?: LoadModuleOptions): Promise<T>\n}\n\n/**\n * Creates a jiti-based loader for Kubb's TypeScript and JavaScript config modules.\n *\n * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load\n * re-evaluates the file, so a config edited on disk is picked up by the next load.\n *\n * @example\n * ```ts\n * const config = await createModuleLoader().load('/abs/kubb.config.ts', { default: true })\n * ```\n */\nexport function createModuleLoader(): ModuleLoader {\n const jiti = createJiti(import.meta.url, JITI_OPTIONS)\n\n return {\n async load<T>(filePath: string, options?: LoadModuleOptions) {\n if (!JAVASCRIPT_EXTENSION.test(filePath)) {\n return (options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath)) as Promise<T>\n }\n\n // Transpiled through jiti instead, so every load re-evaluates the file as it is on disk now.\n // A TypeScript file it imports reloads too. A plain JavaScript file it imports still goes\n // through Node's `import()` and stays cached until the process restarts.\n const mod = (await jiti.evalModule(await readFile(filePath, 'utf8'), { filename: filePath, async: true, forceTranspile: true })) as\n | { default?: unknown }\n | undefined\n return (options?.default ? (mod?.default ?? mod) : mod) as T\n },\n }\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport { CONFIG_EXTENSIONS, createModuleLoader } from '@internals/shared'\nimport { isPathInside, isPromise } from '@internals/utils'\nimport type { CLIOptions, Config, PossibleConfig, SerializedDiagnostic } from '@kubb/core'\n\nconst CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`)\n\n/**\n * Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry\n * keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so\n * the agent can act on the problem rather than parsing a bare message. No ANSI styling,\n * unlike the CLI renderer.\n */\nexport function formatDiagnostics(diagnostics: ReadonlyArray<SerializedDiagnostic>): string {\n return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join('\\n\\n')\n}\n\nfunction formatDiagnostic(diagnostic: SerializedDiagnostic): string {\n const { code, message, location, help, plugin, docsUrl } = diagnostic\n const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`]\n\n if (location && 'pointer' in location) {\n lines.push(` at: ${location.pointer}`)\n }\n if (help) {\n lines.push(` fix: ${help}`)\n }\n if (docsUrl) {\n lines.push(` see: ${docsUrl}`)\n }\n\n return lines.join('\\n')\n}\n\ntype NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => Promise<void>\n\nconst loader = createModuleLoader()\n\n// No cache: the MCP server is long-running, so every tool call re-reads the config to pick up edits.\nfunction loadModule(filePath: string): Promise<unknown> {\n return loader.load(filePath, { default: true })\n}\n\n/**\n * Loads the user's Kubb config and returns it with the directory it was found in.\n *\n * When `configPath` is given it must use an allowed extension and resolve inside\n * the current working directory, otherwise loading throws. When omitted, the\n * known `kubb.config.*` file names are tried in the current directory. Every\n * outcome is reported through `notify` before the function returns or throws.\n */\nexport async function loadUserConfig(configPath: string | undefined, { notify }: { notify: NotifyFunction }): Promise<{ userConfig: Config; cwd: string }> {\n if (configPath) {\n const ext = path.extname(configPath)\n if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {\n const msg = `Invalid config file extension \"${ext}\". Allowed: ${CONFIG_EXTENSIONS.join(', ')}`\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const base = path.resolve(process.cwd())\n const resolvedConfigPath = path.resolve(base, configPath)\n if (!isPathInside(resolvedConfigPath, base)) {\n const msg = 'Invalid config file path: must be within the current working directory'\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const cwd = path.dirname(resolvedConfigPath)\n try {\n const userConfig = (await loadModule(resolvedConfigPath)) as Config\n await notify('CONFIG_LOADED', `Loaded config from ${resolvedConfigPath}`)\n return { userConfig, cwd }\n } catch (error) {\n const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`\n await notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n }\n\n const cwd = process.cwd()\n\n for (const configFileName of CONFIG_FILE_NAMES) {\n const configFilePath = path.resolve(process.cwd(), configFileName)\n if (!existsSync(configFilePath)) continue\n try {\n const userConfig = (await loadModule(configFilePath)) as Config\n await notify('CONFIG_LOADED', `Loaded ${configFileName} from current directory`)\n return { userConfig, cwd }\n } catch (err) {\n await notify('CONFIG_ERROR', `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n await notify('CONFIG_ERROR', 'No config file found')\n throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(', ')}`)\n}\n\n/**\n * Inputs forwarded to a config when it is defined as a function.\n */\ntype ResolveUserConfigOptions = {\n /**\n * Path of the loaded config, passed through to the config function as `config`.\n */\n configPath?: string\n /**\n * Log level passed through to the config function.\n */\n logLevel?: CLIOptions['logLevel']\n}\n\n/**\n * Normalizes a possible config into a single resolved `Config`.\n *\n * Calls the config when it is a function, awaits it when it is a promise, and\n * picks the first entry when it resolves to an array.\n */\nexport async function resolveUserConfig(config: PossibleConfig<CLIOptions>, options: ResolveUserConfigOptions): Promise<Config> {\n const result = typeof config === 'function' ? config({ logLevel: options.logLevel, config: options.configPath }) : config\n const resolved = isPromise(result) ? await result : result\n\n return (Array.isArray(resolved) ? resolved[0] : resolved) as Config\n}\n","import path from 'node:path'\nimport { CONFIG_EXTENSIONS } from '@internals/shared'\nimport { type Config, createKubb, type Diagnostic, Diagnostics, type KubbHooks, Hookable } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics, loadUserConfig, resolveUserConfig } from '../utils.ts'\n\nconst generateSchema = z.object({\n config: z.optional(\n z\n .string()\n .check(\n z.minLength(1),\n z.describe(`Path to kubb.config file (supports ${CONFIG_EXTENSIONS.join(', ')}). If not provided, will look for kubb.config.* in current directory`),\n ),\n ),\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI/Swagger spec file (overrides config)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory path (overrides config)'))),\n logLevel: z._default(z.enum(['silent', 'info', 'verbose']).check(z.describe('Log level for build output')), 'info'),\n})\n\nexport const generateTool = defineTool(\n {\n name: 'generate',\n description: 'Generate OpenAPI spec helpers using Kubb configuration',\n schema: generateSchema,\n },\n async function generate(schema: z.input<typeof generateSchema>) {\n const { config: configPath, input, output, logLevel } = schema\n\n try {\n const hooks = new Hookable<KubbHooks>()\n const messages: Array<string> = []\n\n const notify = async (type: string, message: string, data?: Record<string, unknown>) => {\n messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`)\n }\n\n hooks.hook('kubb:info', async ({ message }: { message: string }) => {\n await notify('INFO', message)\n })\n\n hooks.hook('kubb:success', async ({ message }: { message: string }) => {\n await notify('SUCCESS', message)\n })\n\n hooks.hook('kubb:error', async ({ error }: { error: Error }) => {\n await notify('ERROR', error.message)\n })\n\n hooks.hook('kubb:warn', async ({ message }: { message: string }) => {\n await notify('WARN', message)\n })\n\n hooks.hook('kubb:diagnostic', async ({ diagnostic }: { diagnostic: Diagnostic }) => {\n await notify('DIAGNOSTIC', diagnostic.message, Diagnostics.serialize(diagnostic))\n })\n\n hooks.hook('kubb:plugin:start', async ({ plugin }) => {\n await notify('PLUGIN_START', `Plugin starting: ${plugin.name}`)\n })\n\n hooks.hook('kubb:plugin:end', async ({ plugin, duration }) => {\n await notify('PLUGIN_END', `Plugin finished: ${plugin.name}`, { duration })\n })\n\n hooks.hook('kubb:files:processing:start', async () => {\n await notify('FILES_START', 'Starting file processing')\n })\n\n hooks.hook('kubb:files:processing:update', async ({ files }: { files: Array<{ file: { name: string } }> }) => {\n await notify('FILES_UPDATE', `Processing ${files.length} files`)\n })\n\n hooks.hook('kubb:files:processing:end', async () => {\n await notify('FILES_END', 'File processing complete')\n })\n\n hooks.hook('kubb:generation:start', async () => {\n await notify('GENERATION_START', 'Generation started')\n })\n\n hooks.hook('kubb:setup:start', async () => {\n await notify('SETUP_START', 'Setting up Kubb')\n })\n\n hooks.hook('kubb:setup:end', async () => {\n await notify('SETUP_END', 'Kubb setup complete')\n await notify('BUILD_START', 'Starting build')\n })\n\n hooks.hook('kubb:generation:end', async () => {\n await notify('GENERATION_END', 'Generation ended')\n })\n\n let userConfig: Config\n let cwd: string\n\n try {\n const configResult = await loadUserConfig(configPath, { notify })\n userConfig = configResult.userConfig\n cwd = configResult.cwd\n\n if (Array.isArray(userConfig)) {\n throw new Error('Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.')\n }\n\n userConfig = await resolveUserConfig(userConfig, {\n configPath,\n logLevel,\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return tool.error(errorMessage)\n }\n\n const config: Config = {\n ...userConfig,\n root: path.resolve(cwd, userConfig.root ?? '.'),\n input: input ?? userConfig.input,\n output: output\n ? {\n ...userConfig.output,\n path: output,\n }\n : userConfig.output,\n }\n\n await notify('CONFIG_READY', 'Configuration ready')\n\n const kubb = createKubb(config, { hooks })\n const result = await kubb.generate()\n await notify('BUILD_END', `Build complete - Generated ${result.files.length} files`)\n\n const problems = result.diagnostics.filter(Diagnostics.isProblem)\n const errors = problems.filter((diagnostic) => diagnostic.severity === 'error')\n if (errors.length > 0) {\n await notify('BUILD_FAILED', `Build failed with ${errors.length} diagnostic(s)`)\n\n const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic))\n return tool.error(`Build failed:\\n${formatDiagnostics(serialized)}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n\n await notify('BUILD_SUCCESS', `Build completed successfully - Generated ${result.files.length} files`)\n\n return tool.text(`Build completed successfully!\\n\\nGenerated ${result.files.length} files\\n\\n${messages.join('\\n')}`)\n } catch (caughtError) {\n const serialized = Diagnostics.serialize(Diagnostics.from(caughtError))\n return tool.error(`Build error:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { generateConfigFile, KUBB_CONFIG_FILENAME, resolvePlugins } from '@internals/shared'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\n\nconst initSchema = z.object({\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI spec (default: ./openapi.yaml)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory (default: ./src/gen)'))),\n plugins: z.optional(z.string().check(z.minLength(1), z.describe('Comma-separated list of plugins: plugin-ts,plugin-zod,...'))),\n})\n\nexport const initTool = defineTool(\n {\n name: 'init',\n description: 'Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.',\n schema: initSchema,\n },\n async ({ input = './openapi.yaml', output = './src/gen', plugins }) => {\n const selected = resolvePlugins(plugins)\n const content = generateConfigFile({ selectedPlugins: selected, inputPath: input, outputPath: output })\n const dest = path.join(process.cwd(), KUBB_CONFIG_FILENAME)\n if (fs.existsSync(dest)) {\n return tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`)\n }\n fs.writeFileSync(dest, content, 'utf-8')\n const packageList = ['kubb', ...selected.map((p) => p.packageName)].join(' ')\n return tool.text(`Created kubb.config.ts\\n\\nInstall packages:\\n npm install ${packageList}\\n\\nThen run:\\n npx kubb generate`)\n },\n)\n","import { adapterOas } from '@kubb/adapter-oas'\nimport { Diagnostics } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics } from '../utils.ts'\n\nconst validateSchema = z.object({\n input: z.string().check(z.minLength(1), z.describe('Path or URL to the OpenAPI/Swagger specification')),\n})\n\nexport const validateTool = defineTool(\n {\n name: 'validate',\n description: 'Validate an OpenAPI/Swagger specification file or URL',\n schema: validateSchema,\n },\n async ({ input }) => {\n try {\n await adapterOas().validate(input, { throwOnError: true })\n return tool.text(`Validation successful: ${input}`)\n } catch (err) {\n const serialized = Diagnostics.serialize(Diagnostics.from(err))\n return tool.error(`Validation failed:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import { StdioTransport } from '@tmcp/transport-stdio'\nimport { McpServer } from 'tmcp'\nimport { JsonSchemaAdapter } from 'tmcp/adapter'\nimport { en } from 'zod/locales'\nimport * as z from 'zod/mini'\nimport { version } from '../package.json'\nimport { generateTool } from './tools/generate.ts'\nimport { initTool } from './tools/init.ts'\nimport { validateTool } from './tools/validate.ts'\n\n/**\n * Describes each tool's input to MCP clients. `@tmcp/adapter-zod` imports the full `zod` build, so\n * this converts with Zod Mini instead. `io: 'input'` keeps fields with a default optional.\n */\nclass ZodMiniJsonSchemaAdapter extends JsonSchemaAdapter<z.ZodMiniType> {\n async toJsonSchema(schema: z.ZodMiniType) {\n // Zod's type also allows draft-4's boolean `exclusiveMaximum`, which the draft-7 target never emits.\n return z.toJSONSchema(schema, { target: 'draft-7', io: 'input' }) as Awaited<ReturnType<JsonSchemaAdapter<z.ZodMiniType>['toJsonSchema']>>\n }\n}\n\n/**\n * Builds the Kubb MCP server with the generate, validate, and init tools registered.\n *\n * @example\n * `const server = createMcpServer()`\n */\nexport function createMcpServer() {\n // Zod Mini ships without a locale, so validation errors would only say \"Invalid input\".\n z.config(en())\n const server = new McpServer({ name: 'Kubb', version }, { adapter: new ZodMiniJsonSchemaAdapter(), capabilities: { tools: {} } })\n server.tools([generateTool, validateTool, initTool])\n return server\n}\n\n/**\n * Starts the Kubb MCP server over stdio, the transport every local MCP client\n * (Claude, Copilot, editors) uses when it launches the server as a subprocess.\n */\nexport function startServer(): void {\n new StdioTransport(createMcpServer()).listen()\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["process"],"sources":["../package.json","../../../internals/shared/src/constants.ts","../../../internals/shared/src/init.ts","../../../internals/shared/src/loader.ts","../../../internals/utils/src/fs.ts","../src/utils.ts","../src/tools/generate.ts","../src/tools/init.ts","../src/tools/validate.ts","../src/server.ts"],"sourcesContent":["","export type PluginOption = {\n value: string\n label: string\n hint?: string\n packageName: string\n importName: string\n category: 'types' | 'client' | 'framework' | 'validation' | 'testing' | 'mocks' | 'documentation' | 'ai'\n}\n\nexport const KUBB_CONFIG_FILENAME = 'kubb.config.ts' as const\n\n/** File extensions a Kubb config file may use. */\nexport const CONFIG_EXTENSIONS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const\n\nexport const KUBB_PACKAGE_NAME = 'kubb' as const\n\nexport const initDefaults = {\n inputPath: './openapi.yaml',\n outputPath: './src/gen',\n plugins: ['plugin-ts'],\n} as const\n\nexport const availablePlugins: Array<PluginOption> = [\n {\n value: 'plugin-ts',\n label: 'TypeScript',\n hint: 'Recommended',\n packageName: '@kubb/plugin-ts',\n importName: 'pluginTs',\n category: 'types',\n },\n {\n value: 'plugin-axios',\n label: 'Axios Client',\n packageName: '@kubb/plugin-axios',\n importName: 'pluginAxios',\n category: 'client',\n },\n {\n value: 'plugin-fetch',\n label: 'Fetch Client',\n packageName: '@kubb/plugin-fetch',\n importName: 'pluginFetch',\n category: 'client',\n },\n {\n value: 'plugin-react-query',\n label: 'React Query / TanStack Query',\n packageName: '@kubb/plugin-react-query',\n importName: 'pluginReactQuery',\n category: 'framework',\n },\n {\n value: 'plugin-vue-query',\n label: 'Vue Query',\n packageName: '@kubb/plugin-vue-query',\n importName: 'pluginVueQuery',\n category: 'framework',\n },\n {\n value: 'plugin-zod',\n label: 'Zod Schemas',\n packageName: '@kubb/plugin-zod',\n importName: 'pluginZod',\n category: 'validation',\n },\n {\n value: 'plugin-faker',\n label: 'Faker.js Mocks',\n packageName: '@kubb/plugin-faker',\n importName: 'pluginFaker',\n category: 'mocks',\n },\n {\n value: 'plugin-msw',\n label: 'MSW Handlers',\n packageName: '@kubb/plugin-msw',\n importName: 'pluginMsw',\n category: 'mocks',\n },\n {\n value: 'plugin-cypress',\n label: 'Cypress Tests',\n packageName: '@kubb/plugin-cypress',\n importName: 'pluginCypress',\n category: 'testing',\n },\n {\n value: 'plugin-mcp',\n label: 'MCP Server (AI / Model Context Protocol)',\n packageName: '@kubb/plugin-mcp',\n importName: 'pluginMcp',\n category: 'ai',\n },\n {\n value: 'plugin-redoc',\n label: 'ReDoc Documentation',\n packageName: '@kubb/plugin-redoc',\n importName: 'pluginRedoc',\n category: 'documentation',\n },\n]\n","import { availablePlugins, type PluginOption } from './constants.ts'\n\n/**\n * Resolves a comma-separated plugin flag (e.g. `--plugins plugin-ts,plugin-zod`) into the\n * matching known plugin options. Unrecognized names are dropped, and a missing flag yields\n * an empty list.\n */\nexport function resolvePlugins(pluginsFlag: string | undefined): Array<PluginOption> {\n if (!pluginsFlag) {\n return []\n }\n const requested = pluginsFlag\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n return availablePlugins.filter((plugin) => requested.includes(plugin.value))\n}\n\nexport function generateConfigFile({\n selectedPlugins,\n inputPath,\n outputPath,\n}: {\n selectedPlugins: Array<PluginOption>\n inputPath: string\n outputPath: string\n}): string {\n const imports = [\n `import { defineConfig } from 'kubb/config'`,\n ...selectedPlugins.map((plugin) => `import { ${plugin.importName} } from '${plugin.packageName}'`),\n ].join('\\n')\n\n const pluginConfigs = selectedPlugins.map((plugin) => ` ${plugin.importName}(),`).join('\\n')\n\n return `${imports}\n\nexport default defineConfig({\n input: '${inputPath}',\n output: {\n path: '${outputPath}',\n clean: true,\n },\n plugins: [\n${pluginConfigs}\n ],\n})\n`\n}\n","import { readFile } from 'node:fs/promises'\nimport { createJiti } from 'jiti'\n\n/**\n * jiti options for loading Kubb config modules: the automatic JSX runtime pointed at\n * `@kubb/renderer-jsx`, and `moduleCache` off so a re-load re-evaluates the file.\n */\nconst JITI_OPTIONS = {\n jsx: { runtime: 'automatic', importSource: '@kubb/renderer-jsx' },\n moduleCache: false,\n} satisfies Parameters<typeof createJiti>[1]\n\n/**\n * Plain JavaScript files. jiti hands these to Node's native `import()`, whose module cache never\n * lets go of a file, so `moduleCache: false` alone would keep returning the first version of it.\n */\nconst JAVASCRIPT_EXTENSION = /\\.[cm]?jsx?$/\n\n/**\n * Per-call options for {@link loadModule}.\n */\ntype LoadModuleOptions = {\n /**\n * Return the module's default export instead of the full namespace.\n */\n default?: boolean\n}\n\nconst jiti = createJiti(import.meta.url, JITI_OPTIONS)\n\n/**\n * Loads a Kubb TypeScript or JavaScript config module via jiti.\n *\n * jiti transpiles TypeScript and the `@kubb/renderer-jsx` JSX runtime on the fly. Every load\n * re-evaluates the file, so a config edited on disk is picked up by the next load.\n *\n * @example\n * ```ts\n * const config = await loadModule('/abs/kubb.config.ts', { default: true })\n * ```\n */\nexport async function loadModule<T = unknown>(filePath: string, options?: LoadModuleOptions): Promise<T> {\n if (!JAVASCRIPT_EXTENSION.test(filePath)) {\n return (options?.default ? jiti.import(filePath, { default: true }) : jiti.import(filePath)) as Promise<T>\n }\n\n // Transpiled through jiti instead, so every load re-evaluates the file as it is on disk now.\n // A TypeScript file it imports reloads too. A plain JavaScript file it imports still goes\n // through Node's `import()` and stays cached until the process restarts.\n const mod = (await jiti.evalModule(await readFile(filePath, 'utf8'), { filename: filePath, async: true, forceTranspile: true })) as\n | { default?: unknown }\n | undefined\n return (options?.default ? (mod?.default ?? mod) : mod) as T\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport { CONFIG_EXTENSIONS, loadModule } from '@internals/shared'\nimport { isPathInside } from '@internals/utils'\nimport type { CLIOptions, PossibleConfig, SerializedDiagnostic } from '@kubb/core'\n\nconst CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `kubb.config${extension}`)\n\n/**\n * Renders serialized diagnostics as a plain-text block for an AI assistant. Each entry\n * keeps the stable `code`, the source pointer, the suggested fix, and the docs link, so\n * the agent can act on the problem rather than parsing a bare message. No ANSI styling,\n * unlike the CLI renderer.\n */\nexport function formatDiagnostics(diagnostics: ReadonlyArray<SerializedDiagnostic>): string {\n return diagnostics.map((diagnostic) => formatDiagnostic(diagnostic)).join('\\n\\n')\n}\n\nfunction formatDiagnostic(diagnostic: SerializedDiagnostic): string {\n const { code, message, location, help, plugin, docsUrl } = diagnostic\n const lines = [plugin ? `[${code}] ${plugin}: ${message}` : `[${code}]: ${message}`]\n\n if (location && 'pointer' in location) {\n lines.push(` at: ${location.pointer}`)\n }\n if (help) {\n lines.push(` fix: ${help}`)\n }\n if (docsUrl) {\n lines.push(` see: ${docsUrl}`)\n }\n\n return lines.join('\\n')\n}\n\ntype NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => void\n\n/**\n * Loads the user's Kubb config and returns it with the directory it was found in.\n *\n * When `configPath` is given it must use an allowed extension and resolve inside\n * the current working directory, otherwise loading throws. When omitted, the\n * known `kubb.config.*` file names are tried in the current directory. Every\n * outcome is reported through `notify` before the function returns or throws.\n */\nexport async function loadUserConfig(\n configPath: string | undefined,\n { notify }: { notify: NotifyFunction },\n): Promise<{ userConfig: PossibleConfig<CLIOptions>; cwd: string }> {\n if (configPath) {\n const ext = path.extname(configPath)\n if (!CONFIG_EXTENSIONS.some((extension) => extension === ext)) {\n const msg = `Invalid config file extension \"${ext}\". Allowed: ${CONFIG_EXTENSIONS.join(', ')}`\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const base = path.resolve(process.cwd())\n const resolvedConfigPath = path.resolve(base, configPath)\n if (!isPathInside(resolvedConfigPath, base)) {\n const msg = 'Invalid config file path: must be within the current working directory'\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n const cwd = path.dirname(resolvedConfigPath)\n try {\n const userConfig = await loadModule<PossibleConfig<CLIOptions>>(resolvedConfigPath, { default: true })\n notify('CONFIG_LOADED', `Loaded config from ${resolvedConfigPath}`)\n return { userConfig, cwd }\n } catch (error) {\n const msg = `Failed to load config: ${error instanceof Error ? error.message : String(error)}`\n notify('CONFIG_ERROR', msg)\n throw new Error(msg)\n }\n }\n\n const cwd = process.cwd()\n\n for (const configFileName of CONFIG_FILE_NAMES) {\n const configFilePath = path.resolve(process.cwd(), configFileName)\n if (!existsSync(configFilePath)) continue\n try {\n const userConfig = await loadModule<PossibleConfig<CLIOptions>>(configFilePath, { default: true })\n notify('CONFIG_LOADED', `Loaded ${configFileName} from current directory`)\n return { userConfig, cwd }\n } catch (err) {\n notify('CONFIG_ERROR', `Failed to load ${configFileName}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n\n notify('CONFIG_ERROR', 'No config file found')\n throw new Error(`No config file found. Please provide a config path or create one of: ${CONFIG_FILE_NAMES.join(', ')}`)\n}\n","import path from 'node:path'\nimport { CONFIG_EXTENSIONS } from '@internals/shared'\nimport { type Config, createKubb, Diagnostics, type KubbHooks, Hookable } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics, loadUserConfig } from '../utils.ts'\n\nconst generateSchema = z.object({\n config: z.optional(\n z\n .string()\n .check(\n z.minLength(1),\n z.describe(`Path to kubb.config file (supports ${CONFIG_EXTENSIONS.join(', ')}). If not provided, will look for kubb.config.* in current directory`),\n ),\n ),\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI/Swagger spec file (overrides config)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory path (overrides config)'))),\n logLevel: z._default(z.enum(['silent', 'info', 'verbose']).check(z.describe('Log level for build output')), 'info'),\n})\n\nexport const generateTool = defineTool(\n {\n name: 'generate',\n description: 'Generate OpenAPI spec helpers using Kubb configuration',\n schema: generateSchema,\n },\n async function generate(schema: z.input<typeof generateSchema>) {\n const { config: configPath, input, output, logLevel } = schema\n\n try {\n const hooks = new Hookable<KubbHooks>()\n const messages: Array<string> = []\n\n const notify = (type: string, message: string, data?: Record<string, unknown>) => {\n messages.push(data ? `${type}: ${message} ${JSON.stringify(data)}` : `${type}: ${message}`)\n }\n\n hooks.hook('kubb:info', ({ message }) => notify('INFO', message))\n hooks.hook('kubb:success', ({ message }) => notify('SUCCESS', message))\n hooks.hook('kubb:error', ({ error }) => notify('ERROR', error.message))\n hooks.hook('kubb:warn', ({ message }) => notify('WARN', message))\n hooks.hook('kubb:diagnostic', ({ diagnostic }) => notify('DIAGNOSTIC', diagnostic.message, Diagnostics.serialize(diagnostic)))\n hooks.hook('kubb:plugin:start', ({ plugin }) => notify('PLUGIN_START', `Plugin starting: ${plugin.name}`))\n hooks.hook('kubb:plugin:end', ({ plugin, duration }) => notify('PLUGIN_END', `Plugin finished: ${plugin.name}`, { duration }))\n hooks.hook('kubb:files:processing:start', () => notify('FILES_START', 'Starting file processing'))\n hooks.hook('kubb:files:processing:update', ({ files }) => notify('FILES_UPDATE', `Processing ${files.length} files`))\n hooks.hook('kubb:files:processing:end', () => notify('FILES_END', 'File processing complete'))\n hooks.hook('kubb:generation:start', () => notify('GENERATION_START', 'Generation started'))\n hooks.hook('kubb:setup:start', () => notify('SETUP_START', 'Setting up Kubb'))\n hooks.hook('kubb:setup:end', () => {\n notify('SETUP_END', 'Kubb setup complete')\n notify('BUILD_START', 'Starting build')\n })\n hooks.hook('kubb:generation:end', () => notify('GENERATION_END', 'Generation ended'))\n\n let userConfig: Config\n let cwd: string\n\n try {\n const configResult = await loadUserConfig(configPath, { notify })\n const loaded = configResult.userConfig\n cwd = configResult.cwd\n\n const resolved = await (typeof loaded === 'function' ? loaded({ logLevel, config: configPath }) : loaded)\n const first = Array.isArray(resolved) ? resolved[0] : resolved\n if (!first) throw new Error('The config file exports an empty array of configurations')\n userConfig = first\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return tool.error(errorMessage)\n }\n\n const config: Config = {\n ...userConfig,\n root: path.resolve(cwd, userConfig.root ?? '.'),\n input: input ?? userConfig.input,\n output: output\n ? {\n ...userConfig.output,\n path: output,\n }\n : userConfig.output,\n }\n\n notify('CONFIG_READY', 'Configuration ready')\n\n const kubb = createKubb(config, { hooks })\n const result = await kubb.generate()\n notify('BUILD_END', `Build complete - Generated ${result.files.length} files`)\n\n const problems = result.diagnostics.filter(Diagnostics.isProblem)\n const errors = problems.filter((diagnostic) => diagnostic.severity === 'error')\n if (errors.length > 0) {\n notify('BUILD_FAILED', `Build failed with ${errors.length} diagnostic(s)`)\n\n const serialized = problems.map((diagnostic) => Diagnostics.serialize(diagnostic))\n return tool.error(`Build failed:\\n${formatDiagnostics(serialized)}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n\n notify('BUILD_SUCCESS', `Build completed successfully - Generated ${result.files.length} files`)\n\n return tool.text(`Build completed successfully!\\n\\nGenerated ${result.files.length} files\\n\\n${messages.join('\\n')}`)\n } catch (caughtError) {\n const serialized = Diagnostics.serialize(Diagnostics.from(caughtError))\n return tool.error(`Build error:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { generateConfigFile, KUBB_CONFIG_FILENAME, resolvePlugins } from '@internals/shared'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\n\nconst initSchema = z.object({\n input: z.optional(z.string().check(z.minLength(1), z.describe('Path to OpenAPI spec (default: ./openapi.yaml)'))),\n output: z.optional(z.string().check(z.minLength(1), z.describe('Output directory (default: ./src/gen)'))),\n plugins: z.optional(z.string().check(z.minLength(1), z.describe('Comma-separated list of plugins: plugin-ts,plugin-zod,...'))),\n})\n\nexport const initTool = defineTool(\n {\n name: 'init',\n description: 'Scaffold a kubb.config.ts in the current directory (non-interactive). Does not install packages.',\n schema: initSchema,\n },\n async ({ input = './openapi.yaml', output = './src/gen', plugins }) => {\n const selected = resolvePlugins(plugins)\n const content = generateConfigFile({ selectedPlugins: selected, inputPath: input, outputPath: output })\n const dest = path.join(process.cwd(), KUBB_CONFIG_FILENAME)\n if (fs.existsSync(dest)) {\n return tool.error(`${KUBB_CONFIG_FILENAME} already exists at ${dest}. Delete it first before running init again.`)\n }\n fs.writeFileSync(dest, content, 'utf-8')\n const packageList = ['kubb', ...selected.map((p) => p.packageName)].join(' ')\n return tool.text(`Created kubb.config.ts\\n\\nInstall packages:\\n npm install ${packageList}\\n\\nThen run:\\n npx kubb generate`)\n },\n)\n","import { adapterOas } from '@kubb/adapter-oas'\nimport { Diagnostics } from '@kubb/core'\nimport { defineTool } from 'tmcp/tool'\nimport { tool } from 'tmcp/utils'\nimport * as z from 'zod/mini'\nimport { formatDiagnostics } from '../utils.ts'\n\nconst validateSchema = z.object({\n input: z.string().check(z.minLength(1), z.describe('Path or URL to the OpenAPI/Swagger specification')),\n})\n\nexport const validateTool = defineTool(\n {\n name: 'validate',\n description: 'Validate an OpenAPI/Swagger specification file or URL',\n schema: validateSchema,\n },\n async ({ input }) => {\n try {\n await adapterOas().validate(input, { throwOnError: true })\n return tool.text(`Validation successful: ${input}`)\n } catch (err) {\n const serialized = Diagnostics.serialize(Diagnostics.from(err))\n return tool.error(`Validation failed:\\n${formatDiagnostics([serialized])}\\n\\n\\`\\`\\`json\\n${JSON.stringify(serialized, null, 2)}\\n\\`\\`\\``)\n }\n },\n)\n","import { StdioTransport } from '@tmcp/transport-stdio'\nimport { McpServer } from 'tmcp'\nimport { JsonSchemaAdapter } from 'tmcp/adapter'\nimport { en } from 'zod/locales'\nimport * as z from 'zod/mini'\nimport { version } from '../package.json'\nimport { generateTool } from './tools/generate.ts'\nimport { initTool } from './tools/init.ts'\nimport { validateTool } from './tools/validate.ts'\n\n/**\n * Describes each tool's input to MCP clients. `@tmcp/adapter-zod` imports the full `zod` build, so\n * this converts with Zod Mini instead. `io: 'input'` keeps fields with a default optional.\n */\nclass ZodMiniJsonSchemaAdapter extends JsonSchemaAdapter<z.ZodMiniType> {\n async toJsonSchema(schema: z.ZodMiniType) {\n // Zod's type also allows draft-4's boolean `exclusiveMaximum`, which the draft-7 target never emits.\n return z.toJSONSchema(schema, { target: 'draft-7', io: 'input' }) as Awaited<ReturnType<JsonSchemaAdapter<z.ZodMiniType>['toJsonSchema']>>\n }\n}\n\n/**\n * Builds the Kubb MCP server with the generate, validate, and init tools registered.\n *\n * @example\n * `const server = createMcpServer()`\n */\nexport function createMcpServer() {\n // Zod Mini ships without a locale, so validation errors would only say \"Invalid input\".\n z.config(en())\n const server = new McpServer({ name: 'Kubb', version }, { adapter: new ZodMiniJsonSchemaAdapter(), capabilities: { tools: {} } })\n server.tools([generateTool, validateTool, initTool])\n return server\n}\n\n/**\n * Starts the Kubb MCP server over stdio, the transport every local MCP client\n * (Claude, Copilot, editors) uses when it launches the server as a subprocess.\n */\nexport function startServer(): void {\n new StdioTransport(createMcpServer()).listen()\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/mcp",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.9.1",
|
|
4
4
|
"description": "MCP server for Kubb. Exposes code generation as a tool over the Model Context Protocol so AI assistants like Claude, Cursor, and other MCP-compatible clients can generate TypeScript types, clients, and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"registry": "https://registry.npmjs.org/"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@kubb/adapter-oas": "5.
|
|
66
|
-
"@kubb/core": "5.
|
|
65
|
+
"@kubb/adapter-oas": "5.9.1",
|
|
66
|
+
"@kubb/core": "5.9.1",
|
|
67
67
|
"@tmcp/transport-stdio": "^0.5.0",
|
|
68
68
|
"jiti": "^2.7.0",
|
|
69
69
|
"tmcp": "^1.20.0",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@internals/shared": "0.0.1",
|
|
74
74
|
"@internals/utils": "0.0.1",
|
|
75
|
-
"@kubb/renderer-jsx": "5.
|
|
75
|
+
"@kubb/renderer-jsx": "5.9.1"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@kubb/renderer-jsx": "5.
|
|
78
|
+
"@kubb/renderer-jsx": "5.9.1"
|
|
79
79
|
},
|
|
80
80
|
"engines": {
|
|
81
81
|
"node": ">=22"
|