@kubb/plugin-mcp 5.0.0-alpha.34 → 5.0.0-alpha.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-mcp",
3
- "version": "5.0.0-alpha.34",
3
+ "version": "5.0.0-alpha.35",
4
4
  "description": "Model Context Protocol (MCP) plugin for Kubb, generating MCP-compatible tools and schemas from OpenAPI specifications for AI assistants.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -49,12 +49,11 @@
49
49
  }
50
50
  ],
51
51
  "dependencies": {
52
- "@kubb/ast": "5.0.0-alpha.34",
53
- "@kubb/core": "5.0.0-alpha.34",
54
- "@kubb/plugin-client": "5.0.0-alpha.34",
55
- "@kubb/plugin-ts": "5.0.0-alpha.34",
56
- "@kubb/plugin-zod": "5.0.0-alpha.34",
57
- "@kubb/renderer-jsx": "5.0.0-alpha.34"
52
+ "@kubb/core": "5.0.0-alpha.35",
53
+ "@kubb/plugin-client": "5.0.0-alpha.35",
54
+ "@kubb/plugin-ts": "5.0.0-alpha.35",
55
+ "@kubb/renderer-jsx": "5.0.0-alpha.35",
56
+ "@kubb/plugin-zod": "5.0.0-alpha.35"
58
57
  },
