@kubb/plugin-mcp 5.2.0 → 5.2.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/dist/index.cjs +71 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -34
- package/dist/index.js +72 -126
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -29,26 +29,8 @@ let node_path = require("node:path");
|
|
|
29
29
|
node_path = __toESM(node_path, 1);
|
|
30
30
|
let kubb_jsx = require("kubb/jsx");
|
|
31
31
|
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
32
|
-
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
33
32
|
let _kubb_plugin_zod = require("@kubb/plugin-zod");
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Drops parameters that share the same name, keeping the first.
|
|
37
|
-
*
|
|
38
|
-
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
39
|
-
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
40
|
-
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
41
|
-
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
42
|
-
*/
|
|
43
|
-
function dedupeParams(params) {
|
|
44
|
-
const seen = /* @__PURE__ */ new Set();
|
|
45
|
-
return params.filter((param) => {
|
|
46
|
-
if (seen.has(param.name)) return false;
|
|
47
|
-
seen.add(param.name);
|
|
48
|
-
return true;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
//#endregion
|
|
33
|
+
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
52
34
|
//#region ../../internals/shared/src/operation.ts
|
|
53
35
|
/**
|
|
54
36
|
* Builds the `ResolverFileParams` every operation generator passes to
|
|
@@ -118,12 +100,12 @@ function getContentTypeInfo(node) {
|
|
|
118
100
|
function getResponseContentTypeInfo(node) {
|
|
119
101
|
return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
|
|
120
102
|
}
|
|
103
|
+
const requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
|
|
121
104
|
function buildRequestConfigType(node) {
|
|
122
105
|
const request = getContentTypeInfo(node);
|
|
123
106
|
const response = getResponseContentTypeInfo(node);
|
|
124
|
-
const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
|
|
125
107
|
const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
|
|
126
|
-
return members.length ? `${
|
|
108
|
+
return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join("; ")} } }` : requestConfigBaseType;
|
|
127
109
|
}
|
|
128
110
|
/**
|
|
129
111
|
* Which of the grouped request options an operation carries.
|
|
@@ -203,11 +185,19 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
|
203
185
|
function getOperationParameters(node) {
|
|
204
186
|
const cached = operationParameterGroupsByNode.get(node);
|
|
205
187
|
if (cached) return cached;
|
|
188
|
+
const group = (location) => {
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
return node.parameters.filter((param) => {
|
|
191
|
+
if (param.in !== location || seen.has(param.name)) return false;
|
|
192
|
+
seen.add(param.name);
|
|
193
|
+
return true;
|
|
194
|
+
});
|
|
195
|
+
};
|
|
206
196
|
const groups = {
|
|
207
|
-
path:
|
|
208
|
-
query:
|
|
209
|
-
header:
|
|
210
|
-
cookie:
|
|
197
|
+
path: group("path"),
|
|
198
|
+
query: group("query"),
|
|
199
|
+
header: group("header"),
|
|
200
|
+
cookie: group("cookie")
|
|
211
201
|
};
|
|
212
202
|
operationParameterGroupsByNode.set(node, groups);
|
|
213
203
|
return groups;
|
|
@@ -229,10 +219,6 @@ function getOperationSuccessResponses(node) {
|
|
|
229
219
|
function getPrimarySuccessResponse(node) {
|
|
230
220
|
return getOperationSuccessResponses(node)[0] ?? null;
|
|
231
221
|
}
|
|
232
|
-
function findSuccessStatusCode(responses) {
|
|
233
|
-
for (const response of responses) if (isSuccessStatusCode(response.statusCode)) return response.statusCode;
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
222
|
//#endregion
|
|
237
223
|
//#region ../../internals/utils/src/casing.ts
|
|
238
224
|
/**
|
|
@@ -277,7 +263,6 @@ function toSlash(p) {
|
|
|
277
263
|
* ```
|
|
278
264
|
*/
|
|
279
265
|
function getRelativePath(rootDir, filePath) {
|
|
280
|
-
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
281
266
|
const relativePath = node_path.posix.relative(toSlash(rootDir), toSlash(filePath));
|
|
282
267
|
return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
|
|
283
268
|
}
|
|
@@ -321,7 +306,6 @@ const requestGroupOrder = [
|
|
|
321
306
|
"body"
|
|
322
307
|
];
|
|
323
308
|
function McpHandler({ name, clientName, node, resolver, returnType = "full" }) {
|
|
324
|
-
if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
|
|
325
309
|
const { signature, groups } = buildRequestParamsSignature(node, resolver, { isConfigurable: false });
|
|
326
310
|
const paramsSignature = [signature, "ctx: ServerContext"].filter(Boolean).join(", ");
|
|
327
311
|
const callConfig = `{ ${[
|
|
@@ -359,72 +343,31 @@ function McpHandler({ name, clientName, node, resolver, returnType = "full" }) {
|
|
|
359
343
|
});
|
|
360
344
|
}
|
|
361
345
|
//#endregion
|
|
362
|
-
//#region src/
|
|
363
|
-
/**
|
|
364
|
-
* Render a group param value — compose individual schemas into `z.object({ ... })`,
|
|
365
|
-
* or use a schema name string directly.
|
|
366
|
-
*/
|
|
367
|
-
function zodGroupExpr(entry) {
|
|
368
|
-
if (typeof entry === "string") return entry;
|
|
369
|
-
return `z.object({ ${entry.map((p) => `${JSON.stringify(p.name)}: ${p.schemaName}`).join(", ")} })`;
|
|
370
|
-
}
|
|
346
|
+
//#region src/components/Server.tsx
|
|
371
347
|
/**
|
|
372
|
-
*
|
|
373
|
-
* Used as fallback when no named zod schema is available for a path parameter.
|
|
348
|
+
* Composes individual param schemas into `z.object({ ... })`.
|
|
374
349
|
*/
|
|
375
|
-
|
|
376
|
-
const baseExpr = (() => {
|
|
377
|
-
if (schema.type === "enum") {
|
|
378
|
-
const rawValues = schema.namedEnumValues?.length ? schema.namedEnumValues.map((v) => v.value) : (schema.enumValues ?? []).filter((v) => v !== null);
|
|
379
|
-
if (rawValues.length > 0 && rawValues.every((v) => typeof v === "string")) return `z.enum([${rawValues.map((v) => JSON.stringify(v)).join(", ")}])`;
|
|
380
|
-
if (rawValues.length > 0) {
|
|
381
|
-
const literals = rawValues.map((v) => `z.literal(${JSON.stringify(v)})`);
|
|
382
|
-
return literals.length === 1 ? literals[0] : `z.union([${literals.join(", ")}])`;
|
|
383
|
-
}
|
|
384
|
-
return "z.string()";
|
|
385
|
-
}
|
|
386
|
-
if (schema.type === "integer") return "z.coerce.number()";
|
|
387
|
-
if (schema.type === "number") return "z.number()";
|
|
388
|
-
if (schema.type === "boolean") return "z.boolean()";
|
|
389
|
-
if (schema.type === "array") return "z.array(z.unknown())";
|
|
390
|
-
return "z.string()";
|
|
391
|
-
})();
|
|
392
|
-
return schema.nullable ? `${baseExpr}.nullable()` : baseExpr;
|
|
393
|
-
}
|
|
394
|
-
//#endregion
|
|
395
|
-
//#region src/components/Server.tsx
|
|
396
|
-
const keysPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "call" });
|
|
350
|
+
const zodObject = (params) => `z.object({ ${params.map((p) => `${JSON.stringify(p.name)}: ${p.schemaName}`).join(", ")} })`;
|
|
397
351
|
function Server({ serverName, serverVersion, operations }) {
|
|
398
|
-
const registrations = operations.map(({ tool, mcp, zod
|
|
399
|
-
const { path: pathParams } = getOperationParameters(node);
|
|
352
|
+
const registrations = operations.map(({ tool, mcp, zod }) => {
|
|
400
353
|
const entries = [];
|
|
401
|
-
if (pathParams.length) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}).join(", ");
|
|
406
|
-
entries.push({
|
|
407
|
-
key: "path",
|
|
408
|
-
value: `z.object({ ${pathFields} })`
|
|
409
|
-
});
|
|
410
|
-
}
|
|
354
|
+
if (zod.pathParams.length) entries.push({
|
|
355
|
+
key: "path",
|
|
356
|
+
value: zodObject(zod.pathParams)
|
|
357
|
+
});
|
|
411
358
|
if (zod.queryParams) entries.push({
|
|
412
359
|
key: "query",
|
|
413
|
-
value:
|
|
360
|
+
value: zodObject(zod.queryParams)
|
|
414
361
|
});
|
|
415
362
|
if (zod.headerParams) entries.push({
|
|
416
363
|
key: "headers",
|
|
417
|
-
value:
|
|
364
|
+
value: zodObject(zod.headerParams)
|
|
418
365
|
});
|
|
419
366
|
if (zod.requestName) entries.push({
|
|
420
367
|
key: "body",
|
|
421
368
|
value: zod.requestName
|
|
422
369
|
});
|
|
423
|
-
const
|
|
424
|
-
name: (0, _kubb_plugin_ts.createObjectBindingPattern)({ elements: entries.map((e) => ({ name: e.key })) }),
|
|
425
|
-
optional: false
|
|
426
|
-
})] }) : null;
|
|
427
|
-
const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
|
|
370
|
+
const destructured = `{ ${entries.map((e) => e.key).join(", ")} }`;
|
|
428
371
|
const inputSchema = entries.length ? `z.object({ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} })` : null;
|
|
429
372
|
const outputSchema = zod.responseName;
|
|
430
373
|
const config = [
|
|
@@ -532,7 +475,7 @@ const contractPlugins = [
|
|
|
532
475
|
* @example
|
|
533
476
|
* ```ts
|
|
534
477
|
* resolveClient({ client: undefined, pluginNames: ['plugin-ts'] })
|
|
535
|
-
* // { kind: 'error', message: 'No client plugin is registered…' }
|
|
478
|
+
* // { kind: 'error', code: 'KUBB_MISSING_DEPENDENCY', message: 'No client plugin is registered.', help: 'Add …' }
|
|
536
479
|
* ```
|
|
537
480
|
*/
|
|
538
481
|
function resolveClient(options) {
|
|
@@ -542,7 +485,9 @@ function resolveClient(options) {
|
|
|
542
485
|
const pluginName = selectorToPlugin[client];
|
|
543
486
|
if (!has(pluginName)) return {
|
|
544
487
|
kind: "error",
|
|
545
|
-
|
|
488
|
+
code: kubb_kit.Diagnostics.code.missingDependency,
|
|
489
|
+
message: `\`client: '${client}'\` is set but \`@kubb/plugin-${client}\` is not registered in \`plugins\`.`,
|
|
490
|
+
help: `Add \`@kubb/plugin-${client}\` to \`plugins\`, or drop \`client\` to use a different client plugin.`
|
|
546
491
|
};
|
|
547
492
|
return {
|
|
548
493
|
kind: "contract",
|
|
@@ -551,7 +496,9 @@ function resolveClient(options) {
|
|
|
551
496
|
}
|
|
552
497
|
if (client !== void 0) return {
|
|
553
498
|
kind: "error",
|
|
554
|
-
|
|
499
|
+
code: kubb_kit.Diagnostics.code.invalidPluginOptions,
|
|
500
|
+
message: `Option \`client\` is ${typeof client === "string" ? `'${client}'` : String(client)}, but it must be one of 'fetch', 'axios', 'client'.`,
|
|
501
|
+
help: "Set `client` to one of those values, or drop it to use the registered client plugin."
|
|
555
502
|
};
|
|
556
503
|
const registered = contractPlugins.filter(has);
|
|
557
504
|
if (registered.length === 1) return {
|
|
@@ -560,25 +507,40 @@ function resolveClient(options) {
|
|
|
560
507
|
};
|
|
561
508
|
if (registered.length > 1) return {
|
|
562
509
|
kind: "error",
|
|
563
|
-
|
|
510
|
+
code: kubb_kit.Diagnostics.code.invalidPluginOptions,
|
|
511
|
+
message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}) and \`client\` is not set.`,
|
|
512
|
+
help: "Set `client: 'fetch' | 'axios' | 'client'` to choose which client the hooks call, or register a single client plugin."
|
|
564
513
|
};
|
|
565
514
|
return {
|
|
566
515
|
kind: "error",
|
|
567
|
-
|
|
516
|
+
code: kubb_kit.Diagnostics.code.missingDependency,
|
|
517
|
+
message: "No client plugin is registered.",
|
|
518
|
+
help: "Add `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client` to `plugins` so the generated code has an HTTP client to call."
|
|
568
519
|
};
|
|
569
520
|
}
|
|
570
521
|
/**
|
|
571
|
-
* Resolves the contract client during a consumer plugin's setup hook.
|
|
572
|
-
* from the raw `plugins` config
|
|
573
|
-
* misconfiguration
|
|
522
|
+
* Resolves the contract client during a consumer plugin's setup hook.
|
|
523
|
+
* It extracts the plugin names from the raw `plugins` config and applies {@link resolveClient}.
|
|
524
|
+
* On a misconfiguration it throws a config diagnostic tagged with `plugin`, so every consumer
|
|
525
|
+
* fails fast with the same message.
|
|
574
526
|
*/
|
|
575
527
|
function resolveContractClient(options) {
|
|
576
|
-
const { client, plugins = [] } = options;
|
|
528
|
+
const { plugin, client, plugins = [] } = options;
|
|
577
529
|
const resolved = resolveClient({
|
|
578
530
|
client,
|
|
579
531
|
pluginNames: plugins.map((plugin) => plugin.name).filter((name) => Boolean(name))
|
|
580
532
|
});
|
|
581
|
-
if (resolved.kind === "error")
|
|
533
|
+
if (resolved.kind === "error") {
|
|
534
|
+
const { code, message, help } = resolved;
|
|
535
|
+
throw new kubb_kit.Diagnostics.Error({
|
|
536
|
+
code,
|
|
537
|
+
severity: "error",
|
|
538
|
+
message,
|
|
539
|
+
help,
|
|
540
|
+
location: { kind: "config" },
|
|
541
|
+
plugin
|
|
542
|
+
});
|
|
543
|
+
}
|
|
582
544
|
return resolved;
|
|
583
545
|
}
|
|
584
546
|
//#endregion
|
|
@@ -671,10 +633,7 @@ const mcpGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
671
633
|
const meta = {
|
|
672
634
|
name: resolver.handler.name(node),
|
|
673
635
|
file: resolver.file({
|
|
674
|
-
|
|
675
|
-
extname: ".ts",
|
|
676
|
-
tag: node.tags[0] ?? "default",
|
|
677
|
-
path: node.path,
|
|
636
|
+
...operationFileEntry(node, node.operationId),
|
|
678
637
|
root,
|
|
679
638
|
output,
|
|
680
639
|
group: group ?? void 0
|
|
@@ -753,22 +712,14 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
753
712
|
const { output, group } = ctx.options;
|
|
754
713
|
const pluginZod = ctx.requirePlugin(_kubb_plugin_zod.pluginZodName);
|
|
755
714
|
const zodResolver = driver.getResolver(_kubb_plugin_zod.pluginZodName);
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
path: node_path.default.resolve(root, output.path, "server.ts"),
|
|
715
|
+
const fileAt = (baseName) => ({
|
|
716
|
+
baseName,
|
|
717
|
+
path: node_path.default.resolve(root, output.path, baseName),
|
|
760
718
|
meta: { pluginName: plugin.name }
|
|
761
|
-
};
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
meta: { pluginName: plugin.name }
|
|
766
|
-
};
|
|
767
|
-
const jsonFile = {
|
|
768
|
-
baseName: ".mcp.json",
|
|
769
|
-
path: node_path.default.resolve(root, output.path, ".mcp.json"),
|
|
770
|
-
meta: { pluginName: plugin.name }
|
|
771
|
-
};
|
|
719
|
+
});
|
|
720
|
+
const serverFile = fileAt("server.ts");
|
|
721
|
+
const startFile = fileAt("start.ts");
|
|
722
|
+
const jsonFile = fileAt(".mcp.json");
|
|
772
723
|
const mcpConfig = { mcpServers: { [ctx.meta.title || "server"]: {
|
|
773
724
|
type: "stdio",
|
|
774
725
|
command: "npx",
|
|
@@ -776,26 +727,21 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
776
727
|
} } };
|
|
777
728
|
const operationsMapped = nodes.filter(kubb_kit.ast.isHttpOperationNode).map((node) => {
|
|
778
729
|
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
730
|
+
const fileEntry = operationFileEntry(node, node.operationId);
|
|
779
731
|
const mcpFile = resolver.file({
|
|
780
|
-
|
|
781
|
-
extname: ".ts",
|
|
782
|
-
tag: node.tags[0] ?? "default",
|
|
783
|
-
path: node.path,
|
|
732
|
+
...fileEntry,
|
|
784
733
|
root,
|
|
785
734
|
output,
|
|
786
735
|
group: group ?? void 0
|
|
787
736
|
});
|
|
788
737
|
const zodFile = zodResolver.file({
|
|
789
|
-
|
|
790
|
-
extname: ".ts",
|
|
791
|
-
tag: node.tags[0] ?? "default",
|
|
792
|
-
path: node.path,
|
|
738
|
+
...fileEntry,
|
|
793
739
|
root,
|
|
794
740
|
output: pluginZod.options?.output ?? output,
|
|
795
741
|
group: pluginZod.options?.group ?? void 0
|
|
796
742
|
});
|
|
797
743
|
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null;
|
|
798
|
-
const successStatus =
|
|
744
|
+
const successStatus = getPrimarySuccessResponse(node)?.statusCode;
|
|
799
745
|
const responseName = successStatus ? zodResolver.response.status(node, successStatus) : null;
|
|
800
746
|
const resolveParams = (params) => params.map((p) => ({
|
|
801
747
|
name: p.name,
|
|
@@ -818,8 +764,7 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
818
764
|
requestName,
|
|
819
765
|
responseName,
|
|
820
766
|
file: zodFile
|
|
821
|
-
}
|
|
822
|
-
node
|
|
767
|
+
}
|
|
823
768
|
};
|
|
824
769
|
});
|
|
825
770
|
const imports = operationsMapped.flatMap(({ mcp, zod }) => {
|
|
@@ -917,7 +862,7 @@ const serverGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
917
862
|
path: jsonFile.path,
|
|
918
863
|
meta: jsonFile.meta,
|
|
919
864
|
children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
920
|
-
name,
|
|
865
|
+
name: "server",
|
|
921
866
|
children: `${JSON.stringify(mcpConfig, null, 2)}\n`
|
|
922
867
|
})
|
|
923
868
|
})
|
|
@@ -932,7 +877,7 @@ var compatibility = {
|
|
|
932
877
|
};
|
|
933
878
|
//#endregion
|
|
934
879
|
//#region package.json
|
|
935
|
-
var version = "5.2.
|
|
880
|
+
var version = "5.2.1";
|
|
936
881
|
//#endregion
|
|
937
882
|
//#region src/resolvers/resolverMcp.ts
|
|
938
883
|
/**
|
|
@@ -1018,6 +963,7 @@ const pluginMcp = (0, kubb_kit.defineExtension)({
|
|
|
1018
963
|
override,
|
|
1019
964
|
group: createGroupConfig(group),
|
|
1020
965
|
client: resolveContractClient({
|
|
966
|
+
plugin: pluginMcpName,
|
|
1021
967
|
client,
|
|
1022
968
|
plugins: (0, kubb_kit.useConfig)().plugins
|
|
1023
969
|
}),
|