Migration: @kubb/plugin-mcp
Part of the v4 → v5 migration guide. For the full option reference, see @kubb/plugin-mcp.
resolver.resolveName replaces transformers.name, and the generators option is gone.
client selects a registered client plugin
In v4, client was an object that configured a bundled client: clientType, dataReturnType, baseURL, bundle, importPath, and paramsCasing. v5 drops all of those. The handlers now call a registered client plugin, so client is a single string that picks which one: 'axios' or 'fetch'. Register @kubb/plugin-axios or @kubb/plugin-fetch in plugins, and set baseURL there instead of on pluginMcp. When exactly one client plugin is registered, Kubb auto-detects it and the string is optional.
pluginMcp also depends on @kubb/plugin-ts and @kubb/plugin-zod, so register both alongside the client plugin.
import { defineConfig } from '@kubb/core'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginMcp } from '@kubb/plugin-mcp'
export default defineConfig({
input: { path: './petStore.yaml' },
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginZod(),
pluginMcp({
client: {
client: 'fetch',
baseURL: 'https://petstore.swagger.io/v2',
},
}),
],
})import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginFetch } from '@kubb/plugin-fetch'
import { pluginMcp } from '@kubb/plugin-mcp'
export default defineConfig({
input: { path: './petStore.yaml' },
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginZod(),
pluginFetch({ baseURL: 'https://petstore.swagger.io/v2' }),
pluginMcp({ client: 'fetch' }),
],
})Removed: paramsCasing
pluginMcp({ paramsCasing: 'camelcase' })Parameter properties in the generated handlers are now always camelCase, including the client.paramsCasing sub-option, so drop both. The HTTP layer still uses the original spec names, and Kubb writes the mapping for you.
Generated output
Each handler now takes a second argument, the MCP RequestHandlerExtra object, so it can read the request context. The handler no longer builds the request inline. Instead it calls the named operation from the registered client plugin (addPet here) with a single grouped { path, query, headers, body } config object, and reads res.data.
import type { CallToolResult } from '@modelcontextprotocol/sdk/types'
import type { CallToolResult, ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types'
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol'
import { addPet } from './clients/addPet'
export async function addPetHandler({ data }: { data: AddPetMutationRequest }): Promise<CallToolResult> {
export async function addPetHandler(
{ body }: AddPetRequestConfig,
request: RequestHandlerExtra<ServerRequest, ServerNotification>,
): Promise<CallToolResult> {
const res = await fetch<AddPetMutationResponse, ResponseErrorConfig<AddPet405>, AddPetMutationRequest>({
method: 'POST',
url: '/pet',
baseURL: 'https://petstore.swagger.io/v2',
data,
})
const res = await addPet({ body })
...
}