Skip to main content
The Mcp module starts a stdio MCP server that exposes CLI commands as tools, enabling AI agents to discover and call your commands through the Model Context Protocol.

Functions

serve

Starts a stdio MCP server that exposes commands as tools.
async function serve(
  name: string,
  version: string,
  commands: Map<string, any>,
  options?: serve.Options
): Promise<void>
name
string
required
The MCP server name
version
string
required
Server version
commands
Map<string, any>
required
Map of commands to expose as tools
options
serve.Options
Server configuration options
return
Promise<void>
Promise that resolves when the server connects and starts

Example

import { Cli, Mcp } from 'incur'

const cli = Cli.create({
  name: 'mycli',
  version: '1.0.0',
  commands: {
    greet: Cli.command({
      args: z.object({ name: z.string() }),
      run: ({ args }) => `Hello, ${args.name}!`
    })
  }
})

// Start MCP server
if (process.argv.includes('--mcp')) {
  await Mcp.serve(cli.name, cli.version, cli.commands)
} else {
  await cli.run()
}

Types

serve.Options

Options for the MCP server.
type Options = {
  /** Override input stream. Defaults to `process.stdin` */
  input?: Readable | undefined
  /** Override output stream. Defaults to `process.stdout` */
  output?: Writable | undefined
}
input
Readable
Override input stream. Defaults to process.stdin.
output
Writable
Override output stream. Defaults to process.stdout.

How It Works

The MCP server:
  1. Flattens command hierarchy - Nested commands become flat tools with underscore-separated names (e.g., db_migrate)
  2. Merges schemas - Combines args and options into a single inputSchema for each tool
  3. Handles streaming - Supports async generator commands with progress notifications
  4. Error handling - Returns structured errors using context.error() or catches exceptions

Tool Naming

Commands are converted to tool names by joining the command path with underscores:
// Command: mycli db migrate
// Tool name: db_migrate

// Command: mycli server start
// Tool name: server_start

Input Schema

The tool’s inputSchema merges both positional arguments and options into a flat object:
Cli.command({
  args: z.object({ id: z.number() }),
  options: z.object({ verbose: z.boolean().optional() }),
  run: ({ args, options }) => { /* ... */ }
})

// MCP tool receives:
// { id: 42, verbose: true }

Streaming Commands

Async generator commands send progress notifications:
Cli.command({
  run: async function* ({ args }) {
    yield { status: 'starting' }
    await doWork()
    yield { status: 'complete' }
  }
})

// MCP client receives progress notifications
// Final result is an array of all yielded values

Build docs developers (and LLMs) love