@kubb/plugin-mcp 5.0.0-beta.85 → 5.0.0-beta.87
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 +60 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +13 -16
- package/dist/index.js +60 -86
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -24,12 +24,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
enumerable: true
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
|
-
let node_path = require("node:path");
|
|
28
|
-
node_path = __toESM(node_path, 1);
|
|
29
27
|
let kubb_kit = require("kubb/kit");
|
|
30
28
|
let kubb_jsx = require("kubb/jsx");
|
|
31
29
|
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
32
30
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
31
|
+
let node_path = require("node:path");
|
|
32
|
+
node_path = __toESM(node_path, 1);
|
|
33
33
|
let _kubb_plugin_zod = require("@kubb/plugin-zod");
|
|
34
34
|
//#region ../../internals/utils/src/casing.ts
|
|
35
35
|
/**
|
|
@@ -58,31 +58,6 @@ function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
58
58
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
59
59
|
}
|
|
60
60
|
//#endregion
|
|
61
|
-
//#region ../../internals/utils/src/fs.ts
|
|
62
|
-
/**
|
|
63
|
-
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
64
|
-
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
65
|
-
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
66
|
-
*
|
|
67
|
-
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
68
|
-
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
69
|
-
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
70
|
-
* absolute path, letting generated files escape the configured output directory.
|
|
71
|
-
*
|
|
72
|
-
* @example Nested path from a dotted name
|
|
73
|
-
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
74
|
-
*
|
|
75
|
-
* @example PascalCase the final segment
|
|
76
|
-
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
77
|
-
*
|
|
78
|
-
* @example Suffix applied to the final segment only
|
|
79
|
-
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
80
|
-
*/
|
|
81
|
-
function toFilePath(name, caseLast = camelCase) {
|
|
82
|
-
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
83
|
-
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
86
61
|
//#region ../../internals/utils/src/reserved.ts
|
|
87
62
|
/**
|
|
88
63
|
* JavaScript and Java reserved words.
|
|
@@ -183,6 +158,22 @@ const reservedWords = /* @__PURE__ */ new Set([
|
|
|
183
158
|
*/
|
|
184
159
|
function isValidVarName(name) {
|
|
185
160
|
if (!name || reservedWords.has(name)) return false;
|
|
161
|
+
return isIdentifier(name);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
165
|
+
*
|
|
166
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
167
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
168
|
+
* deciding whether an object key needs quoting.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```ts
|
|
172
|
+
* isIdentifier('name') // true
|
|
173
|
+
* isIdentifier('x-total')// false
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
function isIdentifier(name) {
|
|
186
177
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
187
178
|
}
|
|
188
179
|
//#endregion
|
|
@@ -338,13 +329,13 @@ function dedupeByCasedName(params) {
|
|
|
338
329
|
//#region ../../internals/shared/src/operation.ts
|
|
339
330
|
/**
|
|
340
331
|
* Builds the `ResolverFileParams` every operation generator passes to
|
|
341
|
-
* `resolver.
|
|
332
|
+
* `resolver.file`: a file named `name`, tagged by the operation's first
|
|
342
333
|
* tag (or `'default'`), at the operation's path. Centralizes the entry object
|
|
343
334
|
* that was repeated at dozens of call sites across the client and query plugins.
|
|
344
335
|
*
|
|
345
336
|
* @example
|
|
346
337
|
* ```ts
|
|
347
|
-
* resolver.
|
|
338
|
+
* resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
|
|
348
339
|
* ```
|
|
349
340
|
*/
|
|
350
341
|
function operationFileEntry(node, name, extname = ".ts") {
|
|
@@ -361,8 +352,11 @@ function getOperationLink(node, link) {
|
|
|
361
352
|
if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
|
|
362
353
|
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
363
354
|
}
|
|
364
|
-
|
|
365
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
|
|
357
|
+
* are present and the union, default, and form-data flags the client uses to pick one.
|
|
358
|
+
*/
|
|
359
|
+
function buildContentTypeInfo(contentTypes) {
|
|
366
360
|
const isMultipleContentTypes = contentTypes.length > 1;
|
|
367
361
|
return {
|
|
368
362
|
contentTypes,
|
|
@@ -372,20 +366,15 @@ function getContentTypeInfo(node) {
|
|
|
372
366
|
hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
|
|
373
367
|
};
|
|
374
368
|
}
|
|
369
|
+
function getContentTypeInfo(node) {
|
|
370
|
+
return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
|
|
371
|
+
}
|
|
375
372
|
/**
|
|
376
373
|
* The request-body counterpart for the primary success response: the content types it documents and
|
|
377
374
|
* whether several are present, so the client can let a caller pick which one to accept.
|
|
378
375
|
*/
|
|
379
376
|
function getResponseContentTypeInfo(node) {
|
|
380
|
-
|
|
381
|
-
const isMultipleContentTypes = contentTypes.length > 1;
|
|
382
|
-
return {
|
|
383
|
-
contentTypes,
|
|
384
|
-
isMultipleContentTypes,
|
|
385
|
-
contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
|
|
386
|
-
defaultContentType: contentTypes[0] ?? "application/json",
|
|
387
|
-
hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
|
|
388
|
-
};
|
|
377
|
+
return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
|
|
389
378
|
}
|
|
390
379
|
function buildRequestConfigType(node) {
|
|
391
380
|
const request = getContentTypeInfo(node);
|
|
@@ -427,7 +416,7 @@ function getRequestGroupOptionality(node) {
|
|
|
427
416
|
}
|
|
428
417
|
/**
|
|
429
418
|
* Builds the grouped `{ path, query, body, headers }` parameter for a generated client
|
|
430
|
-
* function, typed from the operation's `
|
|
419
|
+
* function, typed from the operation's `Options` (minus `url`). Only the groups the
|
|
431
420
|
* operation actually has are destructured. The trailing `config` parameter carries the
|
|
432
421
|
* runtime `RequestConfig` overrides plus `client`.
|
|
433
422
|
*/
|
|
@@ -441,7 +430,7 @@ function buildRequestParamsSignature(node, resolver, options = {}) {
|
|
|
441
430
|
"headers"
|
|
442
431
|
].filter((key) => groups[key]);
|
|
443
432
|
return {
|
|
444
|
-
signature: [names.length > 0 ? `{ ${names.join(", ")} }: ${resolver.
|
|
433
|
+
signature: [names.length > 0 ? `{ ${names.join(", ")} }: ${resolver.response.options(node)}${isOptional ? " = {}" : ""}` : null, isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null].filter(Boolean).join(", "),
|
|
445
434
|
groups
|
|
446
435
|
};
|
|
447
436
|
}
|
|
@@ -770,15 +759,15 @@ function resolveClientOperation(options) {
|
|
|
770
759
|
const { clientPlugin, driver, node, root, output } = options;
|
|
771
760
|
if (!clientPlugin) return null;
|
|
772
761
|
const resolver = driver.getResolver(clientPlugin.pluginName);
|
|
773
|
-
if (!resolver) return null;
|
|
774
762
|
const plugin = driver.getPlugin(clientPlugin.pluginName);
|
|
775
|
-
const file = resolver.
|
|
763
|
+
const file = resolver.file({
|
|
764
|
+
...operationFileEntry(node, node.operationId),
|
|
776
765
|
root,
|
|
777
766
|
output: plugin?.options?.output ?? output,
|
|
778
767
|
group: plugin?.options?.group ?? void 0
|
|
779
768
|
});
|
|
780
769
|
return {
|
|
781
|
-
name: resolver.
|
|
770
|
+
name: resolver.name(node.operationId),
|
|
782
771
|
path: file.path,
|
|
783
772
|
clientPath: node_path.default.resolve(root, ".kubb/client.ts")
|
|
784
773
|
};
|
|
@@ -809,25 +798,23 @@ const mcpGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
809
798
|
output
|
|
810
799
|
});
|
|
811
800
|
if (!contractOp) return null;
|
|
812
|
-
const
|
|
801
|
+
const optionsName = tsResolver.response.options(node);
|
|
813
802
|
const meta = {
|
|
814
|
-
name: resolver.
|
|
815
|
-
file: resolver.
|
|
803
|
+
name: resolver.handler.name(node),
|
|
804
|
+
file: resolver.file({
|
|
816
805
|
name: node.operationId,
|
|
817
806
|
extname: ".ts",
|
|
818
807
|
tag: node.tags[0] ?? "default",
|
|
819
|
-
path: node.path
|
|
820
|
-
}, {
|
|
808
|
+
path: node.path,
|
|
821
809
|
root,
|
|
822
810
|
output,
|
|
823
811
|
group: group ?? void 0
|
|
824
812
|
}),
|
|
825
|
-
fileTs: tsResolver.
|
|
813
|
+
fileTs: tsResolver.file({
|
|
826
814
|
name: node.operationId,
|
|
827
815
|
extname: ".ts",
|
|
828
816
|
tag: node.tags[0] ?? "default",
|
|
829
|
-
path: node.path
|
|
830
|
-
}, {
|
|
817
|
+
path: node.path,
|
|
831
818
|
root,
|
|
832
819
|
output: pluginTs.options?.output ?? output,
|
|
833
820
|
group: pluginTs.options?.group ?? void 0
|
|
@@ -838,8 +825,8 @@ const mcpGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
838
825
|
path: meta.file.path,
|
|
839
826
|
meta: meta.file.meta,
|
|
840
827
|
children: [
|
|
841
|
-
meta.fileTs &&
|
|
842
|
-
name: [
|
|
828
|
+
meta.fileTs && optionsName && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
829
|
+
name: [optionsName],
|
|
843
830
|
root: meta.file.path,
|
|
844
831
|
path: meta.fileTs.path,
|
|
845
832
|
isTypeOnly: true
|
|
@@ -904,32 +891,30 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
904
891
|
};
|
|
905
892
|
const operationsMapped = nodes.filter(kubb_kit.ast.isHttpOperationNode).map((node) => {
|
|
906
893
|
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
907
|
-
const mcpFile = resolver.
|
|
894
|
+
const mcpFile = resolver.file({
|
|
908
895
|
name: node.operationId,
|
|
909
896
|
extname: ".ts",
|
|
910
897
|
tag: node.tags[0] ?? "default",
|
|
911
|
-
path: node.path
|
|
912
|
-
}, {
|
|
898
|
+
path: node.path,
|
|
913
899
|
root,
|
|
914
900
|
output,
|
|
915
901
|
group: group ?? void 0
|
|
916
902
|
});
|
|
917
|
-
const zodFile = zodResolver.
|
|
903
|
+
const zodFile = zodResolver.file({
|
|
918
904
|
name: node.operationId,
|
|
919
905
|
extname: ".ts",
|
|
920
906
|
tag: node.tags[0] ?? "default",
|
|
921
|
-
path: node.path
|
|
922
|
-
}, {
|
|
907
|
+
path: node.path,
|
|
923
908
|
root,
|
|
924
909
|
output: pluginZod.options?.output ?? output,
|
|
925
910
|
group: pluginZod.options?.group ?? void 0
|
|
926
911
|
});
|
|
927
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.
|
|
912
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null;
|
|
928
913
|
const successStatus = findSuccessStatusCode(node.responses);
|
|
929
|
-
const responseName = successStatus ? zodResolver.
|
|
914
|
+
const responseName = successStatus ? zodResolver.response.status(node, successStatus) : null;
|
|
930
915
|
const resolveParams = (params) => params.map((p) => ({
|
|
931
916
|
name: p.name,
|
|
932
|
-
schemaName: zodResolver.
|
|
917
|
+
schemaName: zodResolver.param.name(node, p)
|
|
933
918
|
}));
|
|
934
919
|
return {
|
|
935
920
|
tool: {
|
|
@@ -938,7 +923,7 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
938
923
|
description: node.description || `Make a ${node.method.toUpperCase()} request to ${node.path}`
|
|
939
924
|
},
|
|
940
925
|
mcp: {
|
|
941
|
-
name: resolver.
|
|
926
|
+
name: resolver.handler.name(node),
|
|
942
927
|
file: mcpFile
|
|
943
928
|
},
|
|
944
929
|
zod: {
|
|
@@ -975,7 +960,7 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
975
960
|
baseName: serverFile.baseName,
|
|
976
961
|
path: serverFile.path,
|
|
977
962
|
meta: serverFile.meta,
|
|
978
|
-
banner: resolver.
|
|
963
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
979
964
|
output,
|
|
980
965
|
config,
|
|
981
966
|
file: {
|
|
@@ -983,7 +968,7 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
983
968
|
baseName: serverFile.baseName
|
|
984
969
|
}
|
|
985
970
|
}),
|
|
986
|
-
footer: resolver.
|
|
971
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
987
972
|
output,
|
|
988
973
|
config,
|
|
989
974
|
file: {
|
|
@@ -1044,26 +1029,18 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1044
1029
|
* ```ts
|
|
1045
1030
|
* import { resolverMcp } from '@kubb/plugin-mcp'
|
|
1046
1031
|
*
|
|
1047
|
-
* resolverMcp.
|
|
1032
|
+
* resolverMcp.name('addPet') // 'addPetHandler'
|
|
1048
1033
|
* ```
|
|
1049
1034
|
*/
|
|
1050
|
-
const resolverMcp = (0, kubb_kit.
|
|
1051
|
-
name: "default",
|
|
1035
|
+
const resolverMcp = (0, kubb_kit.createResolver)({
|
|
1052
1036
|
pluginName: "plugin-mcp",
|
|
1053
|
-
|
|
1054
|
-
if (type === "file") return toFilePath(name);
|
|
1037
|
+
name(name) {
|
|
1055
1038
|
return camelCase(name, { suffix: "handler" });
|
|
1056
1039
|
},
|
|
1057
|
-
|
|
1058
|
-
return this.
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
return this.default(name, type);
|
|
1062
|
-
},
|
|
1063
|
-
resolveHandlerName(node) {
|
|
1064
|
-
return this.resolveName(node.operationId);
|
|
1065
|
-
}
|
|
1066
|
-
}));
|
|
1040
|
+
handler: { name(node) {
|
|
1041
|
+
return this.name(node.operationId);
|
|
1042
|
+
} }
|
|
1043
|
+
});
|
|
1067
1044
|
//#endregion
|
|
1068
1045
|
//#region src/plugin.ts
|
|
1069
1046
|
/**
|
|
@@ -1109,10 +1086,7 @@ const pluginMcp = (0, kubb_kit.definePlugin)((options) => {
|
|
|
1109
1086
|
options,
|
|
1110
1087
|
dependencies: [_kubb_plugin_ts.pluginTsName, _kubb_plugin_zod.pluginZodName],
|
|
1111
1088
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1112
|
-
const resolver = userResolver ?
|
|
1113
|
-
...resolverMcp,
|
|
1114
|
-
...userResolver
|
|
1115
|
-
} : resolverMcp;
|
|
1089
|
+
const resolver = userResolver ? kubb_kit.Resolver.merge(resolverMcp, userResolver) : resolverMcp;
|
|
1116
1090
|
const resolvedClient = resolveClient({
|
|
1117
1091
|
client,
|
|
1118
1092
|
pluginNames: (ctx.config.plugins ?? []).map((p) => p.name).filter((name) => Boolean(name))
|