@kubb/plugin-mcp 5.0.0-alpha.9 → 5.0.0-beta.4
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/LICENSE +17 -10
- package/README.md +1 -4
- package/dist/index.cjs +957 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +207 -4
- package/dist/index.js +923 -90
- package/dist/index.js.map +1 -1
- package/extension.yaml +470 -0
- package/package.json +42 -64
- package/src/components/McpHandler.tsx +173 -0
- package/src/components/Server.tsx +89 -109
- package/src/generators/mcpGenerator.tsx +65 -84
- package/src/generators/serverGenerator.tsx +95 -58
- package/src/index.ts +11 -2
- package/src/plugin.ts +87 -135
- package/src/resolvers/resolverMcp.ts +25 -0
- package/src/types.ts +46 -28
- package/src/utils.ts +113 -0
- package/dist/Server-DV9zFrUP.cjs +0 -221
- package/dist/Server-DV9zFrUP.cjs.map +0 -1
- package/dist/Server-KWLMg0Lm.js +0 -173
- package/dist/Server-KWLMg0Lm.js.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -41
- package/dist/components.js +0 -2
- package/dist/generators-CWAFnA94.cjs +0 -285
- package/dist/generators-CWAFnA94.cjs.map +0 -1
- package/dist/generators-TtEOkDB1.js +0 -274
- package/dist/generators-TtEOkDB1.js.map +0 -1
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -508
- package/dist/generators.js +0 -2
- package/dist/types-DXZDZ3vf.d.ts +0 -64
- package/src/components/index.ts +0 -1
- package/src/generators/index.ts +0 -2
|
@@ -1,109 +1,90 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
3
|
-
import { createReactGenerator } from '@kubb/plugin-oas/generators'
|
|
4
|
-
import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
|
|
5
|
-
import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
|
|
2
|
+
import { ast, defineGenerator } from '@kubb/core'
|
|
6
3
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
7
|
-
import { File } from '@kubb/
|
|
8
|
-
import
|
|
4
|
+
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
5
|
+
import { McpHandler } from '../components/McpHandler.tsx'
|
|
6
|
+
import type { PluginMcp } from '../types.ts'
|
|
9
7
|
|
|
10
|
-
export const mcpGenerator =
|
|
8
|
+
export const mcpGenerator = defineGenerator<PluginMcp>({
|
|
11
9
|
name: 'mcp',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
10
|
+
renderer: jsxRenderer,
|
|
11
|
+
operation(node, ctx) {
|
|
12
|
+
const { resolver, driver, root } = ctx
|
|
13
|
+
const { output, client, paramsCasing, group } = ctx.options
|
|
15
14
|
|
|
16
|
-
const
|
|
15
|
+
const pluginTs = driver.getPlugin(pluginTsName)
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
file: getFile(operation),
|
|
17
|
+
if (!pluginTs) {
|
|
18
|
+
return null
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const tsResolver = driver.getResolver(pluginTsName)
|
|
22
|
+
|
|
23
|
+
const casedParams = ast.caseParams(node.parameters, paramsCasing)
|
|
24
|
+
|
|
25
|
+
const pathParams = casedParams.filter((p) => p.in === 'path')
|
|
26
|
+
const queryParams = casedParams.filter((p) => p.in === 'query')
|
|
27
|
+
const headerParams = casedParams.filter((p) => p.in === 'header')
|
|
28
|
+
|
|
29
|
+
const importedTypeNames = [
|
|
30
|
+
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
31
|
+
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
32
|
+
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
33
|
+
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
|
|
34
|
+
tsResolver.resolveResponseName(node),
|
|
35
|
+
...node.responses.filter((r) => Number(r.statusCode) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode)),
|
|
36
|
+
].filter(Boolean)
|
|
37
|
+
|
|
38
|
+
const meta = {
|
|
39
|
+
name: resolver.resolveName(node.operationId),
|
|
40
|
+
file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
|
|
41
|
+
fileTs: tsResolver.resolveFile(
|
|
42
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
43
|
+
{
|
|
44
|
+
root,
|
|
45
|
+
output: pluginTs.options?.output ?? output,
|
|
46
|
+
group: pluginTs.options?.group,
|
|
47
|
+
},
|
|
48
|
+
),
|
|
49
|
+
} as const
|
|
27
50
|
|
|
28
51
|
return (
|
|
29
|
-
<File
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{
|
|
52
|
+
<File baseName={meta.file.baseName} path={meta.file.path} meta={meta.file.meta}>
|
|
53
|
+
{meta.fileTs && importedTypeNames.length > 0 && (
|
|
54
|
+
<File.Import name={Array.from(new Set(importedTypeNames)).sort()} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
|
|
55
|
+
)}
|
|
56
|
+
<File.Import name={['CallToolResult', 'ServerNotification', 'ServerRequest']} path={'@modelcontextprotocol/sdk/types'} isTypeOnly />
|
|
57
|
+
<File.Import name={['RequestHandlerExtra']} path={'@modelcontextprotocol/sdk/shared/protocol'} isTypeOnly />
|
|
58
|
+
<File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />
|
|
59
|
+
{client.importPath ? (
|
|
37
60
|
<>
|
|
38
|
-
<File.Import name={'
|
|
39
|
-
<File.Import name={
|
|
40
|
-
{
|
|
61
|
+
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={client.importPath} isTypeOnly />
|
|
62
|
+
<File.Import name={'fetch'} path={client.importPath} />
|
|
63
|
+
{client.dataReturnType === 'full' && <File.Import name={['ResponseConfig']} path={client.importPath} isTypeOnly />}
|
|
41
64
|
</>
|
|
42
65
|
) : (
|
|
43
66
|
<>
|
|
44
|
-
<File.Import name={['fetch']} root={mcp.file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} />
|
|
45
67
|
<File.Import
|
|
46
68
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
47
|
-
root={
|
|
48
|
-
path={path.resolve(
|
|
69
|
+
root={meta.file.path}
|
|
70
|
+
path={path.resolve(root, '.kubb/fetch.ts')}
|
|
49
71
|
isTypeOnly
|
|
50
72
|
/>
|
|
51
|
-
{
|
|
52
|
-
|
|
73
|
+
<File.Import name={['fetch']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} />
|
|
74
|
+
{client.dataReturnType === 'full' && (
|
|
75
|
+
<File.Import name={['ResponseConfig']} root={meta.file.path} path={path.resolve(root, '.kubb/fetch.ts')} isTypeOnly />
|
|
53
76
|
)}
|
|
54
77
|
</>
|
|
55
78
|
)}
|
|
56
|
-
<File.Import name={['buildFormData']} root={mcp.file.path} path={path.resolve(config.root, config.output.path, '.kubb/config.ts')} />
|
|
57
|
-
<File.Import name={['CallToolResult']} path={'@modelcontextprotocol/sdk/types'} isTypeOnly />
|
|
58
|
-
<File.Import
|
|
59
|
-
name={[
|
|
60
|
-
type.schemas.request?.name,
|
|
61
|
-
type.schemas.response.name,
|
|
62
|
-
type.schemas.pathParams?.name,
|
|
63
|
-
type.schemas.queryParams?.name,
|
|
64
|
-
type.schemas.headerParams?.name,
|
|
65
|
-
...(type.schemas.statusCodes?.map((item) => item.name) || []),
|
|
66
|
-
].filter(Boolean)}
|
|
67
|
-
root={mcp.file.path}
|
|
68
|
-
path={type.file.path}
|
|
69
|
-
isTypeOnly
|
|
70
|
-
/>
|
|
71
79
|
|
|
72
|
-
<
|
|
73
|
-
name={
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
baseURL={
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
dataReturnType={options.client.dataReturnType || 'data'}
|
|
81
|
-
paramsType={'object'}
|
|
82
|
-
paramsCasing={options.client?.paramsCasing || options.paramsCasing}
|
|
83
|
-
pathParamsType={'object'}
|
|
84
|
-
parser={'client'}
|
|
85
|
-
>
|
|
86
|
-
{options.client.dataReturnType === 'data' &&
|
|
87
|
-
`return {
|
|
88
|
-
content: [
|
|
89
|
-
{
|
|
90
|
-
type: 'text',
|
|
91
|
-
text: JSON.stringify(res.data)
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
structuredContent: { data: res.data }
|
|
95
|
-
}`}
|
|
96
|
-
{options.client.dataReturnType === 'full' &&
|
|
97
|
-
`return {
|
|
98
|
-
content: [
|
|
99
|
-
{
|
|
100
|
-
type: 'text',
|
|
101
|
-
text: JSON.stringify(res)
|
|
102
|
-
}
|
|
103
|
-
],
|
|
104
|
-
structuredContent: { data: res.data }
|
|
105
|
-
}`}
|
|
106
|
-
</Client>
|
|
80
|
+
<McpHandler
|
|
81
|
+
name={meta.name}
|
|
82
|
+
node={node}
|
|
83
|
+
resolver={tsResolver}
|
|
84
|
+
baseURL={client.baseURL}
|
|
85
|
+
dataReturnType={client.dataReturnType || 'data'}
|
|
86
|
+
paramsCasing={paramsCasing}
|
|
87
|
+
/>
|
|
107
88
|
</File>
|
|
108
89
|
)
|
|
109
90
|
},
|
|
@@ -1,81 +1,118 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
|
|
4
|
-
import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
|
|
5
|
-
import { pluginTsName } from '@kubb/plugin-ts'
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { ast, defineGenerator } from '@kubb/core'
|
|
6
3
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
7
|
-
import { File } from '@kubb/
|
|
8
|
-
import { Server } from '../components/Server'
|
|
9
|
-
import type { PluginMcp } from '../types'
|
|
4
|
+
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
5
|
+
import { Server } from '../components/Server.tsx'
|
|
6
|
+
import type { PluginMcp } from '../types.ts'
|
|
7
|
+
import { findSuccessStatusCode } from '../utils.ts'
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Default v5 server generator for `@kubb/plugin-mcp`.
|
|
11
|
+
*
|
|
12
|
+
* Uses individual zod schemas for each param (e.g. `createPetsPathUuidSchema`, `createPetsQueryOffsetSchema`)
|
|
13
|
+
* and `resolveResponseStatusName` for per-status response schemas.
|
|
14
|
+
* Query and header params are composed into `z.object({ ... })` from individual schemas.
|
|
15
|
+
*/
|
|
16
|
+
export const serverGenerator = defineGenerator<PluginMcp>({
|
|
12
17
|
name: 'operations',
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const {
|
|
18
|
+
renderer: jsxRenderer,
|
|
19
|
+
operations(nodes, ctx) {
|
|
20
|
+
const { adapter, config, resolver, plugin, driver, root } = ctx
|
|
21
|
+
const { output, paramsCasing, group } = ctx.options
|
|
16
22
|
|
|
17
|
-
const
|
|
18
|
-
|
|
23
|
+
const pluginZod = driver.getPlugin(pluginZodName)
|
|
24
|
+
|
|
25
|
+
if (!pluginZod) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const zodResolver = driver.getResolver(pluginZodName)
|
|
19
30
|
|
|
20
31
|
const name = 'server'
|
|
21
|
-
const
|
|
32
|
+
const serverFilePath = path.resolve(root, output.path, 'server.ts')
|
|
33
|
+
const serverFile = {
|
|
34
|
+
baseName: 'server.ts' as const,
|
|
35
|
+
path: serverFilePath,
|
|
36
|
+
meta: { pluginName: plugin.name },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const jsonFilePath = path.resolve(root, output.path, '.mcp.json')
|
|
40
|
+
const jsonFile = {
|
|
41
|
+
baseName: '.mcp.json' as const,
|
|
42
|
+
path: jsonFilePath,
|
|
43
|
+
meta: { pluginName: plugin.name },
|
|
44
|
+
}
|
|
22
45
|
|
|
23
|
-
const
|
|
46
|
+
const operationsMapped = nodes.map((node) => {
|
|
47
|
+
const casedParams = ast.caseParams(node.parameters, paramsCasing)
|
|
48
|
+
const pathParams = casedParams.filter((p) => p.in === 'path')
|
|
49
|
+
const queryParams = casedParams.filter((p) => p.in === 'query')
|
|
50
|
+
const headerParams = casedParams.filter((p) => p.in === 'header')
|
|
51
|
+
|
|
52
|
+
const mcpFile = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
|
|
53
|
+
|
|
54
|
+
const zodFile = zodResolver.resolveFile(
|
|
55
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
56
|
+
{
|
|
57
|
+
root,
|
|
58
|
+
output: pluginZod.options?.output ?? output,
|
|
59
|
+
group: pluginZod.options?.group,
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) : undefined
|
|
64
|
+
const successStatus = findSuccessStatusCode(node.responses)
|
|
65
|
+
const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) : undefined
|
|
66
|
+
|
|
67
|
+
const resolveParams = (params: typeof pathParams) => params.map((p) => ({ name: p.name, schemaName: zodResolver.resolveParamName(node, p) }))
|
|
24
68
|
|
|
25
|
-
const operationsMapped = operations.map((operation) => {
|
|
26
69
|
return {
|
|
27
70
|
tool: {
|
|
28
|
-
name:
|
|
29
|
-
title:
|
|
30
|
-
description:
|
|
71
|
+
name: node.operationId,
|
|
72
|
+
title: node.summary || undefined,
|
|
73
|
+
description: node.description || `Make a ${node.method.toUpperCase()} request to ${node.path}`,
|
|
31
74
|
},
|
|
32
75
|
mcp: {
|
|
33
|
-
name:
|
|
34
|
-
|
|
35
|
-
suffix: 'handler',
|
|
36
|
-
}),
|
|
37
|
-
file: getFile(operation),
|
|
76
|
+
name: resolver.resolveName(node.operationId),
|
|
77
|
+
file: mcpFile,
|
|
38
78
|
},
|
|
39
79
|
zod: {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
file:
|
|
46
|
-
},
|
|
47
|
-
type: {
|
|
48
|
-
schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
|
|
80
|
+
pathParams: resolveParams(pathParams),
|
|
81
|
+
queryParams: queryParams.length ? resolveParams(queryParams) : undefined,
|
|
82
|
+
headerParams: headerParams.length ? resolveParams(headerParams) : undefined,
|
|
83
|
+
requestName,
|
|
84
|
+
responseName,
|
|
85
|
+
file: zodFile,
|
|
49
86
|
},
|
|
87
|
+
node: node,
|
|
50
88
|
}
|
|
51
89
|
})
|
|
52
90
|
|
|
53
91
|
const imports = operationsMapped.flatMap(({ mcp, zod }) => {
|
|
92
|
+
const zodNames = [
|
|
93
|
+
...zod.pathParams.map((p) => p.schemaName),
|
|
94
|
+
...(zod.queryParams ?? []).map((p) => p.schemaName),
|
|
95
|
+
...(zod.headerParams ?? []).map((p) => p.schemaName),
|
|
96
|
+
zod.requestName,
|
|
97
|
+
zod.responseName,
|
|
98
|
+
].filter(Boolean)
|
|
99
|
+
|
|
100
|
+
const uniqueNames = [...new Set(zodNames)].sort()
|
|
101
|
+
|
|
54
102
|
return [
|
|
55
|
-
<File.Import key={mcp.name} name={[mcp.name]} root={
|
|
56
|
-
<File.Import
|
|
57
|
-
|
|
58
|
-
name={[
|
|
59
|
-
zod.schemas.request?.name,
|
|
60
|
-
zod.schemas.pathParams?.name,
|
|
61
|
-
zod.schemas.queryParams?.name,
|
|
62
|
-
zod.schemas.headerParams?.name,
|
|
63
|
-
zod.schemas.response?.name,
|
|
64
|
-
].filter(Boolean)}
|
|
65
|
-
root={file.path}
|
|
66
|
-
path={zod.file.path}
|
|
67
|
-
/>,
|
|
68
|
-
]
|
|
103
|
+
<File.Import key={mcp.name} name={[mcp.name]} root={serverFile.path} path={mcp.file.path} />,
|
|
104
|
+
uniqueNames.length > 0 && <File.Import key={`zod-${mcp.name}`} name={uniqueNames} root={serverFile.path} path={zod.file.path} />,
|
|
105
|
+
].filter(Boolean)
|
|
69
106
|
})
|
|
70
107
|
|
|
71
108
|
return (
|
|
72
109
|
<>
|
|
73
110
|
<File
|
|
74
|
-
baseName={
|
|
75
|
-
path={
|
|
76
|
-
meta={
|
|
77
|
-
banner={
|
|
78
|
-
footer={
|
|
111
|
+
baseName={serverFile.baseName}
|
|
112
|
+
path={serverFile.path}
|
|
113
|
+
meta={serverFile.meta}
|
|
114
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
115
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
79
116
|
>
|
|
80
117
|
<File.Import name={['McpServer']} path={'@modelcontextprotocol/sdk/server/mcp'} />
|
|
81
118
|
<File.Import name={['z']} path={'zod'} />
|
|
@@ -84,9 +121,9 @@ export const serverGenerator = createReactGenerator<PluginMcp>({
|
|
|
84
121
|
{imports}
|
|
85
122
|
<Server
|
|
86
123
|
name={name}
|
|
87
|
-
serverName={
|
|
88
|
-
serverVersion={
|
|
89
|
-
paramsCasing={
|
|
124
|
+
serverName={adapter.inputNode?.meta?.title ?? 'server'}
|
|
125
|
+
serverVersion={adapter.inputNode?.meta?.version ?? '0.0.0'}
|
|
126
|
+
paramsCasing={paramsCasing}
|
|
90
127
|
operations={operationsMapped}
|
|
91
128
|
/>
|
|
92
129
|
</File>
|
|
@@ -96,10 +133,10 @@ export const serverGenerator = createReactGenerator<PluginMcp>({
|
|
|
96
133
|
{`
|
|
97
134
|
{
|
|
98
135
|
"mcpServers": {
|
|
99
|
-
"${
|
|
136
|
+
"${adapter.inputNode?.meta?.title || 'server'}": {
|
|
100
137
|
"type": "stdio",
|
|
101
138
|
"command": "npx",
|
|
102
|
-
"args": ["tsx", "${
|
|
139
|
+
"args": ["tsx", "${path.relative(path.dirname(jsonFile.path), serverFile.path)}"]
|
|
103
140
|
}
|
|
104
141
|
}
|
|
105
142
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export { McpHandler } from './components/McpHandler.tsx'
|
|
2
|
+
export { Server } from './components/Server.tsx'
|
|
3
|
+
|
|
4
|
+
export { mcpGenerator } from './generators/mcpGenerator.tsx'
|
|
5
|
+
export { serverGenerator } from './generators/serverGenerator.tsx'
|
|
6
|
+
|
|
7
|
+
export { default, pluginMcp, pluginMcpName } from './plugin.ts'
|
|
8
|
+
|
|
9
|
+
export { resolverMcp } from './resolvers/resolverMcp.ts'
|
|
10
|
+
|
|
11
|
+
export type { PluginMcp, ResolverMcp } from './types.ts'
|
package/src/plugin.ts
CHANGED
|
@@ -1,170 +1,122 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { camelCase } from '@internals/utils'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { ast, definePlugin, type Group } from '@kubb/core'
|
|
4
5
|
import { pluginClientName } from '@kubb/plugin-client'
|
|
5
6
|
import { source as axiosClientSource } from '@kubb/plugin-client/templates/clients/axios.source'
|
|
6
7
|
import { source as fetchClientSource } from '@kubb/plugin-client/templates/clients/fetch.source'
|
|
7
8
|
import { source as configSource } from '@kubb/plugin-client/templates/config.source'
|
|
8
|
-
import { OperationGenerator, pluginOasName } from '@kubb/plugin-oas'
|
|
9
9
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
10
10
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
11
|
-
import { mcpGenerator
|
|
11
|
+
import { mcpGenerator } from './generators/mcpGenerator.tsx'
|
|
12
|
+
import { serverGenerator } from './generators/serverGenerator.tsx'
|
|
13
|
+
import { resolverMcp } from './resolvers/resolverMcp.ts'
|
|
12
14
|
import type { PluginMcp } from './types.ts'
|
|
13
15
|
|
|
14
16
|
export const pluginMcpName = 'plugin-mcp' satisfies PluginMcp['name']
|
|
15
17
|
|
|
16
|
-
export const pluginMcp =
|
|
18
|
+
export const pluginMcp = definePlugin<PluginMcp>((options) => {
|
|
17
19
|
const {
|
|
18
20
|
output = { path: 'mcp', barrelType: 'named' },
|
|
19
21
|
group,
|
|
20
22
|
exclude = [],
|
|
21
23
|
include,
|
|
22
24
|
override = [],
|
|
23
|
-
transformers = {},
|
|
24
|
-
generators = [mcpGenerator, serverGenerator].filter(Boolean),
|
|
25
|
-
contentType,
|
|
26
25
|
paramsCasing,
|
|
27
26
|
client,
|
|
27
|
+
resolver: userResolver,
|
|
28
|
+
transformer: userTransformer,
|
|
29
|
+
generators: userGenerators = [],
|
|
28
30
|
} = options
|
|
29
31
|
|
|
30
32
|
const clientName = client?.client ?? 'axios'
|
|
31
33
|
const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : undefined)
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
group,
|
|
38
|
-
paramsCasing,
|
|
39
|
-
client: {
|
|
40
|
-
client: clientName,
|
|
41
|
-
clientType: client?.clientType ?? 'function',
|
|
42
|
-
importPath: clientImportPath,
|
|
43
|
-
dataReturnType: client?.dataReturnType ?? 'data',
|
|
44
|
-
bundle: client?.bundle,
|
|
45
|
-
baseURL: client?.baseURL,
|
|
46
|
-
paramsCasing: client?.paramsCasing,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
pre: [pluginOasName, pluginTsName, pluginZodName].filter(Boolean),
|
|
50
|
-
resolvePath(baseName, pathMode, options) {
|
|
51
|
-
const root = path.resolve(this.config.root, this.config.output.path)
|
|
52
|
-
const mode = pathMode ?? getMode(path.resolve(root, output.path))
|
|
53
|
-
|
|
54
|
-
if (mode === 'single') {
|
|
55
|
-
/**
|
|
56
|
-
* when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
|
|
57
|
-
* Other plugins then need to call addOrAppend instead of just add from the fileManager class
|
|
58
|
-
*/
|
|
59
|
-
return path.resolve(root, output.path)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (group && (options?.group?.path || options?.group?.tag)) {
|
|
63
|
-
const groupName: Group['name'] = group?.name
|
|
35
|
+
const groupConfig = group
|
|
36
|
+
? ({
|
|
37
|
+
...group,
|
|
38
|
+
name: group.name
|
|
64
39
|
? group.name
|
|
65
|
-
: (ctx) => {
|
|
66
|
-
if (group
|
|
40
|
+
: (ctx: { group: string }) => {
|
|
41
|
+
if (group.type === 'path') {
|
|
67
42
|
return `${ctx.group.split('/')[1]}`
|
|
68
43
|
}
|
|
69
44
|
return `${camelCase(ctx.group)}Requests`
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return path.resolve(
|
|
73
|
-
root,
|
|
74
|
-
output.path,
|
|
75
|
-
groupName({
|
|
76
|
-
group: group.type === 'path' ? options.group.path! : options.group.tag!,
|
|
77
|
-
}),
|
|
78
|
-
baseName,
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return path.resolve(root, output.path, baseName)
|
|
83
|
-
},
|
|
84
|
-
resolveName(name, type) {
|
|
85
|
-
const resolvedName = camelCase(name, {
|
|
86
|
-
isFile: type === 'file',
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
if (type) {
|
|
90
|
-
return transformers?.name?.(resolvedName, type) || resolvedName
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return resolvedName
|
|
94
|
-
},
|
|
95
|
-
async install() {
|
|
96
|
-
const root = path.resolve(this.config.root, this.config.output.path)
|
|
97
|
-
const mode = getMode(path.resolve(root, output.path))
|
|
98
|
-
const oas = await this.getOas()
|
|
99
|
-
const baseURL = await this.getBaseURL()
|
|
100
|
-
|
|
101
|
-
if (baseURL) {
|
|
102
|
-
this.plugin.options.client.baseURL = baseURL
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const hasClientPlugin = !!this.driver.getPluginByName(pluginClientName)
|
|
106
|
-
|
|
107
|
-
if (this.plugin.options.client.bundle && !hasClientPlugin && !this.plugin.options.client.importPath) {
|
|
108
|
-
// pre add bundled fetch
|
|
109
|
-
await this.addFile({
|
|
110
|
-
baseName: 'fetch.ts',
|
|
111
|
-
path: path.resolve(root, '.kubb/fetch.ts'),
|
|
112
|
-
sources: [
|
|
113
|
-
{
|
|
114
|
-
name: 'fetch',
|
|
115
|
-
value: this.plugin.options.client.client === 'fetch' ? fetchClientSource : axiosClientSource,
|
|
116
|
-
isExportable: true,
|
|
117
|
-
isIndexable: true,
|
|
118
45
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
exports: [],
|
|
122
|
-
})
|
|
123
|
-
}
|
|
46
|
+
} satisfies Group)
|
|
47
|
+
: undefined
|
|
124
48
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
49
|
+
return {
|
|
50
|
+
name: pluginMcpName,
|
|
51
|
+
options,
|
|
52
|
+
dependencies: [pluginTsName, pluginZodName],
|
|
53
|
+
hooks: {
|
|
54
|
+
'kubb:plugin:setup'(ctx) {
|
|
55
|
+
const resolver = userResolver ? { ...resolverMcp, ...userResolver } : resolverMcp
|
|
56
|
+
|
|
57
|
+
ctx.setOptions({
|
|
58
|
+
output,
|
|
59
|
+
exclude,
|
|
60
|
+
include,
|
|
61
|
+
override,
|
|
62
|
+
group: groupConfig,
|
|
63
|
+
paramsCasing,
|
|
64
|
+
client: {
|
|
65
|
+
client: clientName,
|
|
66
|
+
clientType: client?.clientType ?? 'function',
|
|
67
|
+
importPath: clientImportPath,
|
|
68
|
+
dataReturnType: client?.dataReturnType ?? 'data',
|
|
69
|
+
bundle: client?.bundle,
|
|
70
|
+
baseURL: client?.baseURL,
|
|
71
|
+
paramsCasing: client?.paramsCasing,
|
|
72
|
+
},
|
|
73
|
+
resolver,
|
|
139
74
|
})
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
75
|
+
ctx.setResolver(resolver)
|
|
76
|
+
if (userTransformer) {
|
|
77
|
+
ctx.setTransformer(userTransformer)
|
|
78
|
+
}
|
|
79
|
+
ctx.addGenerator(mcpGenerator)
|
|
80
|
+
ctx.addGenerator(serverGenerator)
|
|
81
|
+
for (const gen of userGenerators) {
|
|
82
|
+
ctx.addGenerator(gen)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const root = path.resolve(ctx.config.root, ctx.config.output.path)
|
|
86
|
+
const hasClientPlugin = ctx.config.plugins?.some((p) => p.name === pluginClientName)
|
|
87
|
+
|
|
88
|
+
if (client?.bundle && !hasClientPlugin && !clientImportPath) {
|
|
89
|
+
ctx.injectFile({
|
|
90
|
+
baseName: 'fetch.ts',
|
|
91
|
+
path: path.resolve(root, '.kubb/fetch.ts'),
|
|
92
|
+
sources: [
|
|
93
|
+
ast.createSource({
|
|
94
|
+
name: 'fetch',
|
|
95
|
+
nodes: [ast.createText(clientName === 'fetch' ? fetchClientSource : axiosClientSource)],
|
|
96
|
+
isExportable: true,
|
|
97
|
+
isIndexable: true,
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!hasClientPlugin) {
|
|
104
|
+
ctx.injectFile({
|
|
105
|
+
baseName: 'config.ts',
|
|
106
|
+
path: path.resolve(root, '.kubb/config.ts'),
|
|
107
|
+
sources: [
|
|
108
|
+
ast.createSource({
|
|
109
|
+
name: 'config',
|
|
110
|
+
nodes: [ast.createText(configSource)],
|
|
111
|
+
isExportable: false,
|
|
112
|
+
isIndexable: false,
|
|
113
|
+
}),
|
|
114
|
+
],
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
},
|
|
168
118
|
},
|
|
169
119
|
}
|
|
170
120
|
})
|
|
121
|
+
|
|
122
|
+
export default pluginMcp
|