For the complete documentation index, see llms.txt. This page is also available as Markdown.

CLI as MCP

Any command-line tool can become an MCP server. No code, no wrapper — just config.

{
  "mcpServers": {
    "kubectl": {
      "command": "kubectl",
      "cli": true
    }
  }
}

That's it. mcp runs kubectl --help, discovers subcommands and flags, and exposes them as MCP tools automatically.

Why

MCP is becoming the standard protocol for AI tool integration. But most software ships as a CLI, not an MCP server. This bridge closes the gap: any CLI becomes accessible to GPT, Claude, Cursor, or any MCP-compatible client — without writing a single line of integration code.

You / AI agent  -->  mcp CLI  -->  CliTransport  -->  kubectl / docker / terraform / ...
                         |
                    servers.json

How discovery works

When you add a CLI server, mcp automatically:

  1. Runs <command> --help to discover subcommands

  2. Runs <command> <subcommand> --help for each subcommand to discover flags

  3. Generates MCP tool definitions with proper inputSchema

Each subcommand becomes a tool named <command>_<subcommand> (e.g. kubectl_get, kubectl_describe).

Flags are parsed into typed schema properties:

Calling tools

Tools accept a JSON object. Flags map to properties (dashes become underscores). Positional arguments go in args:

The args field supports shell quoting for arguments with spaces:

Each call spawns the CLI process, captures stdout, and returns it as MCP content. No long-running process — each invocation is independent.

If the command writes to stderr on success (e.g. warnings), it's appended to the output under a --- stderr --- delimiter so nothing is lost silently.

Configuration

Minimal

Full options

Field
Type
Default
Description

command

string

required

CLI executable to wrap

cli

bool

required

Must be true — marks this as a CLI server

cli_help

string

"--help"

Flag used to discover subcommands and options

cli_depth

number

2

How deep to recurse into subcommands for flag discovery

cli_only

string[]

[] (all)

Whitelist of subcommands to expose — everything else is hidden

args

string[]

[]

Base arguments prepended to every invocation

env

object

{}

Environment variables for the CLI process

cli_help

Most CLIs use --help. Some don't:

cli_only

Limit exposure to safe, read-only commands:

This is important for security — you probably don't want an AI agent running kubectl delete or kubectl exec.

cli_depth

Controls how deep the discovery goes:

  • 1 — only parse the top-level --help (subcommand names + descriptions, no flag details)

  • 2 (default) — also run <subcommand> --help to discover flags and build inputSchema

Values greater than 2 are accepted but currently behave the same as 2 (no additional recursion depth).

Preset tools

If automatic discovery doesn't work for a specific CLI, you can define tools manually:

When tools is non-empty, automatic discovery is skipped. The args in each tool define the exact arguments passed to the CLI when that tool is called.

Examples

kubectl

docker

terraform

git (read-only)

How it works with proxy mode

CLI servers work with mcp serve just like any other server. Tools are namespaced the same way:

Idle timeout applies: since each CLI call is a separate process spawn, the CLI transport itself has no persistent connection to shut down. The idle timeout controls when the discovered tool cache is dropped.

Environment variables

Variable
Default
Description

MCP_TIMEOUT

60

Timeout in seconds for each CLI command execution

MCP_MAX_OUTPUT

1048576

Max output size in bytes (1 MB). Larger output is truncated

MCP_DISCOVERY_CONCURRENCY

10

Max parallel --help calls during subcommand discovery

Help format support

The discovery parser handles these common formats:

CLI framework
Example
Supported

Cobra (Go)

kubectl, docker, gh

Yes

Clap (Rust)

ripgrep, fd

Yes

Click (Python)

flask, black

Yes

Argparse (Python)

most Python CLIs

Yes

Custom

varies

Best-effort, fallback to single tool

If a CLI's --help output doesn't follow standard patterns, discovery falls back to exposing the command as a single tool with a free-form args parameter.

Last updated

Was this helpful?