59
58
  "devDependencies": {
60
59
  "@internals/utils": "0.0.0"
@@ -69,7 +68,7 @@
69
68
  "main": "./dist/index.cjs",
70
69
  "module": "./dist/index.js",
71
70
  "peerDependencies": {
72
- "@kubb/renderer-jsx": "5.0.0-alpha.34"
71
+ "@kubb/renderer-jsx": "5.0.0-alpha.35"
73
72
  },
74
73
  "scripts": {
75
74
  "build": "tsdown && size-limit",
@@ -1,6 +1,5 @@
1
1
  import { isValidVarName, URLPath } from '@internals/utils'
2
- import { caseParams, createOperationParams } from '@kubb/ast'
3
- import type { OperationNode } from '@kubb/ast/types'
2
+ import { ast } from '@kubb/core'
4
3
  import type { ResolverTs } from '@kubb/plugin-ts'
5
4
  import { functionPrinter } from '@kubb/plugin-ts'
6
5
  import { File, Function } from '@kubb/renderer-jsx'
@@ -16,7 +15,7 @@ type Props = {
16
15
  /**
17
16
  * AST operation node.
18
17
  */
19
- node: OperationNode
18
+ node: ast.OperationNode
20
19
  /**
21
20
  * TypeScript resolver for resolving param/data/response type names.
22
21
  */
@@ -55,7 +54,7 @@ export function McpHandler({ name, node, resolver, baseURL, dataReturnType, para
55
54
  const contentType = node.requestBody?.contentType
56
55
  const isFormData = contentType === 'multipart/form-data'
57
56
 
58
- const casedParams = caseParams(node.parameters, paramsCasing)
57
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
59
58
  const queryParams = casedParams.filter((p) => p.in === 'query')
60
59
  const headerParams = casedParams.filter((p) => p.in === 'header')
61
60
 
@@ -73,7 +72,7 @@ export function McpHandler({ name, node, resolver, baseURL, dataReturnType, para
73
72
  const TError = `ResponseErrorConfig<${errorType}>`
74
73
  const generics = [responseName, TError, requestName || 'unknown'].filter(Boolean)
75
74
 
76
- const paramsNode = createOperationParams(node, {
75
+ const paramsNode = ast.createOperationParams(node, {
77
76
  paramsType: 'object',
78
77
  pathParamsType: 'inline',
79
78
  resolver,
@@ -1,5 +1,4 @@
1
- import { caseParams, createFunctionParameter, createFunctionParameters, createParameterGroup } from '@kubb/ast'
2
- import type { FileNode, OperationNode } from '@kubb/ast/types'
1
+ import { ast } from '@kubb/core'
3
2
  import { functionPrinter } from '@kubb/plugin-ts'
4
3
  import { Const, File, Function } from '@kubb/renderer-jsx'
5
4
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
@@ -36,7 +35,7 @@ type Props = {
36
35
  }
37
36
  mcp: {
38
37
  name: string
39
- file: FileNode
38
+ file: ast.FileNode
40
39
  }
41
40
  zod: {
42
41
  pathParams: Array<ZodParam>
@@ -51,7 +50,7 @@ type Props = {
51
50
  requestName?: string
52
51
  responseName?: string
53
52
  }
54
- node: OperationNode
53
+ node: ast.OperationNode
55
54
  }>
56
55
  }
57
56
 
@@ -71,7 +70,7 @@ export function Server({ name, serverName, serverVersion, paramsCasing, operatio
71
70
 
72
71
  {operations
73
72
  .map(({ tool, mcp, zod, node }) => {
74
- const casedParams = caseParams(node.parameters, paramsCasing)
73
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
75
74
  const pathParams = casedParams.filter((p) => p.in === 'path')
76
75
 
77
76
  const pathEntries: Array<{ key: string; value: string }> = []
@@ -98,10 +97,10 @@ export function Server({ name, serverName, serverVersion, paramsCasing, operatio
98
97
  const entries = [...pathEntries, ...otherEntries]
99
98
 
100
99
  const paramsNode = entries.length
101
- ? createFunctionParameters({
100
+ ? ast.createFunctionParameters({
102
101
  params: [
103
- createParameterGroup({
104
- properties: entries.map((e) => createFunctionParameter({ name: e.key, optional: false })),
102
+ ast.createParameterGroup({
103
+ properties: entries.map((e) => ast.createFunctionParameter({ name: e.key, optional: false })),
105
104
  }),
106
105
  ],
107
106
  })
@@ -1,16 +1,17 @@
1
1
  import path from 'node:path'
2
- import { caseParams } from '@kubb/ast'
3
- import { defineGenerator } from '@kubb/core'
2
+
3
+ import { ast, defineGenerator } from '@kubb/core'
4
4
  import { pluginTsName } from '@kubb/plugin-ts'
5
- import { File } from '@kubb/renderer-jsx'
5
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
6
6
  import { McpHandler } from '../components/McpHandler.tsx'
7
7
  import type { PluginMcp } from '../types.ts'
8
8
 
9
9
  export const mcpGenerator = defineGenerator<PluginMcp>({
10
10
  name: 'mcp',
11
- operation(node, options) {
12
- const { resolver, driver, root } = this
13
- const { output, client, paramsCasing, group } = options
11
+ renderer: jsxRenderer,
12
+ operation(node, ctx) {
13
+ const { resolver, driver, root } = ctx
14
+ const { output, client, paramsCasing, group } = ctx.options
14
15
 
15
16
  const pluginTs = driver.getPlugin(pluginTsName)
16
17
 
@@ -18,7 +19,7 @@ export const mcpGenerator = defineGenerator<PluginMcp>({
18
19
  return null
19
20
  }
20
21
 
21
- const casedParams = caseParams(node.parameters, paramsCasing)
22
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
22
23
 
23
24
  const pathParams = casedParams.filter((p) => p.in === 'path')
24
25
  const queryParams = casedParams.filter((p) => p.in === 'query')
@@ -1,8 +1,8 @@
1
1
  import path from 'node:path'
2
- import { caseParams } from '@kubb/ast'
3
- import { defineGenerator } from '@kubb/core'
2
+
3
+ import { ast, defineGenerator } from '@kubb/core'
4
4
  import { pluginZodName } from '@kubb/plugin-zod'
5
- import { File } from '@kubb/renderer-jsx'
5
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
6
6
  import { Server } from '../components/Server.tsx'
7
7
  import type { PluginMcp } from '../types.ts'
8
8
  import { findSuccessStatusCode } from '../utils.ts'
@@ -16,9 +16,10 @@ import { findSuccessStatusCode } from '../utils.ts'
16
16
  */
17
17
  export const serverGenerator = defineGenerator<PluginMcp>({
18
18
  name: 'operations',
19
- operations(nodes, options) {
20
- const { adapter, config, resolver, plugin, driver, root } = this
21
- const { output, paramsCasing, group } = options
19
+ renderer: jsxRenderer,
20
+ operations(nodes, ctx) {
21
+ const { adapter, config, resolver, plugin, driver, root } = ctx
22
+ const { output, paramsCasing, group } = ctx.options
22
23
 
23
24
  const pluginZod = driver.getPlugin(pluginZodName)
24
25
 
@@ -42,7 +43,7 @@ export const serverGenerator = defineGenerator<PluginMcp>({
42
43
  }
43
44
 
44
45
  const operationsMapped = nodes.map((node) => {
45
- const casedParams = caseParams(node.parameters, paramsCasing)
46
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
46
47
  const pathParams = casedParams.filter((p) => p.in === 'path')
47
48
  const queryParams = casedParams.filter((p) => p.in === 'query')
48
49
  const headerParams = casedParams.filter((p) => p.in === 'header')
@@ -1,8 +1,8 @@
1
1
  import path from 'node:path'
2
- import { caseParams } from '@kubb/ast'
3
- import { defineGenerator } from '@kubb/core'
2
+
3
+ import { ast, defineGenerator } from '@kubb/core'
4
4
  import { pluginZodName } from '@kubb/plugin-zod'
5
- import { File } from '@kubb/renderer-jsx'
5
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
6
6
  import { Server } from '../components/Server.tsx'
7
7
  import type { PluginMcp } from '../types.ts'
8
8
 
@@ -15,9 +15,10 @@ import type { PluginMcp } from '../types.ts'
15
15
  */
16
16
  export const serverGeneratorLegacy = defineGenerator<PluginMcp>({
17
17
  name: 'operations',
18
- operations(nodes, options) {
19
- const { adapter, config, resolver, plugin, driver, root } = this
20
- const { output, paramsCasing, group } = options
18
+ renderer: jsxRenderer,
19
+ operations(nodes, ctx) {
20
+ const { adapter, config, resolver, plugin, driver, root } = ctx
21
+ const { output, paramsCasing, group } = ctx.options
21
22
 
22
23
  const pluginZod = driver.getPlugin(pluginZodName)
23
24
 
@@ -41,7 +42,7 @@ export const serverGeneratorLegacy = defineGenerator<PluginMcp>({
41
42
  }
42
43
 
43
44
  const operationsMapped = nodes.map((node) => {
44
- const casedParams = caseParams(node.parameters, paramsCasing)
45
+ const casedParams = ast.caseParams(node.parameters, paramsCasing)
45
46
  const queryParams = casedParams.filter((p) => p.in === 'query')
46
47
  const headerParams = casedParams.filter((p) => p.in === 'header')
47
48
 
package/src/plugin.ts CHANGED
@@ -1,20 +1,22 @@
1
1
  import path from 'node:path'
2
2
  import { camelCase } from '@internals/utils'
3
- import { createFile, createSource, createText } from '@kubb/ast'
4
- import { createPlugin, type Group, getPreset, mergeGenerators } from '@kubb/core'
3
+
4
+ import { ast, definePlugin, type Group } from '@kubb/core'
5
5
  import { pluginClientName } from '@kubb/plugin-client'
6
6
  import { source as axiosClientSource } from '@kubb/plugin-client/templates/clients/axios.source'
7
7
  import { source as fetchClientSource } from '@kubb/plugin-client/templates/clients/fetch.source'
8
8
  import { source as configSource } from '@kubb/plugin-client/templates/config.source'
9
9
  import { pluginTsName } from '@kubb/plugin-ts'
10
10
  import { pluginZodName } from '@kubb/plugin-zod'
11
- import { version } from '../package.json'
12
- import { presets } from './presets.ts'
11
+ import { mcpGenerator } from './generators/mcpGenerator.tsx'
12
+ import { serverGenerator } from './generators/serverGenerator.tsx'
13
+ import { serverGeneratorLegacy } from './generators/serverGeneratorLegacy.tsx'
14
+ import { resolverMcp } from './resolvers/resolverMcp.ts'
13
15
  import type { PluginMcp } from './types.ts'
14
16
 
15
17
  export const pluginMcpName = 'plugin-mcp' satisfies PluginMcp['name']
16
18
 
17
- export const pluginMcp = createPlugin<PluginMcp>((options) => {
19
+ export const pluginMcp = definePlugin<PluginMcp>((options) => {
18
20
  const {
19
21
  output = { path: 'mcp', barrelType: 'named' },
20
22
  group,
@@ -23,127 +25,100 @@ export const pluginMcp = createPlugin<PluginMcp>((options) => {
23
25
  override = [],
24
26
  paramsCasing,
25
27
  client,
26
- compatibilityPreset = 'default',
27
28
  resolver: userResolver,
28
29
  transformer: userTransformer,
29
30
  generators: userGenerators = [],
31
+ compatibilityPreset = 'default',
30
32
  } = options
31
33
 
34
+ const defaultServerGenerator = compatibilityPreset === 'kubbV4' ? serverGeneratorLegacy : serverGenerator
35
+
32
36
  const clientName = client?.client ?? 'axios'
33
37
  const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : undefined)
34
38
 
35
- const preset = getPreset({
36
- preset: compatibilityPreset,
37
- presets,
38
- resolver: userResolver,
39
- transformer: userTransformer,
40
- generators: userGenerators,
41
- })
42
-
43
- const generators = preset.generators ?? []
44
- const mergedGenerator = mergeGenerators(generators)
39
+ const groupConfig = group
40
+ ? ({
41
+ ...group,
42
+ name: group.name
43
+ ? group.name
44
+ : (ctx: { group: string }) => {
45
+ if (group.type === 'path') {
46
+ return `${ctx.group.split('/')[1]}`
47
+ }
48
+ return `${camelCase(ctx.group)}Requests`
49
+ },
50
+ } satisfies Group)
51
+ : undefined
45
52
 
46
53
  return {
47
54
  name: pluginMcpName,
48
- version,
49
- get resolver() {
50
- return preset.resolver
51
- },
52
- get transformer() {
53
- return preset.transformer
54
- },
55
- get options() {
56
- return {
57
- output,
58
- exclude,
59
- include,
60
- override,
61
- group: group
62
- ? ({
63
- ...group,
64
- name: group.name
65
- ? group.name
66
- : (ctx: { group: string }) => {
67
- if (group.type === 'path') {
68
- return `${ctx.group.split('/')[1]}`
69
- }
70
- return `${camelCase(ctx.group)}Requests`
71
- },
72
- } satisfies Group)
73
- : undefined,
74
- paramsCasing,
75
- client: {
76
- client: clientName,
77
- clientType: client?.clientType ?? 'function',
78
- importPath: clientImportPath,
79
- dataReturnType: client?.dataReturnType ?? 'data',
80
- bundle: client?.bundle,
81
- baseURL: client?.baseURL,
82
- paramsCasing: client?.paramsCasing,
83
- },
84
- resolver: preset.resolver,
85
- }
86
- },
87
- pre: [pluginTsName, pluginZodName].filter(Boolean),
88
- async schema(node, options) {
89
- return mergedGenerator.schema?.call(this, node, options)
90
- },
91
- async operation(node, options) {
92
- return mergedGenerator.operation?.call(this, node, options)
93
- },
94
- async operations(nodes, options) {
95
- return mergedGenerator.operations?.call(this, nodes, options)
96
- },
97
- async buildStart() {
98
- const { adapter, driver } = this
99
- const root = this.root
55
+ options,
56
+ dependencies: [pluginTsName, pluginZodName],
57
+ hooks: {
58
+ 'kubb:plugin:setup'(ctx) {
59
+ const resolver = userResolver ? { ...resolverMcp, ...userResolver } : resolverMcp
100
60
 
101
- const baseURL = adapter?.inputNode?.meta?.baseURL
102
- if (baseURL) {
103
- this.plugin.options.client.baseURL = this.plugin.options.client.baseURL || baseURL
104
- }
61
+ ctx.setOptions({
62
+ output,
63
+ exclude,
64
+ include,
65
+ override,
66
+ group: groupConfig,
67
+ paramsCasing,
68
+ client: {
69
+ client: clientName,
70
+ clientType: client?.clientType ?? 'function',
71
+ importPath: clientImportPath,
72
+ dataReturnType: client?.dataReturnType ?? 'data',
73
+ bundle: client?.bundle,
74
+ baseURL: client?.baseURL,
75
+ paramsCasing: client?.paramsCasing,
76
+ },
77
+ resolver,
78
+ })
79
+ ctx.setResolver(resolver)
80
+ if (userTransformer) {
81
+ ctx.setTransformer(userTransformer)
82
+ }
83
+ ctx.addGenerator(mcpGenerator)
84
+ ctx.addGenerator(defaultServerGenerator)
85
+ for (const gen of userGenerators) {
86
+ ctx.addGenerator(gen)
87
+ }
105
88
 
106
- const hasClientPlugin = !!driver.getPlugin(pluginClientName)
89
+ const root = path.resolve(ctx.config.root, ctx.config.output.path)
90
+ const hasClientPlugin = ctx.config.plugins?.some((p) => p.name === pluginClientName)
107
91
 
108
- if (this.plugin.options.client.bundle && !hasClientPlugin && !this.plugin.options.client.importPath) {
109
- await this.addFile(
110
- createFile({
92
+ if (client?.bundle && !hasClientPlugin && !clientImportPath) {
93
+ ctx.injectFile({
111
94
  baseName: 'fetch.ts',
112
95
  path: path.resolve(root, '.kubb/fetch.ts'),
113
96
  sources: [
114
- createSource({
97
+ ast.createSource({
115
98
  name: 'fetch',
116
- nodes: [createText(this.plugin.options.client.client === 'fetch' ? fetchClientSource : axiosClientSource)],
99
+ nodes: [ast.createText(clientName === 'fetch' ? fetchClientSource : axiosClientSource)],
117
100
  isExportable: true,
118
101
  isIndexable: true,
119
102
  }),
120
103
  ],
121
- imports: [],
122
- exports: [],
123
- }),
124
- )
125
- }
104
+ })
105
+ }
126
106
 
127
- if (!hasClientPlugin) {
128
- await this.addFile(
129
- createFile({
107
+ if (!hasClientPlugin) {
108
+ ctx.injectFile({
130
109
  baseName: 'config.ts',
131
110
  path: path.resolve(root, '.kubb/config.ts'),
132
111
  sources: [
133
- createSource({
112
+ ast.createSource({
134
113
  name: 'config',
135
- nodes: [createText(configSource)],
114
+ nodes: [ast.createText(configSource)],
136
115
  isExportable: false,
137
116
  isIndexable: false,
138
117
  }),
139
118
  ],
140
- imports: [],
141
- exports: [],
142
- }),
143
- )
144
- }
145
-
146
- await this.openInStudio({ ast: true })
119
+ })
120
+ }
121
+ },
147
122
  },
148
123
  }
149
124
  })
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { Visitor } from '@kubb/ast/types'
2
1
  import type {
2
+ ast,
3
3
  CompatibilityPreset,
4
4
  Exclude,
5
5
  Generator,
@@ -74,7 +74,7 @@ export type Options = {
74
74
  * A single AST visitor applied before printing.
75
75
  * When a visitor method returns `null` or `undefined`, the preset transformer's result is used instead.
76
76
  */
77
- transformer?: Visitor
77
+ transformer?: ast.Visitor
78
78
  /**
79
79
  * Define some generators next to the default MCP generators.
80
80
  */
package/src/utils.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { camelCase } from '@internals/utils'
2
- import type { OperationNode, SchemaNode, StatusCode } from '@kubb/ast/types'
2
+ import type { ast } from '@kubb/core'
3
3
 
4
4
  /**
5
5
  * Find the first 2xx response status code from an operation's responses.
6
6
  */
7
- export function findSuccessStatusCode(responses: Array<{ statusCode: number | string }>): StatusCode | undefined {
7
+ export function findSuccessStatusCode(responses: Array<{ statusCode: number | string }>): ast.StatusCode | undefined {
8
8
  for (const res of responses) {
9
9
  const code = Number(res.statusCode)
10
10
  if (code >= 200 && code < 300) {
11
- return res.statusCode as StatusCode
11
+ return res.statusCode as ast.StatusCode
12
12
  }
13
13
  }
14
14
  return undefined
@@ -34,7 +34,7 @@ export function zodGroupExpr(entry: string | Array<ZodParam>): string {
34
34
  /**
35
35
  * Build JSDoc comment lines from an OperationNode.
36
36
  */
37
- export function getComments(node: OperationNode): Array<string> {
37
+ export function getComments(node: ast.OperationNode): Array<string> {
38
38
  return [
39
39
  node.description && `@description ${node.description}`,
40
40
  node.summary && `@summary ${node.summary}`,
@@ -70,7 +70,7 @@ export function getParamsMapping(params: Array<{ name: string }>): Record<string
70
70
  * Convert a SchemaNode type to an inline Zod expression string.
71
71
  * Used as fallback when no named zod schema is available for a path parameter.
72
72
  */
73
- export function zodExprFromSchemaNode(schema: SchemaNode): string {
73
+ export function zodExprFromSchemaNode(schema: ast.SchemaNode): string {
74
74
  let expr: string
75
75
  switch (schema.type) {
76
76
  case 'enum': {
package/src/presets.ts DELETED
@@ -1,25 +0,0 @@
1
- import { definePresets } from '@kubb/core'
2
- import { mcpGenerator } from './generators/mcpGenerator.tsx'
3
- import { serverGenerator } from './generators/serverGenerator.tsx'
4
- import { serverGeneratorLegacy } from './generators/serverGeneratorLegacy.tsx'
5
- import { resolverMcp } from './resolvers/resolverMcp.ts'
6
- import type { ResolverMcp } from './types.ts'
7
-
8
- /**
9
- * Built-in preset registry for `@kubb/plugin-mcp`.
10
- *
11
- * - `default` — v5 naming with individual zod schemas and per-status responses.
12
- * - `kubbV4` — legacy naming with grouped zod schemas and combined responses.
13
- */
14
- export const presets = definePresets<ResolverMcp>({
15
- default: {
16
- name: 'default',
17
- resolver: resolverMcp,
18
- generators: [mcpGenerator, serverGenerator],
19
- },
20
- kubbV4: {
21
- name: 'kubbV4',
22
- resolver: resolverMcp,
23
- generators: [mcpGenerator, serverGeneratorLegacy],
24
- },
25
- })