mcpPlugin

Install an MCP (Model Context Protocol) endpoint so AI assistants can call your API as tools.

Serve an MCP (Model Context Protocol) endpoint as part of your API. Name it under plugins on defineConfig and api.mount serves it, on every adapter.

pnpm add @kizunajs/mcp@beta
import { mcpPlugin } from '@kizunajs/mcp';

Parameters

mcpPlugin(props?: McpPluginProps): PluginDeclaration
ParameterTypeRequiredDescription
propsMcpPluginPropsNoConfiguration options

Props

OptionDefaultDescription
slug'mcp'What handlers reach this plugin under
path'/mcp'Path the endpoint is served from
name'MCP Server'Human-readable name shown to AI assistants
version'1.0.0'Semantic version string
instructionsbuilt from your groupsAppended to the generated overview
oauthoffServe the endpoint as an OAuth 2.1 resource server (guide)

Which routes it publishes

The plugin takes no list of routes. Each route decides for itself, by declaring tool:

src/routes/weather.ts
getForecast: k
    .route({
        method: 'GET',
        path: '/forecast/:city',
        summary: 'Look up tomorrow forecast for one city',
        tool: true,
        responses: {
            200: ForecastSchema,
        },
    })
    .handler(/* ... */),

A route with no tool is an HTTP endpoint and nothing more. Two shapes never publish even with it: a route whose contentType is multipart/form-data or application/x-www-form-urlencoded, because tool input is JSON, and a route whose response streams, because a tool result is one value. A hidden route can't say tool: the two together are a type error.

Example

kizuna.config.ts
import { defineConfig } from 'kizunajs';
import { expressAdapter } from '@kizunajs/express';
import { mcpPlugin } from '@kizunajs/mcp';
import { routes } from './src/routes';

export default defineConfig({
    adapter: expressAdapter(),
    routes,
    plugins: [
        mcpPlugin({
            name: 'My API',
        }),
    ],
});
src/index.ts
import kizuna from '../kizuna.config';

kizuna.api.mount(app);

Next.js

The endpoint is served by the catch-all route file that already serves your routes, so path resolves under its basePath:

src/app/api/[...kizuna]/route.ts
export const { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS } = kizuna.api.mount({
    basePath: '/api',
});

With the default path, the endpoint is at /api/mcp.

Secured routes

Each tool call runs the same guard pipeline as an HTTP request, reading credentials from the MCP transport request's headers. A deny(...) becomes a tool error result; with oauth configured, denials from that identity answer at the HTTP level instead. See the MCP guide for configuring clients to send credentials.

Plugin route

The endpoint is a plugin route, which is hidden, so your fetch client, the OpenAPI document, and the generated Swift and Kotlin clients never list it.

See also