@kubb/plugin-mcp 5.0.0-beta.4 → 5.0.0-beta.42
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 +39 -22
- package/dist/index.cjs +343 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +82 -27
- package/dist/index.js +345 -207
- package/dist/index.js.map +1 -1
- package/extension.yaml +633 -160
- package/package.json +11 -11
- package/src/components/McpHandler.tsx +15 -20
- package/src/components/Server.tsx +76 -71
- package/src/generators/mcpGenerator.tsx +21 -23
- package/src/generators/serverGenerator.tsx +22 -22
- package/src/plugin.ts +38 -18
- package/src/resolvers/resolverMcp.ts +16 -6
- package/src/types.ts +27 -13
- package/src/utils.ts +15 -80
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./chunk
|
|
1
|
+
import "./chunk-C0LytTxp.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
4
4
|
import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
5
|
-
import { Const, File, Function,
|
|
5
|
+
import { Const, File, Function, jsxRendererSync } from "@kubb/renderer-jsx";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
7
7
|
import { pluginZodName } from "@kubb/plugin-zod";
|
|
8
8
|
import { pluginClientName } from "@kubb/plugin-client";
|
|
@@ -220,16 +220,16 @@ var URLPath = class {
|
|
|
220
220
|
get object() {
|
|
221
221
|
return this.toObject();
|
|
222
222
|
}
|
|
223
|
-
/** Returns a map of path parameter names, or `
|
|
223
|
+
/** Returns a map of path parameter names, or `null` when the path has no parameters.
|
|
224
224
|
*
|
|
225
225
|
* @example
|
|
226
226
|
* ```ts
|
|
227
227
|
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
228
|
-
* new URLPath('/pet').params //
|
|
228
|
+
* new URLPath('/pet').params // null
|
|
229
229
|
* ```
|
|
230
230
|
*/
|
|
231
231
|
get params() {
|
|
232
|
-
return this.
|
|
232
|
+
return this.toParamsObject();
|
|
233
233
|
}
|
|
234
234
|
#transformParam(raw) {
|
|
235
235
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
@@ -247,7 +247,7 @@ var URLPath = class {
|
|
|
247
247
|
toObject({ type = "path", replacer, stringify } = {}) {
|
|
248
248
|
const object = {
|
|
249
249
|
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
250
|
-
params: this.
|
|
250
|
+
params: this.toParamsObject()
|
|
251
251
|
};
|
|
252
252
|
if (stringify) {
|
|
253
253
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -263,12 +263,13 @@ var URLPath = class {
|
|
|
263
263
|
* @example
|
|
264
264
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
265
265
|
*/
|
|
266
|
-
toTemplateString({ prefix
|
|
267
|
-
|
|
266
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
267
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
268
268
|
if (i % 2 === 0) return part;
|
|
269
269
|
const param = this.#transformParam(part);
|
|
270
270
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
271
|
-
}).join("")
|
|
271
|
+
}).join("");
|
|
272
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
272
273
|
}
|
|
273
274
|
/**
|
|
274
275
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -277,17 +278,17 @@ var URLPath = class {
|
|
|
277
278
|
*
|
|
278
279
|
* @example
|
|
279
280
|
* ```ts
|
|
280
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').
|
|
281
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
281
282
|
* // { petId: 'petId', tagId: 'tagId' }
|
|
282
283
|
* ```
|
|
283
284
|
*/
|
|
284
|
-
|
|
285
|
+
toParamsObject(replacer) {
|
|
285
286
|
const params = {};
|
|
286
287
|
this.#eachParam((_raw, param) => {
|
|
287
288
|
const key = replacer ? replacer(param) : param;
|
|
288
289
|
params[key] = key;
|
|
289
290
|
});
|
|
290
|
-
return Object.keys(params).length > 0 ? params :
|
|
291
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
291
292
|
}
|
|
292
293
|
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
293
294
|
*
|
|
@@ -301,82 +302,144 @@ var URLPath = class {
|
|
|
301
302
|
}
|
|
302
303
|
};
|
|
303
304
|
//#endregion
|
|
304
|
-
//#region src/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const code = Number(res.statusCode);
|
|
311
|
-
if (code >= 200 && code < 300) return res.statusCode;
|
|
312
|
-
}
|
|
305
|
+
//#region ../../internals/shared/src/operation.ts
|
|
306
|
+
function getOperationLink(node, link) {
|
|
307
|
+
if (!link) return null;
|
|
308
|
+
if (typeof link === "function") return link(node) ?? null;
|
|
309
|
+
if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
|
|
310
|
+
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
313
311
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
* Build JSDoc comment lines from an OperationNode.
|
|
324
|
-
*/
|
|
325
|
-
function getComments(node) {
|
|
326
|
-
return [
|
|
312
|
+
function buildOperationComments(node, options = {}) {
|
|
313
|
+
const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
|
|
314
|
+
const linkComment = getOperationLink(node, link);
|
|
315
|
+
const filteredComments = (linkPosition === "beforeDeprecated" ? [
|
|
316
|
+
node.description && `@description ${node.description}`,
|
|
317
|
+
node.summary && `@summary ${node.summary}`,
|
|
318
|
+
linkComment,
|
|
319
|
+
node.deprecated && "@deprecated"
|
|
320
|
+
] : [
|
|
327
321
|
node.description && `@description ${node.description}`,
|
|
328
322
|
node.summary && `@summary ${node.summary}`,
|
|
329
323
|
node.deprecated && "@deprecated",
|
|
330
|
-
|
|
331
|
-
].filter((
|
|
324
|
+
linkComment
|
|
325
|
+
]).filter((comment) => Boolean(comment));
|
|
326
|
+
if (!splitLines) return filteredComments;
|
|
327
|
+
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
332
328
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
329
|
+
function getOperationParameters(node, options = {}) {
|
|
330
|
+
const params = ast.caseParams(node.parameters, options.paramsCasing);
|
|
331
|
+
return {
|
|
332
|
+
path: params.filter((param) => param.in === "path"),
|
|
333
|
+
query: params.filter((param) => param.in === "query"),
|
|
334
|
+
header: params.filter((param) => param.in === "header"),
|
|
335
|
+
cookie: params.filter((param) => param.in === "cookie")
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function getStatusCodeNumber(statusCode) {
|
|
339
|
+
const code = Number(statusCode);
|
|
340
|
+
return Number.isNaN(code) ? null : code;
|
|
341
|
+
}
|
|
342
|
+
function isSuccessStatusCode(statusCode) {
|
|
343
|
+
const code = getStatusCodeNumber(statusCode);
|
|
344
|
+
return code !== null && code >= 200 && code < 300;
|
|
345
|
+
}
|
|
346
|
+
function isErrorStatusCode(statusCode) {
|
|
347
|
+
const code = getStatusCodeNumber(statusCode);
|
|
348
|
+
return code !== null && code >= 400;
|
|
349
|
+
}
|
|
350
|
+
function resolveErrorNames(node, resolver) {
|
|
351
|
+
return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
352
|
+
}
|
|
353
|
+
function resolveStatusCodeNames(node, resolver) {
|
|
354
|
+
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
355
|
+
}
|
|
356
|
+
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
357
|
+
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
358
|
+
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
359
|
+
let byResolver = typeNamesByResolver.get(resolver);
|
|
360
|
+
if (byResolver) {
|
|
361
|
+
const cached = byResolver.get(cacheKey);
|
|
362
|
+
if (cached) return cached;
|
|
363
|
+
} else {
|
|
364
|
+
byResolver = /* @__PURE__ */ new Map();
|
|
365
|
+
typeNamesByResolver.set(resolver, byResolver);
|
|
345
366
|
}
|
|
346
|
-
|
|
367
|
+
const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
|
|
368
|
+
const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
|
|
369
|
+
const exclude = new Set(options.exclude ?? []);
|
|
370
|
+
const paramNames = [
|
|
371
|
+
...path.map((param) => resolver.resolvePathParamsName(node, param)),
|
|
372
|
+
...query.map((param) => resolver.resolveQueryParamsName(node, param)),
|
|
373
|
+
...header.map((param) => resolver.resolveHeaderParamsName(node, param))
|
|
374
|
+
];
|
|
375
|
+
const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
|
|
376
|
+
const result = (options.order === "body-response-first" ? [
|
|
377
|
+
...bodyAndResponseNames,
|
|
378
|
+
...paramNames,
|
|
379
|
+
...responseStatusNames
|
|
380
|
+
] : [
|
|
381
|
+
...paramNames,
|
|
382
|
+
...bodyAndResponseNames,
|
|
383
|
+
...responseStatusNames
|
|
384
|
+
]).filter((name) => Boolean(name) && !exclude.has(name));
|
|
385
|
+
byResolver.set(cacheKey, result);
|
|
386
|
+
return result;
|
|
387
|
+
}
|
|
388
|
+
function findSuccessStatusCode(responses) {
|
|
389
|
+
for (const response of responses) if (isSuccessStatusCode(response.statusCode)) return response.statusCode;
|
|
390
|
+
return null;
|
|
347
391
|
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region ../../internals/shared/src/group.ts
|
|
348
394
|
/**
|
|
349
|
-
*
|
|
350
|
-
*
|
|
395
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
396
|
+
* shared default naming so every plugin groups output consistently:
|
|
397
|
+
*
|
|
398
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
399
|
+
* - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
|
|
400
|
+
*
|
|
401
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
402
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
403
|
+
* per-plugin convention.
|
|
404
|
+
*
|
|
405
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
406
|
+
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```ts
|
|
410
|
+
* createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
|
|
411
|
+
* createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
|
|
412
|
+
* ```
|
|
351
413
|
*/
|
|
352
|
-
function
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
414
|
+
function createGroupConfig(group, options) {
|
|
415
|
+
if (!group) return null;
|
|
416
|
+
const defaultName = (ctx) => {
|
|
417
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
418
|
+
return `${camelCase(ctx.group)}${options.suffix}`;
|
|
419
|
+
};
|
|
420
|
+
return {
|
|
421
|
+
...group,
|
|
422
|
+
name: group.name ? group.name : defaultName
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region ../../internals/shared/src/params.ts
|
|
427
|
+
function buildParamsMapping(originalParams, mappedParams) {
|
|
428
|
+
const mapping = {};
|
|
429
|
+
let hasChanged = false;
|
|
430
|
+
originalParams.forEach((param, i) => {
|
|
431
|
+
const mappedName = mappedParams[i]?.name ?? param.name;
|
|
432
|
+
mapping[param.name] = mappedName;
|
|
433
|
+
if (param.name !== mappedName) hasChanged = true;
|
|
434
|
+
});
|
|
435
|
+
return hasChanged ? mapping : null;
|
|
436
|
+
}
|
|
437
|
+
function buildTransformedParamsMapping(params, transformName) {
|
|
438
|
+
if (!params.length) return null;
|
|
439
|
+
return buildParamsMapping(params, params.map((param) => ({
|
|
440
|
+
...param,
|
|
441
|
+
name: transformName(param.name)
|
|
442
|
+
})));
|
|
380
443
|
}
|
|
381
444
|
//#endregion
|
|
382
445
|
//#region src/components/McpHandler.tsx
|
|
@@ -388,16 +451,13 @@ function buildRemappingCode(mapping, varName, sourceName) {
|
|
|
388
451
|
}
|
|
389
452
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
390
453
|
function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasing }) {
|
|
454
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
391
455
|
const urlPath = new URLPath(node.path);
|
|
392
456
|
const contentType = node.requestBody?.content?.[0]?.contentType;
|
|
393
457
|
const isFormData = contentType === "multipart/form-data";
|
|
394
|
-
const
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
const originalPathParams = node.parameters.filter((p) => p.in === "path");
|
|
398
|
-
const originalQueryParams = node.parameters.filter((p) => p.in === "query");
|
|
399
|
-
const originalHeaderParams = node.parameters.filter((p) => p.in === "header");
|
|
400
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0;
|
|
458
|
+
const { query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing });
|
|
459
|
+
const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
|
|
460
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
|
|
401
461
|
const responseName = resolver.resolveResponseName(node);
|
|
402
462
|
const errorResponses = node.responses.filter((r) => Number(r.statusCode) >= 400).map((r) => resolver.resolveResponseStatusName(node, r.statusCode));
|
|
403
463
|
const generics = [
|
|
@@ -413,11 +473,11 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
413
473
|
});
|
|
414
474
|
const baseParamsSignature = declarationPrinter.print(paramsNode) ?? "";
|
|
415
475
|
const paramsSignature = baseParamsSignature ? `${baseParamsSignature}, request: RequestHandlerExtra<ServerRequest, ServerNotification>` : "request: RequestHandlerExtra<ServerRequest, ServerNotification>";
|
|
416
|
-
const pathParamsMapping = paramsCasing ?
|
|
417
|
-
const queryParamsMapping = paramsCasing ?
|
|
418
|
-
const headerParamsMapping = paramsCasing ?
|
|
419
|
-
const contentTypeHeader = contentType && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` :
|
|
420
|
-
const headers = [headerParams.length ? headerParamsMapping ? "...mappedHeaders" : "...headers" :
|
|
476
|
+
const pathParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalPathParams, camelCase) : null;
|
|
477
|
+
const queryParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalQueryParams, camelCase) : null;
|
|
478
|
+
const headerParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalHeaderParams, camelCase) : null;
|
|
479
|
+
const contentTypeHeader = contentType && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null;
|
|
480
|
+
const headers = [headerParams.length ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null, contentTypeHeader].filter(Boolean);
|
|
421
481
|
const fetchConfig = [];
|
|
422
482
|
fetchConfig.push(`method: ${JSON.stringify(node.method.toUpperCase())}`);
|
|
423
483
|
fetchConfig.push(`url: ${urlPath.template}`);
|
|
@@ -451,7 +511,7 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
451
511
|
async: true,
|
|
452
512
|
export: true,
|
|
453
513
|
params: paramsSignature,
|
|
454
|
-
JSDoc: { comments:
|
|
514
|
+
JSDoc: { comments: buildOperationComments(node) },
|
|
455
515
|
returnType: "Promise<CallToolResult>",
|
|
456
516
|
children: [
|
|
457
517
|
"",
|
|
@@ -473,7 +533,7 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
473
533
|
/* @__PURE__ */ jsx("br", {}),
|
|
474
534
|
isFormData && requestName && "const formData = buildFormData(requestData)",
|
|
475
535
|
/* @__PURE__ */ jsx("br", {}),
|
|
476
|
-
`const res = await
|
|
536
|
+
`const res = await client<${generics.join(", ")}>({ ${fetchConfig.join(", ")} }, request)`,
|
|
477
537
|
/* @__PURE__ */ jsx("br", {}),
|
|
478
538
|
callToolResult
|
|
479
539
|
]
|
|
@@ -481,62 +541,80 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
481
541
|
});
|
|
482
542
|
}
|
|
483
543
|
//#endregion
|
|
544
|
+
//#region src/utils.ts
|
|
545
|
+
/**
|
|
546
|
+
* Render a group param value — compose individual schemas into `z.object({ ... })`,
|
|
547
|
+
* or use a schema name string directly.
|
|
548
|
+
*/
|
|
549
|
+
function zodGroupExpr(entry) {
|
|
550
|
+
if (typeof entry === "string") return entry;
|
|
551
|
+
return `z.object({ ${entry.map((p) => `${JSON.stringify(p.name)}: ${p.schemaName}`).join(", ")} })`;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Convert a SchemaNode type to an inline Zod expression string.
|
|
555
|
+
* Used as fallback when no named zod schema is available for a path parameter.
|
|
556
|
+
*/
|
|
557
|
+
function zodExprFromSchemaNode(schema) {
|
|
558
|
+
const baseExpr = (() => {
|
|
559
|
+
if (schema.type === "enum") {
|
|
560
|
+
const rawValues = schema.namedEnumValues?.length ? schema.namedEnumValues.map((v) => v.value) : (schema.enumValues ?? []).filter((v) => v !== null);
|
|
561
|
+
if (rawValues.length > 0 && rawValues.every((v) => typeof v === "string")) return `z.enum([${rawValues.map((v) => JSON.stringify(v)).join(", ")}])`;
|
|
562
|
+
if (rawValues.length > 0) {
|
|
563
|
+
const literals = rawValues.map((v) => `z.literal(${JSON.stringify(v)})`);
|
|
564
|
+
return literals.length === 1 ? literals[0] : `z.union([${literals.join(", ")}])`;
|
|
565
|
+
}
|
|
566
|
+
return "z.string()";
|
|
567
|
+
}
|
|
568
|
+
if (schema.type === "integer") return "z.coerce.number()";
|
|
569
|
+
if (schema.type === "number") return "z.number()";
|
|
570
|
+
if (schema.type === "boolean") return "z.boolean()";
|
|
571
|
+
if (schema.type === "array") return "z.array(z.unknown())";
|
|
572
|
+
return "z.string()";
|
|
573
|
+
})();
|
|
574
|
+
return schema.nullable ? `${baseExpr}.nullable()` : baseExpr;
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
484
577
|
//#region src/components/Server.tsx
|
|
485
578
|
const keysPrinter = functionPrinter({ mode: "keys" });
|
|
486
579
|
function Server({ name, serverName, serverVersion, paramsCasing, operations }) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
otherEntries.sort((a, b) => a.key.localeCompare(b.key));
|
|
526
|
-
const entries = [...pathEntries, ...otherEntries];
|
|
527
|
-
const paramsNode = entries.length ? ast.createFunctionParameters({ params: [ast.createParameterGroup({ properties: entries.map((e) => ast.createFunctionParameter({
|
|
528
|
-
name: e.key,
|
|
529
|
-
optional: false
|
|
530
|
-
})) })] }) : void 0;
|
|
531
|
-
const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
|
|
532
|
-
const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} }` : void 0;
|
|
533
|
-
const outputSchema = zod.responseName;
|
|
534
|
-
const config = [
|
|
535
|
-
tool.title ? `title: ${JSON.stringify(tool.title)}` : null,
|
|
536
|
-
`description: ${JSON.stringify(tool.description)}`,
|
|
537
|
-
outputSchema ? `outputSchema: { data: ${outputSchema} }` : null
|
|
538
|
-
].filter(Boolean).join(",\n ");
|
|
539
|
-
if (inputSchema) return `
|
|
580
|
+
const registrations = operations.map(({ tool, mcp, zod, node }) => {
|
|
581
|
+
const { path: pathParams } = getOperationParameters(node, { paramsCasing });
|
|
582
|
+
const pathEntries = [];
|
|
583
|
+
const otherEntries = [];
|
|
584
|
+
for (const p of pathParams) {
|
|
585
|
+
const zodParam = zod.pathParams.find((zp) => zp.name === p.name);
|
|
586
|
+
pathEntries.push({
|
|
587
|
+
key: p.name,
|
|
588
|
+
value: zodParam ? zodParam.schemaName : zodExprFromSchemaNode(p.schema)
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
if (zod.requestName) otherEntries.push({
|
|
592
|
+
key: "data",
|
|
593
|
+
value: zod.requestName
|
|
594
|
+
});
|
|
595
|
+
if (zod.queryParams) otherEntries.push({
|
|
596
|
+
key: "params",
|
|
597
|
+
value: zodGroupExpr(zod.queryParams)
|
|
598
|
+
});
|
|
599
|
+
if (zod.headerParams) otherEntries.push({
|
|
600
|
+
key: "headers",
|
|
601
|
+
value: zodGroupExpr(zod.headerParams)
|
|
602
|
+
});
|
|
603
|
+
otherEntries.sort((a, b) => a.key.localeCompare(b.key));
|
|
604
|
+
const entries = [...pathEntries, ...otherEntries];
|
|
605
|
+
const paramsNode = entries.length ? ast.createFunctionParameters({ params: [ast.createParameterGroup({ properties: entries.map((e) => ast.createFunctionParameter({
|
|
606
|
+
name: e.key,
|
|
607
|
+
optional: false
|
|
608
|
+
})) })] }) : null;
|
|
609
|
+
const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
|
|
610
|
+
const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} }` : null;
|
|
611
|
+
const outputSchema = zod.responseName;
|
|
612
|
+
const config = [
|
|
613
|
+
tool.title ? `title: ${JSON.stringify(tool.title)}` : null,
|
|
614
|
+
`description: ${JSON.stringify(tool.description)}`,
|
|
615
|
+
outputSchema ? `outputSchema: { data: ${outputSchema} }` : null
|
|
616
|
+
].filter(Boolean).join(",\n ");
|
|
617
|
+
if (inputSchema) return `
|
|
540
618
|
server.registerTool(${JSON.stringify(tool.name)}, {
|
|
541
619
|
${config},
|
|
542
620
|
inputSchema: ${inputSchema},
|
|
@@ -544,14 +622,34 @@ server.registerTool(${JSON.stringify(tool.name)}, {
|
|
|
544
622
|
return ${mcp.name}(${destructured}, request)
|
|
545
623
|
})
|
|
546
624
|
`;
|
|
547
|
-
|
|
625
|
+
return `
|
|
548
626
|
server.registerTool(${JSON.stringify(tool.name)}, {
|
|
549
627
|
${config},
|
|
550
628
|
}, async (request) => {
|
|
551
629
|
return ${mcp.name}(request)
|
|
552
630
|
})
|
|
553
631
|
`;
|
|
554
|
-
|
|
632
|
+
}).filter(Boolean).join("\n");
|
|
633
|
+
return /* @__PURE__ */ jsxs(File.Source, {
|
|
634
|
+
name,
|
|
635
|
+
isExportable: true,
|
|
636
|
+
isIndexable: true,
|
|
637
|
+
children: [
|
|
638
|
+
/* @__PURE__ */ jsx(Function, {
|
|
639
|
+
name: "getServer",
|
|
640
|
+
export: true,
|
|
641
|
+
children: `const server = new McpServer({
|
|
642
|
+
name: '${serverName}',
|
|
643
|
+
version: '${serverVersion}',
|
|
644
|
+
})
|
|
645
|
+
${registrations}
|
|
646
|
+
return server`
|
|
647
|
+
}),
|
|
648
|
+
/* @__PURE__ */ jsx(Const, {
|
|
649
|
+
name: "server",
|
|
650
|
+
export: true,
|
|
651
|
+
children: "getServer()"
|
|
652
|
+
}),
|
|
555
653
|
/* @__PURE__ */ jsx(Function, {
|
|
556
654
|
name: "startServer",
|
|
557
655
|
async: true,
|
|
@@ -570,29 +668,28 @@ server.registerTool(${JSON.stringify(tool.name)}, {
|
|
|
570
668
|
}
|
|
571
669
|
//#endregion
|
|
572
670
|
//#region src/generators/mcpGenerator.tsx
|
|
671
|
+
/**
|
|
672
|
+
* Built-in operation generator for `@kubb/plugin-mcp`. Emits one MCP tool
|
|
673
|
+
* handler per OpenAPI operation, wiring the input Zod schema, the HTTP call,
|
|
674
|
+
* and the response shape into a single function that an MCP server can
|
|
675
|
+
* register as a callable tool.
|
|
676
|
+
*/
|
|
573
677
|
const mcpGenerator = defineGenerator({
|
|
574
678
|
name: "mcp",
|
|
575
|
-
renderer:
|
|
679
|
+
renderer: jsxRendererSync,
|
|
576
680
|
operation(node, ctx) {
|
|
681
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
577
682
|
const { resolver, driver, root } = ctx;
|
|
578
683
|
const { output, client, paramsCasing, group } = ctx.options;
|
|
579
684
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
580
685
|
if (!pluginTs) return null;
|
|
581
686
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const importedTypeNames = [
|
|
587
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
588
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
589
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
590
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
591
|
-
tsResolver.resolveResponseName(node),
|
|
592
|
-
...node.responses.filter((r) => Number(r.statusCode) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode))
|
|
593
|
-
].filter(Boolean);
|
|
687
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, {
|
|
688
|
+
paramsCasing,
|
|
689
|
+
responseStatusNames: "error"
|
|
690
|
+
});
|
|
594
691
|
const meta = {
|
|
595
|
-
name: resolver.
|
|
692
|
+
name: resolver.resolveHandlerName(node),
|
|
596
693
|
file: resolver.resolveFile({
|
|
597
694
|
name: node.operationId,
|
|
598
695
|
extname: ".ts",
|
|
@@ -601,7 +698,7 @@ const mcpGenerator = defineGenerator({
|
|
|
601
698
|
}, {
|
|
602
699
|
root,
|
|
603
700
|
output,
|
|
604
|
-
group
|
|
701
|
+
group: group ?? void 0
|
|
605
702
|
}),
|
|
606
703
|
fileTs: tsResolver.resolveFile({
|
|
607
704
|
name: node.operationId,
|
|
@@ -611,7 +708,7 @@ const mcpGenerator = defineGenerator({
|
|
|
611
708
|
}, {
|
|
612
709
|
root,
|
|
613
710
|
output: pluginTs.options?.output ?? output,
|
|
614
|
-
group: pluginTs.options?.group
|
|
711
|
+
group: pluginTs.options?.group ?? void 0
|
|
615
712
|
})
|
|
616
713
|
};
|
|
617
714
|
return /* @__PURE__ */ jsxs(File, {
|
|
@@ -655,7 +752,7 @@ const mcpGenerator = defineGenerator({
|
|
|
655
752
|
isTypeOnly: true
|
|
656
753
|
}),
|
|
657
754
|
/* @__PURE__ */ jsx(File.Import, {
|
|
658
|
-
name: "
|
|
755
|
+
name: "client",
|
|
659
756
|
path: client.importPath
|
|
660
757
|
}),
|
|
661
758
|
client.dataReturnType === "full" && /* @__PURE__ */ jsx(File.Import, {
|
|
@@ -671,18 +768,18 @@ const mcpGenerator = defineGenerator({
|
|
|
671
768
|
"ResponseErrorConfig"
|
|
672
769
|
],
|
|
673
770
|
root: meta.file.path,
|
|
674
|
-
path: path.resolve(root, ".kubb/
|
|
771
|
+
path: path.resolve(root, ".kubb/client.ts"),
|
|
675
772
|
isTypeOnly: true
|
|
676
773
|
}),
|
|
677
774
|
/* @__PURE__ */ jsx(File.Import, {
|
|
678
|
-
name: ["
|
|
775
|
+
name: ["client"],
|
|
679
776
|
root: meta.file.path,
|
|
680
|
-
path: path.resolve(root, ".kubb/
|
|
777
|
+
path: path.resolve(root, ".kubb/client.ts")
|
|
681
778
|
}),
|
|
682
779
|
client.dataReturnType === "full" && /* @__PURE__ */ jsx(File.Import, {
|
|
683
780
|
name: ["ResponseConfig"],
|
|
684
781
|
root: meta.file.path,
|
|
685
|
-
path: path.resolve(root, ".kubb/
|
|
782
|
+
path: path.resolve(root, ".kubb/client.ts"),
|
|
686
783
|
isTypeOnly: true
|
|
687
784
|
})
|
|
688
785
|
] }),
|
|
@@ -709,9 +806,9 @@ const mcpGenerator = defineGenerator({
|
|
|
709
806
|
*/
|
|
710
807
|
const serverGenerator = defineGenerator({
|
|
711
808
|
name: "operations",
|
|
712
|
-
renderer:
|
|
809
|
+
renderer: jsxRendererSync,
|
|
713
810
|
operations(nodes, ctx) {
|
|
714
|
-
const {
|
|
811
|
+
const { config, resolver, plugin, driver, root } = ctx;
|
|
715
812
|
const { output, paramsCasing, group } = ctx.options;
|
|
716
813
|
const pluginZod = driver.getPlugin(pluginZodName);
|
|
717
814
|
if (!pluginZod) return;
|
|
@@ -727,11 +824,8 @@ const serverGenerator = defineGenerator({
|
|
|
727
824
|
path: path.resolve(root, output.path, ".mcp.json"),
|
|
728
825
|
meta: { pluginName: plugin.name }
|
|
729
826
|
};
|
|
730
|
-
const operationsMapped = nodes.map((node) => {
|
|
731
|
-
const
|
|
732
|
-
const pathParams = casedParams.filter((p) => p.in === "path");
|
|
733
|
-
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
734
|
-
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
827
|
+
const operationsMapped = nodes.filter(ast.isHttpOperationNode).map((node) => {
|
|
828
|
+
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing });
|
|
735
829
|
const mcpFile = resolver.resolveFile({
|
|
736
830
|
name: node.operationId,
|
|
737
831
|
extname: ".ts",
|
|
@@ -740,7 +834,7 @@ const serverGenerator = defineGenerator({
|
|
|
740
834
|
}, {
|
|
741
835
|
root,
|
|
742
836
|
output,
|
|
743
|
-
group
|
|
837
|
+
group: group ?? void 0
|
|
744
838
|
});
|
|
745
839
|
const zodFile = zodResolver.resolveFile({
|
|
746
840
|
name: node.operationId,
|
|
@@ -750,11 +844,11 @@ const serverGenerator = defineGenerator({
|
|
|
750
844
|
}, {
|
|
751
845
|
root,
|
|
752
846
|
output: pluginZod.options?.output ?? output,
|
|
753
|
-
group: pluginZod.options?.group
|
|
847
|
+
group: pluginZod.options?.group ?? void 0
|
|
754
848
|
});
|
|
755
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) :
|
|
849
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) : null;
|
|
756
850
|
const successStatus = findSuccessStatusCode(node.responses);
|
|
757
|
-
const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) :
|
|
851
|
+
const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) : null;
|
|
758
852
|
const resolveParams = (params) => params.map((p) => ({
|
|
759
853
|
name: p.name,
|
|
760
854
|
schemaName: zodResolver.resolveParamName(node, p)
|
|
@@ -766,13 +860,13 @@ const serverGenerator = defineGenerator({
|
|
|
766
860
|
description: node.description || `Make a ${node.method.toUpperCase()} request to ${node.path}`
|
|
767
861
|
},
|
|
768
862
|
mcp: {
|
|
769
|
-
name: resolver.
|
|
863
|
+
name: resolver.resolveHandlerName(node),
|
|
770
864
|
file: mcpFile
|
|
771
865
|
},
|
|
772
866
|
zod: {
|
|
773
867
|
pathParams: resolveParams(pathParams),
|
|
774
|
-
queryParams: queryParams.length ? resolveParams(queryParams) :
|
|
775
|
-
headerParams: headerParams.length ? resolveParams(headerParams) :
|
|
868
|
+
queryParams: queryParams.length ? resolveParams(queryParams) : null,
|
|
869
|
+
headerParams: headerParams.length ? resolveParams(headerParams) : null,
|
|
776
870
|
requestName,
|
|
777
871
|
responseName,
|
|
778
872
|
file: zodFile
|
|
@@ -787,7 +881,7 @@ const serverGenerator = defineGenerator({
|
|
|
787
881
|
...(zod.headerParams ?? []).map((p) => p.schemaName),
|
|
788
882
|
zod.requestName,
|
|
789
883
|
zod.responseName
|
|
790
|
-
].filter(Boolean);
|
|
884
|
+
].filter((name) => Boolean(name));
|
|
791
885
|
const uniqueNames = [...new Set(zodNames)].sort();
|
|
792
886
|
return [/* @__PURE__ */ jsx(File.Import, {
|
|
793
887
|
name: [mcp.name],
|
|
@@ -803,13 +897,21 @@ const serverGenerator = defineGenerator({
|
|
|
803
897
|
baseName: serverFile.baseName,
|
|
804
898
|
path: serverFile.path,
|
|
805
899
|
meta: serverFile.meta,
|
|
806
|
-
banner: resolver.resolveBanner(
|
|
900
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
807
901
|
output,
|
|
808
|
-
config
|
|
902
|
+
config,
|
|
903
|
+
file: {
|
|
904
|
+
path: serverFile.path,
|
|
905
|
+
baseName: serverFile.baseName
|
|
906
|
+
}
|
|
809
907
|
}),
|
|
810
|
-
footer: resolver.resolveFooter(
|
|
908
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
811
909
|
output,
|
|
812
|
-
config
|
|
910
|
+
config,
|
|
911
|
+
file: {
|
|
912
|
+
path: serverFile.path,
|
|
913
|
+
baseName: serverFile.baseName
|
|
914
|
+
}
|
|
813
915
|
}),
|
|
814
916
|
children: [
|
|
815
917
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -827,8 +929,8 @@ const serverGenerator = defineGenerator({
|
|
|
827
929
|
imports,
|
|
828
930
|
/* @__PURE__ */ jsx(Server, {
|
|
829
931
|
name,
|
|
830
|
-
serverName:
|
|
831
|
-
serverVersion:
|
|
932
|
+
serverName: ctx.meta.title ?? "server",
|
|
933
|
+
serverVersion: ctx.meta.version ?? "0.0.0",
|
|
832
934
|
paramsCasing,
|
|
833
935
|
operations: operationsMapped
|
|
834
936
|
})
|
|
@@ -842,7 +944,7 @@ const serverGenerator = defineGenerator({
|
|
|
842
944
|
children: `
|
|
843
945
|
{
|
|
844
946
|
"mcpServers": {
|
|
845
|
-
"${
|
|
947
|
+
"${ctx.meta.title || "server"}": {
|
|
846
948
|
"type": "stdio",
|
|
847
949
|
"command": "npx",
|
|
848
950
|
"args": ["tsx", "${path.relative(path.dirname(jsonFile.path), serverFile.path)}"]
|
|
@@ -857,14 +959,18 @@ const serverGenerator = defineGenerator({
|
|
|
857
959
|
//#endregion
|
|
858
960
|
//#region src/resolvers/resolverMcp.ts
|
|
859
961
|
/**
|
|
860
|
-
*
|
|
962
|
+
* Default resolver used by `@kubb/plugin-mcp`. Decides the names and file
|
|
963
|
+
* paths for every generated MCP tool handler. Function names get a `Handler`
|
|
964
|
+
* suffix so an operation `addPet` becomes `addPetHandler`.
|
|
861
965
|
*
|
|
862
|
-
*
|
|
966
|
+
* @example Resolve a handler name
|
|
967
|
+
* ```ts
|
|
968
|
+
* import { resolverMcp } from '@kubb/plugin-mcp'
|
|
863
969
|
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
970
|
+
* resolverMcp.default('addPet', 'function') // 'addPetHandler'
|
|
971
|
+
* ```
|
|
866
972
|
*/
|
|
867
|
-
const resolverMcp = defineResolver((
|
|
973
|
+
const resolverMcp = defineResolver(() => ({
|
|
868
974
|
name: "default",
|
|
869
975
|
pluginName: "plugin-mcp",
|
|
870
976
|
default(name, type) {
|
|
@@ -872,12 +978,50 @@ const resolverMcp = defineResolver((ctx) => ({
|
|
|
872
978
|
return camelCase(name, { suffix: "handler" });
|
|
873
979
|
},
|
|
874
980
|
resolveName(name) {
|
|
875
|
-
return
|
|
981
|
+
return this.default(name, "function");
|
|
982
|
+
},
|
|
983
|
+
resolvePathName(name, type) {
|
|
984
|
+
return this.default(name, type);
|
|
985
|
+
},
|
|
986
|
+
resolveHandlerName(node) {
|
|
987
|
+
return this.resolveName(node.operationId);
|
|
876
988
|
}
|
|
877
989
|
}));
|
|
878
990
|
//#endregion
|
|
879
991
|
//#region src/plugin.ts
|
|
992
|
+
/**
|
|
993
|
+
* Canonical plugin name for `@kubb/plugin-mcp`. Used for driver lookups and
|
|
994
|
+
* cross-plugin dependency references.
|
|
995
|
+
*/
|
|
880
996
|
const pluginMcpName = "plugin-mcp";
|
|
997
|
+
/**
|
|
998
|
+
* Generates a Model Context Protocol (MCP) server from an OpenAPI spec. Every
|
|
999
|
+
* operation becomes a typed MCP tool that AI assistants (Claude Desktop, Claude
|
|
1000
|
+
* Code, MCP-compatible clients) can call directly.
|
|
1001
|
+
*
|
|
1002
|
+
* @example
|
|
1003
|
+
* ```ts
|
|
1004
|
+
* import { defineConfig } from 'kubb'
|
|
1005
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1006
|
+
* import { pluginClient } from '@kubb/plugin-client'
|
|
1007
|
+
* import { pluginZod } from '@kubb/plugin-zod'
|
|
1008
|
+
* import { pluginMcp } from '@kubb/plugin-mcp'
|
|
1009
|
+
*
|
|
1010
|
+
* export default defineConfig({
|
|
1011
|
+
* input: { path: './petStore.yaml' },
|
|
1012
|
+
* output: { path: './src/gen' },
|
|
1013
|
+
* plugins: [
|
|
1014
|
+
* pluginTs(),
|
|
1015
|
+
* pluginClient(),
|
|
1016
|
+
* pluginZod(),
|
|
1017
|
+
* pluginMcp({
|
|
1018
|
+
* output: { path: './mcp' },
|
|
1019
|
+
* client: { baseURL: 'https://petstore.swagger.io/v2' },
|
|
1020
|
+
* }),
|
|
1021
|
+
* ],
|
|
1022
|
+
* })
|
|
1023
|
+
* ```
|
|
1024
|
+
*/
|
|
881
1025
|
const pluginMcp = definePlugin((options) => {
|
|
882
1026
|
const { output = {
|
|
883
1027
|
path: "mcp",
|
|
@@ -885,13 +1029,7 @@ const pluginMcp = definePlugin((options) => {
|
|
|
885
1029
|
}, group, exclude = [], include, override = [], paramsCasing, client, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
|
|
886
1030
|
const clientName = client?.client ?? "axios";
|
|
887
1031
|
const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : void 0);
|
|
888
|
-
const groupConfig = group
|
|
889
|
-
...group,
|
|
890
|
-
name: group.name ? group.name : (ctx) => {
|
|
891
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
892
|
-
return `${camelCase(ctx.group)}Requests`;
|
|
893
|
-
}
|
|
894
|
-
} : void 0;
|
|
1032
|
+
const groupConfig = createGroupConfig(group, { suffix: "Requests" });
|
|
895
1033
|
return {
|
|
896
1034
|
name: pluginMcpName,
|
|
897
1035
|
options,
|
|
@@ -927,10 +1065,10 @@ const pluginMcp = definePlugin((options) => {
|
|
|
927
1065
|
const root = path.resolve(ctx.config.root, ctx.config.output.path);
|
|
928
1066
|
const hasClientPlugin = ctx.config.plugins?.some((p) => p.name === pluginClientName);
|
|
929
1067
|
if (client?.bundle && !hasClientPlugin && !clientImportPath) ctx.injectFile({
|
|
930
|
-
baseName: "
|
|
931
|
-
path: path.resolve(root, ".kubb/
|
|
1068
|
+
baseName: "client.ts",
|
|
1069
|
+
path: path.resolve(root, ".kubb/client.ts"),
|
|
932
1070
|
sources: [ast.createSource({
|
|
933
|
-
name: "
|
|
1071
|
+
name: "client",
|
|
934
1072
|
nodes: [ast.createText(clientName === "fetch" ? source$1 : source)],
|
|
935
1073
|
isExportable: true,
|
|
936
1074
|
isIndexable: true
|