@kubb/plugin-mcp 5.2.0 → 5.2.3
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 +72 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +241 -0
- package/dist/index.d.ts +15 -34
- package/dist/index.js +73 -127
- package/dist/index.js.map +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +15 -10
- package/dist/rolldown-runtime-C0LytTxp.js +0 -8
package/dist/index.js
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import { Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver, useConfig } from "kubb/kit";
|
|
1
|
+
import { Diagnostics, Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver, useConfig } from "kubb/kit";
|
|
3
2
|
import path, { posix } from "node:path";
|
|
4
3
|
import { Const, File, Function, jsxRenderer } from "kubb/jsx";
|
|
5
4
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
6
|
-
import { createFunctionParameter, createFunctionParameters, createObjectBindingPattern, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
7
5
|
import { pluginZodName } from "@kubb/plugin-zod";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Drops parameters that share the same name, keeping the first.
|
|
11
|
-
*
|
|
12
|
-
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
13
|
-
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
14
|
-
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
15
|
-
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
16
|
-
*/
|
|
17
|
-
function dedupeParams(params) {
|
|
18
|
-
const seen = /* @__PURE__ */ new Set();
|
|
19
|
-
return params.filter((param) => {
|
|
20
|
-
if (seen.has(param.name)) return false;
|
|
21
|
-
seen.add(param.name);
|
|
22
|
-
return true;
|
|
23
|
-
});
|
|
24
|
-
}
|
|
6
|
+
import { pluginTsName } from "@kubb/plugin-ts";
|
|
25
7
|
//#endregion
|
|
26
8
|
//#region ../../internals/shared/src/operation.ts
|
|
27
9
|
/**
|
|
@@ -92,12 +74,12 @@ function getContentTypeInfo(node) {
|
|
|
92
74
|
function getResponseContentTypeInfo(node) {
|
|
93
75
|
return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
|
|
94
76
|
}
|
|
77
|
+
const requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
|
|
95
78
|
function buildRequestConfigType(node) {
|
|
96
79
|
const request = getContentTypeInfo(node);
|
|
97
80
|
const response = getResponseContentTypeInfo(node);
|
|
98
|
-
const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
|
|
99
81
|
const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
|
|
100
|
-
return members.length ? `${
|
|
82
|
+
return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join("; ")} } }` : requestConfigBaseType;
|
|
101
83
|
}
|
|
102
84
|
/**
|
|
103
85
|
* Which of the grouped request options an operation carries.
|
|
@@ -177,11 +159,19 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
|
177
159
|
function getOperationParameters(node) {
|
|
178
160
|
const cached = operationParameterGroupsByNode.get(node);
|
|
179
161
|
if (cached) return cached;
|
|
162
|
+
const group = (location) => {
|
|
163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
164
|
+
return node.parameters.filter((param) => {
|
|
165
|
+
if (param.in !== location || seen.has(param.name)) return false;
|
|
166
|
+
seen.add(param.name);
|
|
167
|
+
return true;
|
|
168
|
+
});
|
|
169
|
+
};
|
|
180
170
|
const groups = {
|
|
181
|
-
path:
|
|
182
|
-
query:
|
|
183
|
-
header:
|
|
184
|
-
cookie:
|
|
171
|
+
path: group("path"),
|
|
172
|
+
query: group("query"),
|
|
173
|
+
header: group("header"),
|
|
174
|
+
cookie: group("cookie")
|
|
185
175
|
};
|
|
186
176
|
operationParameterGroupsByNode.set(node, groups);
|
|
187
177
|
return groups;
|
|
@@ -203,10 +193,6 @@ function getOperationSuccessResponses(node) {
|
|
|
203
193
|
function getPrimarySuccessResponse(node) {
|
|
204
194
|
return getOperationSuccessResponses(node)[0] ?? null;
|
|
205
195
|
}
|
|
206
|
-
function findSuccessStatusCode(responses) {
|
|
207
|
-
for (const response of responses) if (isSuccessStatusCode(response.statusCode)) return response.statusCode;
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
196
|
//#endregion
|
|
211
197
|
//#region ../../internals/utils/src/casing.ts
|
|
212
198
|
/**
|
|
@@ -251,7 +237,6 @@ function toSlash(p) {
|
|
|
251
237
|
* ```
|
|
252
238
|
*/
|
|
253
239
|
function getRelativePath(rootDir, filePath) {
|
|
254
|
-
if (!rootDir || !filePath) throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ""} ${filePath || ""}`);
|
|
255
240
|
const relativePath = posix.relative(toSlash(rootDir), toSlash(filePath));
|
|
256
241
|
return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
|
|
257
242
|
}
|
|
@@ -295,7 +280,6 @@ const requestGroupOrder = [
|
|
|
295
280
|
"body"
|
|
296
281
|
];
|
|
297
282
|
function McpHandler({ name, clientName, node, resolver, returnType = "full" }) {
|
|
298
|
-
if (!ast.isHttpOperationNode(node)) return null;
|
|
299
283
|
const { signature, groups } = buildRequestParamsSignature(node, resolver, { isConfigurable: false });
|
|
300
284
|
const paramsSignature = [signature, "ctx: ServerContext"].filter(Boolean).join(", ");
|
|
301
285
|
const callConfig = `{ ${[
|
|
@@ -333,72 +317,31 @@ function McpHandler({ name, clientName, node, resolver, returnType = "full" }) {
|
|
|
333
317
|
});
|
|
334
318
|
}
|
|
335
319
|
//#endregion
|
|
336
|
-
//#region src/
|
|
337
|
-
/**
|
|
338
|
-
* Render a group param value — compose individual schemas into `z.object({ ... })`,
|
|
339
|
-
* or use a schema name string directly.
|
|
340
|
-
*/
|
|
341
|
-
function zodGroupExpr(entry) {
|
|
342
|
-
if (typeof entry === "string") return entry;
|
|
343
|
-
return `z.object({ ${entry.map((p) => `${JSON.stringify(p.name)}: ${p.schemaName}`).join(", ")} })`;
|
|
344
|
-
}
|
|
320
|
+
//#region src/components/Server.tsx
|
|
345
321
|
/**
|
|
346
|
-
*
|
|
347
|
-
* Used as fallback when no named zod schema is available for a path parameter.
|
|
322
|
+
* Composes individual param schemas into `z.object({ ... })`.
|
|
348
323
|
*/
|
|
349
|
-
|
|
350
|
-
const baseExpr = (() => {
|
|
351
|
-
if (schema.type === "enum") {
|
|
352
|
-
const rawValues = schema.namedEnumValues?.length ? schema.namedEnumValues.map((v) => v.value) : (schema.enumValues ?? []).filter((v) => v !== null);
|
|
353
|
-
if (rawValues.length > 0 && rawValues.every((v) => typeof v === "string")) return `z.enum([${rawValues.map((v) => JSON.stringify(v)).join(", ")}])`;
|
|
354
|
-
if (rawValues.length > 0) {
|
|
355
|
-
const literals = rawValues.map((v) => `z.literal(${JSON.stringify(v)})`);
|
|
356
|
-
return literals.length === 1 ? literals[0] : `z.union([${literals.join(", ")}])`;
|
|
357
|
-
}
|
|
358
|
-
return "z.string()";
|
|
359
|
-
}
|
|
360
|
-
if (schema.type === "integer") return "z.coerce.number()";
|
|
361
|
-
if (schema.type === "number") return "z.number()";
|
|
362
|
-
if (schema.type === "boolean") return "z.boolean()";
|
|
363
|
-
if (schema.type === "array") return "z.array(z.unknown())";
|
|
364
|
-
return "z.string()";
|
|
365
|
-
})();
|
|
366
|
-
return schema.nullable ? `${baseExpr}.nullable()` : baseExpr;
|
|
367
|
-
}
|
|
368
|
-
//#endregion
|
|
369
|
-
//#region src/components/Server.tsx
|
|
370
|
-
const keysPrinter = functionPrinter({ mode: "call" });
|
|
324
|
+
const zodObject = (params) => `z.object({ ${params.map((p) => `${JSON.stringify(p.name)}: ${p.schemaName}`).join(", ")} })`;
|
|
371
325
|
function Server({ serverName, serverVersion, operations }) {
|
|
372
|
-
const registrations = operations.map(({ tool, mcp, zod
|
|
373
|
-
const { path: pathParams } = getOperationParameters(node);
|
|
326
|
+
const registrations = operations.map(({ tool, mcp, zod }) => {
|
|
374
327
|
const entries = [];
|
|
375
|
-
if (pathParams.length) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}).join(", ");
|
|
380
|
-
entries.push({
|
|
381
|
-
key: "path",
|
|
382
|
-
value: `z.object({ ${pathFields} })`
|
|
383
|
-
});
|
|
384
|
-
}
|
|
328
|
+
if (zod.pathParams.length) entries.push({
|
|
329
|
+
key: "path",
|
|
330
|
+
value: zodObject(zod.pathParams)
|
|
331
|
+
});
|
|
385
332
|
if (zod.queryParams) entries.push({
|
|
386
333
|
key: "query",
|
|
387
|
-
value:
|
|
334
|
+
value: zodObject(zod.queryParams)
|
|
388
335
|
});
|
|
389
336
|
if (zod.headerParams) entries.push({
|
|
390
337
|
key: "headers",
|
|
391
|
-
value:
|
|
338
|
+
value: zodObject(zod.headerParams)
|
|
392
339
|
});
|
|
393
340
|
if (zod.requestName) entries.push({
|
|
394
341
|
key: "body",
|
|
395
342
|
value: zod.requestName
|
|
396
343
|
});
|
|
397
|
-
const
|
|
398
|
-
name: createObjectBindingPattern({ elements: entries.map((e) => ({ name: e.key })) }),
|
|
399
|
-
optional: false
|
|
400
|
-
})] }) : null;
|
|
401
|
-
const destructured = paramsNode ? keysPrinter.print(paramsNode) ?? "" : "";
|
|
344
|
+
const destructured = `{ ${entries.map((e) => e.key).join(", ")} }`;
|
|
402
345
|
const inputSchema = entries.length ? `z.object({ ${entries.map((e) => `${e.key}: ${e.value}`).join(", ")} })` : null;
|
|
403
346
|
const outputSchema = zod.responseName;
|
|
404
347
|
const config = [
|
|
@@ -506,7 +449,7 @@ const contractPlugins = [
|
|
|
506
449
|
* @example
|
|
507
450
|
* ```ts
|
|
508
451
|
* resolveClient({ client: undefined, pluginNames: ['plugin-ts'] })
|
|
509
|
-
* // { kind: 'error', message: 'No client plugin is registered…' }
|
|
452
|
+
* // { kind: 'error', code: 'KUBB_MISSING_DEPENDENCY', message: 'No client plugin is registered.', help: 'Add …' }
|
|
510
453
|
* ```
|
|
511
454
|
*/
|
|
512
455
|
function resolveClient(options) {
|
|
@@ -516,7 +459,9 @@ function resolveClient(options) {
|
|
|
516
459
|
const pluginName = selectorToPlugin[client];
|
|
517
460
|
if (!has(pluginName)) return {
|
|
518
461
|
kind: "error",
|
|
519
|
-
|
|
462
|
+
code: Diagnostics.code.missingDependency,
|
|
463
|
+
message: `\`client: '${client}'\` is set but \`@kubb/plugin-${client}\` is not registered in \`plugins\`.`,
|
|
464
|
+
help: `Add \`@kubb/plugin-${client}\` to \`plugins\`, or drop \`client\` to use a different client plugin.`
|
|
520
465
|
};
|
|
521
466
|
return {
|
|
522
467
|
kind: "contract",
|
|
@@ -525,7 +470,9 @@ function resolveClient(options) {
|
|
|
525
470
|
}
|
|
526
471
|
if (client !== void 0) return {
|
|
527
472
|
kind: "error",
|
|
528
|
-
|
|
473
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
474
|
+
message: `Option \`client\` is ${typeof client === "string" ? `'${client}'` : String(client)}, but it must be one of 'fetch', 'axios', 'client'.`,
|
|
475
|
+
help: "Set `client` to one of those values, or drop it to use the registered client plugin."
|
|
529
476
|
};
|
|
530
477
|
const registered = contractPlugins.filter(has);
|
|
531
478
|
if (registered.length === 1) return {
|
|
@@ -534,25 +481,40 @@ function resolveClient(options) {
|
|
|
534
481
|
};
|
|
535
482
|
if (registered.length > 1) return {
|
|
536
483
|
kind: "error",
|
|
537
|
-
|
|
484
|
+
code: Diagnostics.code.invalidPluginOptions,
|
|
485
|
+
message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}) and \`client\` is not set.`,
|
|
486
|
+
help: "Set `client: 'fetch' | 'axios' | 'client'` to choose which client the hooks call, or register a single client plugin."
|
|
538
487
|
};
|
|
539
488
|
return {
|
|
540
489
|
kind: "error",
|
|
541
|
-
|
|
490
|
+
code: Diagnostics.code.missingDependency,
|
|
491
|
+
message: "No client plugin is registered.",
|
|
492
|
+
help: "Add `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client` to `plugins` so the generated code has an HTTP client to call."
|
|
542
493
|
};
|
|
543
494
|
}
|
|
544
495
|
/**
|
|
545
|
-
* Resolves the contract client during a consumer plugin's setup hook.
|
|
546
|
-
* from the raw `plugins` config
|
|
547
|
-
* misconfiguration
|
|
496
|
+
* Resolves the contract client during a consumer plugin's setup hook.
|
|
497
|
+
* It extracts the plugin names from the raw `plugins` config and applies {@link resolveClient}.
|
|
498
|
+
* On a misconfiguration it throws a config diagnostic tagged with `plugin`, so every consumer
|
|
499
|
+
* fails fast with the same message.
|
|
548
500
|
*/
|
|
549
501
|
function resolveContractClient(options) {
|
|
550
|
-
const { client, plugins = [] } = options;
|
|
502
|
+
const { plugin, client, plugins = [] } = options;
|
|
551
503
|
const resolved = resolveClient({
|
|
552
504
|
client,
|
|
553
505
|
pluginNames: plugins.map((plugin) => plugin.name).filter((name) => Boolean(name))
|
|
554
506
|
});
|
|
555
|
-
if (resolved.kind === "error")
|
|
507
|
+
if (resolved.kind === "error") {
|
|
508
|
+
const { code, message, help } = resolved;
|
|
509
|
+
throw new Diagnostics.Error({
|
|
510
|
+
code,
|
|
511
|
+
severity: "error",
|
|
512
|
+
message,
|
|
513
|
+
help,
|
|
514
|
+
location: { kind: "config" },
|
|
515
|
+
plugin
|
|
516
|
+
});
|
|
517
|
+
}
|
|
556
518
|
return resolved;
|
|
557
519
|
}
|
|
558
520
|
//#endregion
|
|
@@ -645,10 +607,7 @@ const mcpGenerator = defineGenerator({
|
|
|
645
607
|
const meta = {
|
|
646
608
|
name: resolver.handler.name(node),
|
|
647
609
|
file: resolver.file({
|
|
648
|
-
|
|
649
|
-
extname: ".ts",
|
|
650
|
-
tag: node.tags[0] ?? "default",
|
|
651
|
-
path: node.path,
|
|
610
|
+
...operationFileEntry(node, node.operationId),
|
|
652
611
|
root,
|
|
653
612
|
output,
|
|
654
613
|
group: group ?? void 0
|
|
@@ -727,22 +686,14 @@ const serverGenerator = defineGenerator({
|
|
|
727
686
|
const { output, group } = ctx.options;
|
|
728
687
|
const pluginZod = ctx.requirePlugin(pluginZodName);
|
|
729
688
|
const zodResolver = driver.getResolver(pluginZodName);
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
path: path.resolve(root, output.path, "server.ts"),
|
|
734
|
-
meta: { pluginName: plugin.name }
|
|
735
|
-
};
|
|
736
|
-
const startFile = {
|
|
737
|
-
baseName: "start.ts",
|
|
738
|
-
path: path.resolve(root, output.path, "start.ts"),
|
|
689
|
+
const fileAt = (baseName) => ({
|
|
690
|
+
baseName,
|
|
691
|
+
path: path.resolve(root, output.path, baseName),
|
|
739
692
|
meta: { pluginName: plugin.name }
|
|
740
|
-
};
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
meta: { pluginName: plugin.name }
|
|
745
|
-
};
|
|
693
|
+
});
|
|
694
|
+
const serverFile = fileAt("server.ts");
|
|
695
|
+
const startFile = fileAt("start.ts");
|
|
696
|
+
const jsonFile = fileAt(".mcp.json");
|
|
746
697
|
const mcpConfig = { mcpServers: { [ctx.meta.title || "server"]: {
|
|
747
698
|
type: "stdio",
|
|
748
699
|
command: "npx",
|
|
@@ -750,26 +701,21 @@ const serverGenerator = defineGenerator({
|
|
|
750
701
|
} } };
|
|
751
702
|
const operationsMapped = nodes.filter(ast.isHttpOperationNode).map((node) => {
|
|
752
703
|
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
|
|
704
|
+
const fileEntry = operationFileEntry(node, node.operationId);
|
|
753
705
|
const mcpFile = resolver.file({
|
|
754
|
-
|
|
755
|
-
extname: ".ts",
|
|
756
|
-
tag: node.tags[0] ?? "default",
|
|
757
|
-
path: node.path,
|
|
706
|
+
...fileEntry,
|
|
758
707
|
root,
|
|
759
708
|
output,
|
|
760
709
|
group: group ?? void 0
|
|
761
710
|
});
|
|
762
711
|
const zodFile = zodResolver.file({
|
|
763
|
-
|
|
764
|
-
extname: ".ts",
|
|
765
|
-
tag: node.tags[0] ?? "default",
|
|
766
|
-
path: node.path,
|
|
712
|
+
...fileEntry,
|
|
767
713
|
root,
|
|
768
714
|
output: pluginZod.options?.output ?? output,
|
|
769
715
|
group: pluginZod.options?.group ?? void 0
|
|
770
716
|
});
|
|
771
717
|
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null;
|
|
772
|
-
const successStatus =
|
|
718
|
+
const successStatus = getPrimarySuccessResponse(node)?.statusCode;
|
|
773
719
|
const responseName = successStatus ? zodResolver.response.status(node, successStatus) : null;
|
|
774
720
|
const resolveParams = (params) => params.map((p) => ({
|
|
775
721
|
name: p.name,
|
|
@@ -792,8 +738,7 @@ const serverGenerator = defineGenerator({
|
|
|
792
738
|
requestName,
|
|
793
739
|
responseName,
|
|
794
740
|
file: zodFile
|
|
795
|
-
}
|
|
796
|
-
node
|
|
741
|
+
}
|
|
797
742
|
};
|
|
798
743
|
});
|
|
799
744
|
const imports = operationsMapped.flatMap(({ mcp, zod }) => {
|
|
@@ -891,7 +836,7 @@ const serverGenerator = defineGenerator({
|
|
|
891
836
|
path: jsonFile.path,
|
|
892
837
|
meta: jsonFile.meta,
|
|
893
838
|
children: /* @__PURE__ */ jsx(File.Source, {
|
|
894
|
-
name,
|
|
839
|
+
name: "server",
|
|
895
840
|
children: `${JSON.stringify(mcpConfig, null, 2)}\n`
|
|
896
841
|
})
|
|
897
842
|
})
|
|
@@ -902,11 +847,11 @@ const serverGenerator = defineGenerator({
|
|
|
902
847
|
//#region kubb-extension.json
|
|
903
848
|
var compatibility = {
|
|
904
849
|
"kubb": ">=5.8.3",
|
|
905
|
-
"node": ">=22"
|
|
850
|
+
"node": ">=22.12"
|
|
906
851
|
};
|
|
907
852
|
//#endregion
|
|
908
853
|
//#region package.json
|
|
909
|
-
var version = "5.2.
|
|
854
|
+
var version = "5.2.3";
|
|
910
855
|
//#endregion
|
|
911
856
|
//#region src/resolvers/resolverMcp.ts
|
|
912
857
|
/**
|
|
@@ -992,6 +937,7 @@ const pluginMcp = defineExtension({
|
|
|
992
937
|
override,
|
|
993
938
|
group: createGroupConfig(group),
|
|
994
939
|
client: resolveContractClient({
|
|
940
|
+
plugin: pluginMcpName,
|
|
995
941
|
client,
|
|
996
942
|
plugins: useConfig().plugins
|
|
997
943
|
}),
|