PackagesPlugin Packages
@bunli/plugin-mcp
Create CLI commands from MCP (Model Context Protocol) tool schemas
Installation
bun add @bunli/plugin-mcpOverview
@bunli/plugin-mcp converts MCP (Model Context Protocol) tool schemas into type-safe Bunli CLI commands. This lets you expose MCP tools as CLI commands that AI agents can discover and invoke.
Features
- Schema to Zod conversion - Automatically converts JSON schemas to Zod validation
- Dynamic registration - Commands registered at runtime from MCP tool definitions
- Namespace support - Organize commands by MCP server namespace
- Type generation - Optional TypeScript type generation for enhanced DX
- Zero-config - Works out of the box with standard MCP tool definitions
Two Usage Patterns
1. As a Plugin (Recommended)
import { createCLI } from "@bunli/core";
import { mcpPlugin } from "@bunli/plugin-mcp";
const cli = await createCLI({
name: "my-cli",
plugins: [
mcpPlugin({
toolsProvider: async (context) => {
// Fetch tools from your MCP client
const tools = await myMcpClient.listTools();
return [{ namespace: "my-server", tools }];
},
createHandler:
(namespace, toolName) =>
async ({ flags }) => {
return myMcpClient.callTool(toolName, flags);
},
sync: true, // Enable type generation
}),
],
});
await cli.run();2. Direct Command Conversion
Use the converter directly without the plugin pattern:
import { createCommandsFromMCPTools } from "@bunli/plugin-mcp";
const tools = await myMcpClient.listTools();
const commands = createCommandsFromMCPTools(tools, {
namespace: "my-server",
createHandler:
(toolName) =>
async ({ flags }) => {
return myMcpClient.callTool(toolName, flags);
},
});
commands.forEach((cmd) => cli.command(cmd));MCP Tool Schema
The plugin converts standard MCP tool input schemas:
interface MCPTool {
name: string;
description?: string;
inputSchema: {
type: "object";
properties?: Record<string, JSONSchema7>;
required?: string[];
};
}Plugin Options
interface McpPluginOptions<TStore = Record<string, unknown>> {
/** Function to fetch tools from MCP client(s) */
toolsProvider: (context: IPluginContext) => Promise<MCPToolGroup[]>;
/** Factory to create handlers for each tool */
createHandler: (namespace: string, toolName: string) => CommandHandler<TStore>;
/** Enable type generation (or set to { outputDir: string }) */
sync?: boolean | { outputDir?: string };
}MCPToolGroup
interface MCPToolGroup {
namespace: string;
tools: MCPTool[];
}Type Generation
Enable automatic TypeScript type generation:
mcpPlugin({
toolsProvider: async () => {
/* ... */
},
createHandler:
(ns, name) =>
async ({ flags }) => {
/* ... */
},
sync: true, // Generates .bunli/mcp-types.gen.ts
});Or with custom output:
mcpPlugin({
// ...
sync: { outputDir: "./types" },
});CLI Codegen (Builder API)
For CLI-based code generation workflows:
import { Commands } from "@bunli/plugin-mcp";
const builder = Commands.from(tools).namespace("exa").timeout(30000);
const code = template
.replace("{{COMMANDS}}", builder.commands())
.replace("{{REGISTRATIONS}}", builder.registrations());Schema Utilities
Convert JSON schemas to Zod schemas:
import { jsonSchemaToZodSchema } from "@bunli/plugin-mcp";
const zodSchema = jsonSchemaToZodSchema({
type: "object",
properties: {
name: { type: "string" },
age: { type: "number" },
},
required: ["name"],
});Naming Conventions
The plugin handles naming automatically:
| MCP Tool | CLI Command |
|---|---|
create_project | create-project |
listItems | list-items |
getUserById | get-user-by-id |
Available utilities:
toKebabCase- Convert to kebab-casetoCamelCase- Convert to camelCasetoPascalCase- Convert to PascalCasetoCommandName- Convert MCP name to CLI command nametoFlagName- Convert to flag name format
Error Handling
import {
SchemaConversionError,
ConvertToolsError,
GenerateMCPTypesError,
McpToolsProviderError,
} from "@bunli/plugin-mcp";
try {
// ...
} catch (error) {
if (error instanceof McpToolsProviderError) {
console.error("Failed to fetch tools:", error.cause);
} else if (error instanceof SchemaConversionError) {
console.error("Invalid schema:", error.message);
}
}Example: Linear MCP Integration
import { createCLI } from "@bunli/core";
import { mcpPlugin } from "@bunli/plugin-mcp";
import { LinearClient } from "@linear/sdk";
const linear = new LinearClient({ apiKey: process.env.LINEAR_API_KEY });
const cli = await createCLI({
name: "linear",
plugins: [
mcpPlugin({
toolsProvider: async () => {
const tools = await fetchLinearTools(); // Your tool fetcher
return [{ namespace: "linear", tools }];
},
createHandler:
(namespace, toolName) =>
async ({ flags }) => {
return linear.call(toolName, flags);
},
}),
],
});
await cli.run();Users can then run:
linear issues list --team=backend --limit=10
linear issues create --title="Bug fix" --team=backendSee Also
- Plugin API - Bunli plugin system
- Type Generation - Bunli codegen
- MCP Specification - MCP protocol docs