API Reference

mcpPlugin

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

Beta

MCP support is new and still settling. The plugin options and the published tool surface may change while v2 is in beta, so pin your version if you depend on them.

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
bun add @kizunajs/mcp@beta
npm install @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, and the key it installs at
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 tagsAppended 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.

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);

Two endpoints

slug is what a plugin installs at, so give a second MCP endpoint its own:

kizuna.config.ts
plugins: [
    mcpPlugin({
        name: 'My API',
    }),
    mcpPlugin({
        slug: 'internalMcp',
        path: '/internal/mcp',
        name: 'My API, internal',
    }),
],

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: it lives outside api.routes, so your fetch client, the OpenAPI document, and the generated Swift and Kotlin clients never list it.

See also

On this page