@kubb/mcp 5.0.0-beta.1 → 5.0.0-beta.100

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.
@@ -0,0 +1,30 @@
1
+ import { createRequire } from "node:module";
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __name = (target, value) => __defProp(target, "name", {
6
+ value,
7
+ configurable: true
8
+ });
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
29
+ //#endregion
30
+ export { __toESM as i, __name as n, __require as r, __commonJSMin as t };
package/package.json CHANGED
@@ -1,17 +1,15 @@
1
1
  {
2
2
  "name": "@kubb/mcp",
3
- "version": "5.0.0-beta.1",
4
- "description": "Model Context Protocol (MCP) server for Kubb, enabling AI assistants to generate code from OpenAPI specifications.",
3
+ "version": "5.0.0-beta.100",
4
+ "description": "MCP server for Kubb. Exposes code generation as a tool over the Model Context Protocol so AI assistants like Claude, Cursor, and other MCP-compatible clients can generate TypeScript types, clients, and more.",
5
5
  "keywords": [
6
6
  "ai",
7
- "code-generation",
8
7
  "codegen",
9
8
  "kubb",
10
9
  "llm",
11
10
  "mcp",
11
+ "meta-framework",
12
12
  "model-context-protocol",
13
- "openapi",
14
- "swagger",
15
13
  "typescript"
16
14
  ],
17
15
  "license": "MIT",
@@ -25,7 +23,6 @@
25
23
  "kubb-mcp": "bin/kubb-mcp.cjs"
26
24
  },
27
25
  "files": [
28
- "src",
29
26
  "dist",
30
27
  "bin",
31
28
  "!/**/**.test.**",
@@ -49,38 +46,35 @@
49
46
  "registry": "https://registry.npmjs.org/"
50
47
  },
51
48
  "dependencies": {
52
- "@modelcontextprotocol/sdk": "^1.29.0",
53
- "unrun": "^0.2.37",
54
- "zod": "^4.3.6",
55
- "@kubb/core": "5.0.0-beta.1"
49
+ "@tmcp/adapter-valibot": "^0.1.6",
50
+ "@tmcp/transport-stdio": "^0.4.3",
51
+ "tmcp": "^1.19.4",
52
+ "valibot": "^1.4.2",
53
+ "@kubb/core": "5.0.0-beta.100",
54
+ "@kubb/adapter-oas": "5.0.0-beta.100"
56
55
  },
57
56
  "devDependencies": {
58
57
  "@internals/utils": "0.0.0",
59
- "@kubb/renderer-jsx": "5.0.0-beta.1"
58
+ "@kubb/renderer-jsx": "5.0.0-beta.100",
59
+ "@internals/shared": "0.0.0"
60
60
  },
61
61
  "peerDependencies": {
62
- "@kubb/renderer-jsx": "5.0.0-beta.1"
62
+ "@kubb/renderer-jsx": "5.0.0-beta.100"
63
63
  },
64
- "size-limit": [
65
- {
66
- "path": "./dist/*.js",
67
- "limit": "510 KiB",
68
- "gzip": true
69
- }
70
- ],
71
64
  "engines": {
72
65
  "node": ">=22"
73
66
  },
67
+ "inlinedDependencies": {
68
+ "jiti": "2.7.0"
69
+ },
74
70
  "scripts": {
75
- "build": "tsdown && size-limit",
76
- "start:mcp": "bun --watch ./src/server.ts",
71
+ "build": "tsdown",
72
+ "clean": "node -e \"require('node:fs').rmSync('./dist', {recursive:true,force:true})\"",
77
73
  "debug:mcp": "npx -y @modelcontextprotocol/inspector node ./bin/kubb-mcp.cjs",
78
- "clean": "npx rimraf ./dist",
79
74
  "lint": "oxlint .",
80
75
  "lint:fix": "oxlint --fix .",
81
- "release": "pnpm publish --no-git-check",
82
- "release:canary": "bash ../../.github/canary.sh && node ../../scripts/build.js canary && pnpm publish --no-git-check",
83
76
  "start": "tsdown --watch",
77
+ "start:mcp": "bun --watch ./src/server.ts",
84
78
  "test": "vitest --passWithNoTests",
85
79
  "typecheck": "tsc -p ./tsconfig.json --noEmit --emitDeclarationOnly false"
86
80
  }
