mcpPlugin
Install an MCP (Model Context Protocol) endpoint so AI assistants can call your API as tools.
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@betabun add @kizunajs/mcp@betanpm install @kizunajs/mcp@betaimport { mcpPlugin } from '@kizunajs/mcp';Parameters
mcpPlugin(props?: McpPluginProps): PluginDeclaration| Parameter | Type | Required | Description |
|---|---|---|---|
props | McpPluginProps | No | Configuration options |
Props
| Option | Default | Description |
|---|---|---|
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 |
instructions | built from your tags | Appended to the generated overview |
oauth | off | Serve 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:
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
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',
}),
],
});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:
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:
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
- Tools for what a route's
tooldeclares createMcpServerto drive the transport yourself