@kubb/plugin-mcp 5.0.0-beta.42 → 5.0.0-beta.64
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/dist/index.cjs +116 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -14
- package/dist/index.js +117 -180
- package/dist/index.js.map +1 -1
- package/package.json +10 -18
- package/extension.yaml +0 -943
- package/src/components/McpHandler.tsx +0 -168
- package/src/components/Server.tsx +0 -159
- package/src/generators/mcpGenerator.tsx +0 -89
- package/src/generators/serverGenerator.tsx +0 -149
- package/src/index.ts +0 -11
- package/src/plugin.ts +0 -142
- package/src/resolvers/resolverMcp.ts +0 -35
- package/src/types.ts +0 -94
- package/src/utils.ts +0 -48
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import "./chunk-C0LytTxp.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
4
|
+
import { caseParams, createOperationParams } from "@kubb/ast/utils";
|
|
4
5
|
import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
5
|
-
import { Const, File, Function,
|
|
6
|
+
import { Const, File, Function, jsxRenderer } from "@kubb/renderer-jsx";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
7
8
|
import { pluginZodName } from "@kubb/plugin-zod";
|
|
8
9
|
import { pluginClientName } from "@kubb/plugin-client";
|
|
@@ -20,36 +21,45 @@ import { source as source$2 } from "@kubb/plugin-client/templates/config.source"
|
|
|
20
21
|
function toCamelOrPascal(text, pascal) {
|
|
21
22
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
22
23
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
23
|
-
|
|
24
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
24
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
25
25
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Converts `text` to camelCase.
|
|
29
|
+
*
|
|
30
|
+
* @example Word boundaries
|
|
31
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
31
32
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* @example With a prefix
|
|
34
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
34
35
|
*/
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
36
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
37
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
38
38
|
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region ../../internals/utils/src/fs.ts
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
43
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
44
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
42
45
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
47
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
48
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
49
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
50
|
+
*
|
|
51
|
+
* @example Nested path from a dotted name
|
|
52
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
53
|
+
*
|
|
54
|
+
* @example PascalCase the final segment
|
|
55
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
56
|
+
*
|
|
57
|
+
* @example Suffix applied to the final segment only
|
|
58
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
46
59
|
*/
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
suffix
|
|
51
|
-
} : {}));
|
|
52
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
60
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
61
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
62
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
53
63
|
}
|
|
54
64
|
//#endregion
|
|
55
65
|
//#region ../../internals/utils/src/reserved.ts
|
|
@@ -155,99 +165,80 @@ function isValidVarName(name) {
|
|
|
155
165
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
156
166
|
}
|
|
157
167
|
//#endregion
|
|
158
|
-
//#region ../../internals/utils/src/
|
|
168
|
+
//#region ../../internals/utils/src/url.ts
|
|
169
|
+
function transformParam(raw, casing) {
|
|
170
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
171
|
+
return casing === "camelcase" ? camelCase(param) : param;
|
|
172
|
+
}
|
|
173
|
+
function toParamsObject(path, { replacer, casing } = {}) {
|
|
174
|
+
const params = {};
|
|
175
|
+
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
176
|
+
const param = transformParam(match[1], casing);
|
|
177
|
+
const key = replacer ? replacer(param) : param;
|
|
178
|
+
params[key] = key;
|
|
179
|
+
}
|
|
180
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
181
|
+
}
|
|
159
182
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* const p = new URLPath('/pet/{petId}')
|
|
164
|
-
* p.URL // '/pet/:petId'
|
|
165
|
-
* p.template // '`/pet/${petId}`'
|
|
183
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
166
184
|
*/
|
|
167
|
-
var
|
|
185
|
+
var Url = class Url {
|
|
168
186
|
/**
|
|
169
|
-
*
|
|
170
|
-
*/
|
|
171
|
-
path;
|
|
172
|
-
#options;
|
|
173
|
-
constructor(path, options = {}) {
|
|
174
|
-
this.path = path;
|
|
175
|
-
this.#options = options;
|
|
176
|
-
}
|
|
177
|
-
/** Converts the OpenAPI path to Express-style colon syntax, e.g. `/pet/{petId}` → `/pet/:petId`.
|
|
187
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
178
188
|
*
|
|
179
189
|
* @example
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* ```
|
|
190
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
191
|
+
* Url.canParse('/pet/{petId}') // false
|
|
183
192
|
*/
|
|
184
|
-
|
|
185
|
-
return
|
|
193
|
+
static canParse(url, base) {
|
|
194
|
+
return URL.canParse(url, base);
|
|
186
195
|
}
|
|
187
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
188
198
|
*
|
|
189
199
|
* @example
|
|
190
|
-
*
|
|
191
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
192
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
193
|
-
* ```
|
|
200
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
194
201
|
*/
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return !!new URL(this.path).href;
|
|
198
|
-
} catch {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
202
|
+
static toPath(path) {
|
|
203
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
201
204
|
}
|
|
202
205
|
/**
|
|
203
|
-
* Converts
|
|
206
|
+
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
207
|
+
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
208
|
+
* and `casing` controls parameter identifier casing.
|
|
204
209
|
*
|
|
205
210
|
* @example
|
|
206
|
-
*
|
|
207
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
208
|
-
*/
|
|
209
|
-
get template() {
|
|
210
|
-
return this.toTemplateString();
|
|
211
|
-
}
|
|
212
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
211
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
213
212
|
*
|
|
214
213
|
* @example
|
|
215
|
-
*
|
|
216
|
-
* new URLPath('/pet/{petId}').object
|
|
217
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
218
|
-
* ```
|
|
214
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
219
215
|
*/
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
217
|
+
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
218
|
+
if (i % 2 === 0) return part;
|
|
219
|
+
const param = transformParam(part, casing);
|
|
220
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
221
|
+
}).join("");
|
|
222
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
222
223
|
}
|
|
223
|
-
/**
|
|
224
|
+
/**
|
|
225
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
226
|
+
* expression when `stringify` is set.
|
|
224
227
|
*
|
|
225
228
|
* @example
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* new URLPath('/pet').params // null
|
|
229
|
-
* ```
|
|
230
|
-
*/
|
|
231
|
-
get params() {
|
|
232
|
-
return this.toParamsObject();
|
|
233
|
-
}
|
|
234
|
-
#transformParam(raw) {
|
|
235
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
236
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
229
|
+
* Url.toObject('/pet/{petId}')
|
|
230
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
240
231
|
*/
|
|
241
|
-
|
|
242
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
243
|
-
const raw = match[1];
|
|
244
|
-
fn(raw, this.#transformParam(raw));
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
232
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
248
233
|
const object = {
|
|
249
|
-
url: type === "path" ?
|
|
250
|
-
|
|
234
|
+
url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
|
|
235
|
+
replacer,
|
|
236
|
+
casing
|
|
237
|
+
}),
|
|
238
|
+
params: toParamsObject(path, {
|
|
239
|
+
replacer,
|
|
240
|
+
casing
|
|
241
|
+
})
|
|
251
242
|
};
|
|
252
243
|
if (stringify) {
|
|
253
244
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -256,57 +247,13 @@ var URLPath = class {
|
|
|
256
247
|
}
|
|
257
248
|
return object;
|
|
258
249
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
261
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
265
|
-
*/
|
|
266
|
-
toTemplateString({ prefix, replacer } = {}) {
|
|
267
|
-
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
268
|
-
if (i % 2 === 0) return part;
|
|
269
|
-
const param = this.#transformParam(part);
|
|
270
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
271
|
-
}).join("");
|
|
272
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
276
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
277
|
-
* Returns `undefined` when no path parameters are found.
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* ```ts
|
|
281
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
282
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
283
|
-
* ```
|
|
284
|
-
*/
|
|
285
|
-
toParamsObject(replacer) {
|
|
286
|
-
const params = {};
|
|
287
|
-
this.#eachParam((_raw, param) => {
|
|
288
|
-
const key = replacer ? replacer(param) : param;
|
|
289
|
-
params[key] = key;
|
|
290
|
-
});
|
|
291
|
-
return Object.keys(params).length > 0 ? params : null;
|
|
292
|
-
}
|
|
293
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
294
|
-
*
|
|
295
|
-
* @example
|
|
296
|
-
* ```ts
|
|
297
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
298
|
-
* ```
|
|
299
|
-
*/
|
|
300
|
-
toURLPath() {
|
|
301
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
302
|
-
}
|
|
303
250
|
};
|
|
304
251
|
//#endregion
|
|
305
252
|
//#region ../../internals/shared/src/operation.ts
|
|
306
253
|
function getOperationLink(node, link) {
|
|
307
254
|
if (!link) return null;
|
|
308
255
|
if (typeof link === "function") return link(node) ?? null;
|
|
309
|
-
if (link === "urlPath") return node.path ? `{@link ${
|
|
256
|
+
if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
|
|
310
257
|
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
311
258
|
}
|
|
312
259
|
function buildOperationComments(node, options = {}) {
|
|
@@ -327,7 +274,7 @@ function buildOperationComments(node, options = {}) {
|
|
|
327
274
|
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
328
275
|
}
|
|
329
276
|
function getOperationParameters(node, options = {}) {
|
|
330
|
-
const params =
|
|
277
|
+
const params = caseParams(node.parameters, options.paramsCasing);
|
|
331
278
|
return {
|
|
332
279
|
path: params.filter((param) => param.in === "path"),
|
|
333
280
|
query: params.filter((param) => param.in === "query"),
|
|
@@ -396,26 +343,24 @@ function findSuccessStatusCode(responses) {
|
|
|
396
343
|
* shared default naming so every plugin groups output consistently:
|
|
397
344
|
*
|
|
398
345
|
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
399
|
-
* - other groups use
|
|
346
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
400
347
|
*
|
|
401
348
|
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
402
349
|
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
403
350
|
* per-plugin convention.
|
|
404
351
|
*
|
|
405
352
|
* @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
353
|
*
|
|
408
354
|
* @example
|
|
409
355
|
* ```ts
|
|
410
|
-
* createGroupConfig(group
|
|
411
|
-
* createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
|
|
356
|
+
* createGroupConfig(group) // shared across every plugin
|
|
412
357
|
* ```
|
|
413
358
|
*/
|
|
414
|
-
function createGroupConfig(group
|
|
359
|
+
function createGroupConfig(group) {
|
|
415
360
|
if (!group) return null;
|
|
416
361
|
const defaultName = (ctx) => {
|
|
417
362
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
418
|
-
return
|
|
363
|
+
return camelCase(ctx.group);
|
|
419
364
|
};
|
|
420
365
|
return {
|
|
421
366
|
...group,
|
|
@@ -452,7 +397,6 @@ function buildRemappingCode(mapping, varName, sourceName) {
|
|
|
452
397
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
453
398
|
function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasing }) {
|
|
454
399
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
455
|
-
const urlPath = new URLPath(node.path);
|
|
456
400
|
const contentType = node.requestBody?.content?.[0]?.contentType;
|
|
457
401
|
const isFormData = contentType === "multipart/form-data";
|
|
458
402
|
const { query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing });
|
|
@@ -465,7 +409,7 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
465
409
|
`ResponseErrorConfig<${errorResponses.length > 0 ? errorResponses.join(" | ") : "Error"}>`,
|
|
466
410
|
requestName || "unknown"
|
|
467
411
|
].filter(Boolean);
|
|
468
|
-
const paramsNode =
|
|
412
|
+
const paramsNode = createOperationParams(node, {
|
|
469
413
|
paramsType: "object",
|
|
470
414
|
pathParamsType: "inline",
|
|
471
415
|
resolver,
|
|
@@ -480,28 +424,20 @@ function McpHandler({ name, node, resolver, baseURL, dataReturnType, paramsCasin
|
|
|
480
424
|
const headers = [headerParams.length ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null, contentTypeHeader].filter(Boolean);
|
|
481
425
|
const fetchConfig = [];
|
|
482
426
|
fetchConfig.push(`method: ${JSON.stringify(node.method.toUpperCase())}`);
|
|
483
|
-
fetchConfig.push(`url: ${
|
|
427
|
+
fetchConfig.push(`url: ${Url.toTemplateString(node.path)}`);
|
|
484
428
|
if (baseURL) fetchConfig.push(`baseURL: \`${baseURL}\``);
|
|
485
429
|
if (queryParams.length) fetchConfig.push(queryParamsMapping ? "params: mappedParams" : "params");
|
|
486
430
|
if (requestName) fetchConfig.push(`data: ${isFormData ? "formData as FormData" : "requestData"}`);
|
|
487
431
|
if (headers.length) fetchConfig.push(`headers: { ${headers.join(", ")} }`);
|
|
488
|
-
const callToolResult =
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
content: [
|
|
498
|
-
{
|
|
499
|
-
type: 'text',
|
|
500
|
-
text: JSON.stringify(res)
|
|
501
|
-
}
|
|
502
|
-
],
|
|
503
|
-
structuredContent: { data: res.data }
|
|
504
|
-
}`;
|
|
432
|
+
const callToolResult = `return {
|
|
433
|
+
content: [
|
|
434
|
+
{
|
|
435
|
+
type: 'text',
|
|
436
|
+
text: JSON.stringify(${dataReturnType === "data" ? "res.data" : "res"})
|
|
437
|
+
}
|
|
438
|
+
],
|
|
439
|
+
structuredContent: { data: res.data }
|
|
440
|
+
}`;
|
|
505
441
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
506
442
|
name,
|
|
507
443
|
isExportable: true,
|
|
@@ -575,7 +511,7 @@ function zodExprFromSchemaNode(schema) {
|
|
|
575
511
|
}
|
|
576
512
|
//#endregion
|
|
577
513
|
//#region src/components/Server.tsx
|
|
578
|
-
const keysPrinter = functionPrinter({ mode: "
|
|
514
|
+
const keysPrinter = functionPrinter({ mode: "call" });
|
|
579
515
|
function Server({ name, serverName, serverVersion, paramsCasing, operations }) {
|
|
580
516
|
const registrations = operations.map(({ tool, mcp, zod, node }) => {
|
|
581
517
|
const { path: pathParams } = getOperationParameters(node, { paramsCasing });
|
|
@@ -602,10 +538,11 @@ function Server({ name, serverName, serverVersion, paramsCasing, operations }) {
|
|
|
602
538
|
});
|
|
603
539
|
otherEntries.sort((a, b) => a.key.localeCompare(b.key));
|
|
604
540
|
const entries = [...pathEntries, ...otherEntries];
|
|
605
|
-
const paramsNode = entries.length ? ast.createFunctionParameters({ params: [
|
|
606
|
-
|
|
541
|
+
const paramsNode = entries.length ? ast.factory.createFunctionParameters({ params: [{
|
|
542
|
+
kind: "FunctionParameter",
|
|
543
|
+
name: ast.factory.createObjectBindingPattern({ elements: entries.map((e) => ({ name: e.key })) }),
|
|
607
544
|
optional: false
|
|
608
|
-
}
|
|
545
|
+
}] }) : null;
|
|
609
546
|
const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
|
|
610
547
|
const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} }` : null;
|
|
611
548
|
const outputSchema = zod.responseName;
|
|
@@ -676,7 +613,7 @@ return server`
|
|
|
676
613
|
*/
|
|
677
614
|
const mcpGenerator = defineGenerator({
|
|
678
615
|
name: "mcp",
|
|
679
|
-
renderer:
|
|
616
|
+
renderer: jsxRenderer,
|
|
680
617
|
operation(node, ctx) {
|
|
681
618
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
682
619
|
const { resolver, driver, root } = ctx;
|
|
@@ -806,7 +743,7 @@ const mcpGenerator = defineGenerator({
|
|
|
806
743
|
*/
|
|
807
744
|
const serverGenerator = defineGenerator({
|
|
808
745
|
name: "operations",
|
|
809
|
-
renderer:
|
|
746
|
+
renderer: jsxRenderer,
|
|
810
747
|
operations(nodes, ctx) {
|
|
811
748
|
const { config, resolver, plugin, driver, root } = ctx;
|
|
812
749
|
const { output, paramsCasing, group } = ctx.options;
|
|
@@ -974,7 +911,7 @@ const resolverMcp = defineResolver(() => ({
|
|
|
974
911
|
name: "default",
|
|
975
912
|
pluginName: "plugin-mcp",
|
|
976
913
|
default(name, type) {
|
|
977
|
-
if (type === "file") return
|
|
914
|
+
if (type === "file") return toFilePath(name);
|
|
978
915
|
return camelCase(name, { suffix: "handler" });
|
|
979
916
|
},
|
|
980
917
|
resolveName(name) {
|
|
@@ -1025,11 +962,11 @@ const pluginMcpName = "plugin-mcp";
|
|
|
1025
962
|
const pluginMcp = definePlugin((options) => {
|
|
1026
963
|
const { output = {
|
|
1027
964
|
path: "mcp",
|
|
1028
|
-
|
|
1029
|
-
}, group, exclude = [], include, override = [], paramsCasing, client, resolver: userResolver,
|
|
965
|
+
barrel: { type: "named" }
|
|
966
|
+
}, group, exclude = [], include, override = [], paramsCasing, client, resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
|
|
1030
967
|
const clientName = client?.client ?? "axios";
|
|
1031
968
|
const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : void 0);
|
|
1032
|
-
const groupConfig = createGroupConfig(group
|
|
969
|
+
const groupConfig = createGroupConfig(group);
|
|
1033
970
|
return {
|
|
1034
971
|
name: pluginMcpName,
|
|
1035
972
|
options,
|
|
@@ -1058,7 +995,7 @@ const pluginMcp = definePlugin((options) => {
|
|
|
1058
995
|
resolver
|
|
1059
996
|
});
|
|
1060
997
|
ctx.setResolver(resolver);
|
|
1061
|
-
if (
|
|
998
|
+
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
1062
999
|
ctx.addGenerator(mcpGenerator);
|
|
1063
1000
|
ctx.addGenerator(serverGenerator);
|
|
1064
1001
|
for (const gen of userGenerators) ctx.addGenerator(gen);
|
|
@@ -1067,9 +1004,9 @@ const pluginMcp = definePlugin((options) => {
|
|
|
1067
1004
|
if (client?.bundle && !hasClientPlugin && !clientImportPath) ctx.injectFile({
|
|
1068
1005
|
baseName: "client.ts",
|
|
1069
1006
|
path: path.resolve(root, ".kubb/client.ts"),
|
|
1070
|
-
sources: [ast.createSource({
|
|
1007
|
+
sources: [ast.factory.createSource({
|
|
1071
1008
|
name: "client",
|
|
1072
|
-
nodes: [ast.createText(clientName === "fetch" ? source$1 : source)],
|
|
1009
|
+
nodes: [ast.factory.createText(clientName === "fetch" ? source$1 : source)],
|
|
1073
1010
|
isExportable: true,
|
|
1074
1011
|
isIndexable: true
|
|
1075
1012
|
})]
|
|
@@ -1077,9 +1014,9 @@ const pluginMcp = definePlugin((options) => {
|
|
|
1077
1014
|
if (!hasClientPlugin) ctx.injectFile({
|
|
1078
1015
|
baseName: "config.ts",
|
|
1079
1016
|
path: path.resolve(root, ".kubb/config.ts"),
|
|
1080
|
-
sources: [ast.createSource({
|
|
1017
|
+
sources: [ast.factory.createSource({
|
|
1081
1018
|
name: "config",
|
|
1082
|
-
nodes: [ast.createText(source$2)],
|
|
1019
|
+
nodes: [ast.factory.createText(source$2)],
|
|
1083
1020
|
isExportable: false,
|
|
1084
1021
|
isIndexable: false
|
|
1085
1022
|
})]
|