@@ -1,8 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __defProp = Object.defineProperty;
3
- var __name = (target, value) => __defProp(target, "name", {
4
- value,
5
- configurable: true
6
- });
7
- //#endregion
8
- export { __name as t };
package/src/constants.ts DELETED
@@ -1 +0,0 @@
1
- export const ALLOWED_CONFIG_EXTENSIONS = new Set(['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'])
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- import { startServer } from './server.ts'
2
-
3
- export async function run(_argv?: string[]): Promise<void> {
4
- await startServer()
5
- }
@@ -1,12 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- export const generateSchema = z.object({
4
- config: z
5
- .string()
6
- .optional()
7
-
8
- .describe('Path to kubb.config file (supports .ts, .js, .cjs). If not provided, will look for kubb.config.{ts,js,cjs} in current directory'),
9
- input: z.string().optional().describe('Path to OpenAPI/Swagger spec file (overrides config)'),
10
- output: z.string().optional().describe('Output directory path (overrides config)'),
11
- logLevel: z.enum(['silent', 'error', 'warn', 'info', 'verbose', 'debug']).optional().default('info').describe('Log level for build output'),
12
- })
package/src/server.ts DELETED
@@ -1,48 +0,0 @@
1
- import process from 'node:process'
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
- import { version } from '../package.json'
5
- import { generateSchema } from './schemas/generateSchema.ts'
6
- import { generate } from './tools/generate.ts'
7
-
8
- /**
9
- * Kubb MCP Server
10
- *
11
- * Provides tools for building OpenAPI specifications using Kubb.
12
- */
13
- export async function startServer() {
14
- try {
15
- const transport = new StdioServerTransport()
16
- const server = new McpServer({
17
- name: 'Kubb',
18
- version,
19
- })
20
-
21
- // Build tool - runs Kubb build using @kubb/core
22
- // Wrapped to pass notification handler for real-time progress updates
23
- server.tool('generate', 'Generate OpenAPI spec helpers using Kubb configuration', generateSchema.shape, async (args) => {
24
- // Create notification handler that sends events back to the client
25
- const notificationHandler = {
26
- sendNotification: async (method: string, params: any) => {
27
- try {
28
- await transport.send({
29
- jsonrpc: '2.0',
30
- method,
31
- params,
32
- })
33
- } catch (error) {
34
- console.error('Failed to send notification:', error)
35
- }
36
- },
37
- }
38
-
39
- // Call build tool with notification handler
40
- return generate(args, notificationHandler)
41
- })
42
-
43
- await server.connect(transport)
44
- } catch (error) {
45
- console.error('Failed to start MCP server:', error)
46
- process.exit(1)
47
- }
48
- }
@@ -1,210 +0,0 @@
1
- import { AsyncEventEmitter } from '@internals/utils'
2
- import { type Config, createKubb, type KubbHooks } from '@kubb/core'
3
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.d.ts'
4
- import type { z } from 'zod'
5
- import type { generateSchema } from '../schemas/generateSchema.ts'
6
- import { NotifyTypes } from '../types.ts'
7
- import { loadUserConfig } from '../utils/loadUserConfig.ts'
8
- import { resolveCwd } from '../utils/resolveCwd.ts'
9
- import { resolveUserConfig } from '../utils/resolveUserConfig.ts'
10
-
11
- interface NotificationHandler {
12
- sendNotification(method: string, params: unknown): Promise<void>
13
- }
14
-
15
- /**
16
- * Build tool that generates code from OpenAPI specs using Kubb.
17
- * Sends real-time notifications of build progress and events.
18
- */
19
- export async function generate(schema: z.infer<typeof generateSchema>, handler: NotificationHandler): Promise<CallToolResult> {
20
- const { config: configPath, input, output, logLevel } = schema
21
-
22
- try {
23
- const hooks = new AsyncEventEmitter<KubbHooks>()
24
- const messages: string[] = []
25
-
26
- // Helper to send notifications
27
- const notify = async (type: string, message: string, data?: Record<string, unknown>) => {
28
- messages.push(`${type}: ${message}`)
29
-
30
- await handler.sendNotification('kubb/progress', {
31
- type,
32
- message,
33
- timestamp: new Date().toISOString(),
34
- ...data,
35
- })
36
- }
37
-
38
- // Capture events for output and send notifications
39
- hooks.on('kubb:info', async ({ message }: { message: string }) => {
40
- await notify(NotifyTypes.INFO, message)
41
- })
42
-
43
- hooks.on('kubb:success', async ({ message }: { message: string }) => {
44
- await notify(NotifyTypes.SUCCESS, message)
45
- })
46
-
47
- hooks.on('kubb:error', async ({ error }: { error: Error }) => {
48
- await notify(NotifyTypes.ERROR, error.message, { stack: error.stack })
49
- })
50
-
51
- hooks.on('kubb:warn', async ({ message }: { message: string }) => {
52
- await notify(NotifyTypes.WARN, message)
53
- })
54
-
55
- // Plugin lifecycle events
56
- hooks.on('kubb:plugin:start', async ({ plugin }) => {
57
- await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`)
58
- })
59
-
60
- hooks.on('kubb:plugin:end', async ({ plugin, duration }) => {
61
- await notify(NotifyTypes.PLUGIN_END, `Plugin finished: ${plugin.name}`, {
62
- duration,
63
- })
64
- })
65
-
66
- // File processing events
67
- hooks.on('kubb:files:processing:start', async () => {
68
- await notify(NotifyTypes.FILES_START, 'Starting file processing')
69
- })
70
-
71
- hooks.on('kubb:file:processing:update', async ({ file }: { file: { name: string } }) => {
72
- await notify(NotifyTypes.FILE_UPDATE, `Processing file: ${file.name}`)
73
- })
74
-
75
- hooks.on('kubb:files:processing:end', async () => {
76
- await notify(NotifyTypes.FILES_END, 'File processing complete')
77
- })
78
-
79
- // Generation events
80
- hooks.on('kubb:generation:start', async () => {
81
- await notify(NotifyTypes.GENERATION_START, 'Generation started')
82
- })
83
-
84
- hooks.on('kubb:generation:end', async () => {
85
- await notify(NotifyTypes.GENERATION_END, 'Generation ended')
86
- })
87
-
88
- // Load and process configuration
89
- let userConfig: Config
90
- let cwd: string
91
-
92
- try {
93
- const configResult = await loadUserConfig(configPath, { notify })
94
- userConfig = configResult.userConfig
95
- cwd = configResult.cwd
96
-
97
- if (Array.isArray(userConfig) && userConfig.length) {
98
- throw new Error('Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.')
99
- }
100
-
101
- userConfig = await resolveUserConfig(userConfig, {
102
- configPath,
103
- logLevel,
104
- })
105
- } catch (error) {
106
- const errorMessage = error instanceof Error ? error.message : String(error)
107
- await notify(NotifyTypes.CONFIG_ERROR, errorMessage)
108
- return {
109
- content: [
110
- {
111
- type: 'text',
112
- text: errorMessage,
113
- },
114
- ],
115
- isError: true,
116
- }
117
- }
118
-
119
- const inputPath = input ?? ('path' in userConfig.input ? userConfig.input.path : undefined)
120
-
121
- // Override config with CLI options
122
- const config: Config = {
123
- ...userConfig,
124
- root: resolveCwd(userConfig, cwd),
125
- input: inputPath
126
- ? {
127
- ...userConfig.input,
128
- path: inputPath,
129
- }
130
- : userConfig.input,
131
- output: output
132
- ? {
133
- ...userConfig.output,
134
- path: output,
135
- }
136
- : userConfig.output,
137
- }
138
-
139
- await notify(NotifyTypes.CONFIG_READY, 'Configuration ready', {
140
- root: config.root,
141
- })
142
-
143
- // Setup and build
144
- await notify(NotifyTypes.SETUP_START, 'Setting up Kubb')
145
-
146
- const kubb = createKubb(config, { hooks })
147
- await kubb.setup()
148
- await notify(NotifyTypes.SETUP_END, 'Kubb setup complete')
149
-
150
- await notify(NotifyTypes.BUILD_START, 'Starting build')
151
- const { files, failedPlugins, error } = await kubb.safeBuild()
152
- await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`)
153
-
154
- if (error || failedPlugins.size > 0) {
155
- const allErrors: Error[] = [
156
- error,
157
- ...Array.from(failedPlugins)
158
- .filter((it) => it.error)
159
- .map((it) => it.error),
160
- ].filter(Boolean)
161
-
162
- await notify(NotifyTypes.BUILD_FAILED, `Build failed with ${allErrors.length} error(s)`, {
163
- errorCount: allErrors.length,
164
- errors: allErrors.map((err) => err.message),
165
- })
166
-
167
- return {
168
- content: [
169
- {
170
- type: 'text',
171
- text: `Build failed:\n${allErrors.map((err) => err.message).join('\n')}\n\n${messages.join('\n')}`,
172
- },
173
- ],
174
- isError: true,
175
- }
176
- }
177
-
178
- await notify(NotifyTypes.BUILD_SUCCESS, `Build completed successfully - Generated ${files.length} files`, {
179
- filesCount: files.length,
180
- })
181
-
182
- return {
183
- content: [
184
- {
185
- type: 'text',
186
- text: `Build completed successfully!\n\nGenerated ${files.length} files\n\n${messages.join('\n')}`,
187
- },
188
- ],
189
- }
190
- } catch (caughtError) {
191
- const error = caughtError as Error
192
-
193
- await handler.sendNotification('kubb/progress', {
194
- type: NotifyTypes.FATAL_ERROR,
195
- message: error.message,
196
- stack: error.stack,
197
- timestamp: new Date().toISOString(),
198
- })
199
-
200
- return {
201
- content: [
202
- {
203
- type: 'text',
204
- text: `Build error: ${error.message}\n${error.stack || ''}`,
205
- },
206
- ],
207
- isError: true,
208
- }
209
- }
210
- }
package/src/types.ts DELETED
@@ -1,23 +0,0 @@
1
- export const NotifyTypes = {
2
- INFO: 'INFO',
3
- SUCCESS: 'SUCCESS',
4
- ERROR: 'ERROR',
5
- WARN: 'WARN',
6
- PLUGIN_START: 'PLUGIN_START',
7
- PLUGIN_END: 'PLUGIN_END',
8
- FILES_START: 'FILES_START',
9
- FILE_UPDATE: 'FILE_UPDATE',
10
- FILES_END: 'FILES_END',
11
- GENERATION_START: 'GENERATION_START',
12
- GENERATION_END: 'GENERATION_END',
13
- CONFIG_LOADED: 'CONFIG_LOADED',
14
- CONFIG_ERROR: 'CONFIG_ERROR',
15
- CONFIG_READY: 'CONFIG_READY',
16
- SETUP_START: 'SETUP_START',
17
- SETUP_END: 'SETUP_END',
18
- BUILD_START: 'BUILD_START',
19
- BUILD_END: 'BUILD_END',
20
- BUILD_FAILED: 'BUILD_FAILED',
21
- BUILD_SUCCESS: 'BUILD_SUCCESS',
22
- FATAL_ERROR: 'FATAL_ERROR',
23
- } as const
@@ -1,77 +0,0 @@
1
- import { existsSync } from 'node:fs'
2
- import path from 'node:path'
3
- import type { Config } from '@kubb/core'
4
- import { unrun } from 'unrun'
5
- import { ALLOWED_CONFIG_EXTENSIONS } from '../constants.ts'
6
- import { NotifyTypes } from '../types.ts'
7
-
8
- type NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => Promise<void>
9
-
10
- const loadedModules = new Map<string, unknown>()
11
-
12
- async function loadModule(filePath: string): Promise<unknown> {
13
- const ext = path.extname(filePath)
14
- if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
15
- throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(', ')}`)
16
- }
17
- if (loadedModules.has(filePath)) {
18
- return loadedModules.get(filePath)
19
- }
20
- const { module } = await unrun({ path: filePath })
21
- loadedModules.set(filePath, module)
22
- return module
23
- }
24
-
25
- export async function loadUserConfig(configPath: string | undefined, { notify }: { notify: NotifyFunction }): Promise<{ userConfig: Config; cwd: string }> {
26
- let userConfig: Config | undefined
27
- let cwd: string
28
-
29
- if (configPath) {
30
- const ext = path.extname(configPath)
31
- if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
32
- const msg = `Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(', ')}`
33
- await notify(NotifyTypes.CONFIG_ERROR, msg)
34
- throw new Error(msg)
35
- }
36
- const base = path.resolve(process.cwd())
37
- const resolvedConfigPath = path.resolve(base, configPath)
38
- const relative = path.relative(base, resolvedConfigPath)
39
- if (relative.startsWith('..') || path.isAbsolute(relative)) {
40
- const msg = 'Invalid config file path: must be within the current working directory'
41
- await notify(NotifyTypes.CONFIG_ERROR, msg)
42
- throw new Error(msg)
43
- }
44
- cwd = path.dirname(resolvedConfigPath)
45
-
46
- try {
47
- userConfig = (await loadModule(resolvedConfigPath)) as Config
48
- await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`)
49
- } catch (error) {
50
- await notify(NotifyTypes.CONFIG_ERROR, `Failed to load config: ${error instanceof Error ? error.message : String(error)}`)
51
- throw new Error(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`)
52
- }
53
- } else {
54
- cwd = process.cwd()
55
- const configFileNames = ['kubb.config.ts', 'kubb.config.mts', 'kubb.config.cts', 'kubb.config.js', 'kubb.config.cjs']
56
-
57
- for (const configFileName of configFileNames) {
58
- const configFilePath = path.resolve(process.cwd(), configFileName)
59
- if (!existsSync(configFilePath)) continue
60
- try {
61
- userConfig = (await loadModule(configFilePath)) as Config
62
- await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`)
63
- break
64
- } catch {
65
- // Continue trying next config file
66
- }
67
- }
68
-
69
- if (!userConfig) {
70
- await notify(NotifyTypes.CONFIG_ERROR, 'No config file found')
71
-
72
- throw new Error(`No config file found. Please provide a config path or create one of: ${configFileNames.join(', ')}`)
73
- }
74
- }
75
-
76
- return { userConfig: userConfig!, cwd }
77
- }
@@ -1,20 +0,0 @@
1
- import path from 'node:path'
2
- import type { Config } from '@kubb/core'
3
-
4
- /**
5
- * Determine the root directory based on userConfig.root and resolvedConfigDir
6
- * 1. If userConfig.root exists and is absolute, use it as-is
7
- * 2. If userConfig.root exists and is relative, resolve it relative to config directory
8
- * 3. Otherwise, use the config directory as root
9
- */
10
- export function resolveCwd(userConfig: Config, cwd: string): string {
11
- if (userConfig.root) {
12
- if (path.isAbsolute(userConfig.root)) {
13
- return userConfig.root
14
- }
15
-
16
- return path.resolve(cwd, userConfig.root)
17
- }
18
-
19
- return cwd
20
- }
@@ -1,28 +0,0 @@
1
- import { isPromise } from '@internals/utils'
2
- import type { CLIOptions, Config } from '@kubb/core'
3
-
4
- export type ResolveUserConfigOptions = {
5
- configPath?: string
6
- logLevel?: string
7
- }
8
-
9
- /**
10
- * Resolve the config by handling function configs and returning the final configuration
11
- */
12
- export async function resolveUserConfig(config: Config, options: ResolveUserConfigOptions): Promise<Config> {
13
- let kubbUserConfig = Promise.resolve(config) as Promise<Config>
14
-
15
- if (typeof config === 'function') {
16
- const possiblePromise = (config as any)({
17
- logLevel: options.logLevel,
18
- config: options.configPath,
19
- } as CLIOptions)
20
- if (isPromise(possiblePromise)) {
21
- kubbUserConfig = possiblePromise
22
- } else {
23
- kubbUserConfig = Promise.resolve(possiblePromise)
24
- }
25
- }
26
-
27
- return (await kubbUserConfig) as Config
28
- }