urfave/cli v3 extension: project a command tree as an MCP server (stdio + SSE). Intended for the urfave/cli ecosystem.
cli-mcp
cli-mcp turns a urfave/cli v3 command tree into a CLI by default, with a batteries-included Model Context Protocol server attached. One binary, three usage forms, same code path.
app := &cli.Command{Name: "demo", Commands: []*cli.Command{ /* your tree */ }}
climcp.AttachMCP(app, climcp.Options{Name: "demo", Version: "v0.0.0"})
app.Run(ctx, os.Args)
That's it. app is now a regular CLI you can run directly, AND an MCP server when invoked as app mcp serve (stdio) or app mcp serve-http (Streamable HTTP).
The three forms
Given a hello subcommand, the same code is reachable as:
1. Plain CLI — humans run subcommands directly:
./demo hello kai --loud
# HELLO, KAI!
2. MCP server, called via raw JSON-RPC — agents call tools over the Streamable HTTP transport:
./demo mcp serve-http --addr 127.0.0.1:8080 &
curl -sS -X POST http://127.0.0.1:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"hello","arguments":{"args":["kai"],"loud":true}}}'
# ... "text":"HELLO, KAI!\n"
3. MCP server, called via mcporter — the client wraps MCP tools as subcommands, so the caller sees a CLI shape again:
# mcporter.json
# {"mcpServers":{"demo":{"command":"./demo","args":["mcp","serve"]}}}
mcporter call demo hello kai --loud
# HELLO, KAI!
Form 1 runs the Action directly. Forms 2 and 3 round-trip through MCP and end up running the same Action with the same flags and args. The mcp subcommand group is auto-excluded from the MCP tool surface so calling MCP doesn't expose "how to start MCP".
See examples/dual-mode for the runnable version.
Features
- Every leaf command becomes an MCP tool; tool name is the dot-joined path from the root's children.
- Input schema derived from urfave flags (typed) and positional args.
- stdio transport for editor/agent integrations, Streamable HTTP (current spec) for remote.
- Built on the official modelcontextprotocol/go-sdk.
- Relays
webops.*metadata onto MCPtool.Metafor cli-web-ops consumption. - Composes with cli-guard: Guard-wrapped actions audit and validate every tool call.
Documentation
See docs/FEATURES.md for a feature inventory, examples/ for runnable demos, and deploy/Caddyfile.example for the production-shaped Caddy-in-front posture. Local dev verbs live in .coily/coily.yaml; coily lint validates that against the Makefile.
Support
If you found a bug or have a feature request, create a new issue. Participation in this community is governed by the Code of Conduct. Security disclosures go through SECURITY.md.
Sibling repos in the cli-* family: cli-guard, cli-web-docs, cli-web-ops.
See also
Prior art in the CLI-as-MCP-server space:
- njayp/ophis - Closest analog. Bridges Cobra command trees to MCP. Executes tools by spawning the CLI as a subprocess; cli-mcp runs them in-process, which is what lets it compose cleanly with cli-guard and avoids PATH-capture workarounds.
- f/mcptools - Generalized CLI for interacting with MCP servers, with a proxy mode that exposes shell scripts and inline commands as MCP tools. Lower ceiling on schema (no derivation from a real command tree), but useful when you don't have a structured CLI to project.
- FastMCP - Python-side equivalent of the auto-derivation instinct: generates tool definitions from type hints and docstrings instead of from a CLI tree. Different source artifact, same goal of "write the code once, get an MCP server."
License
See LICENSE.