NAME
dual-mode - demo CLI that is also an MCP server
SYNOPSIS
dual-mode
DESCRIPTION
dual-mode is cli-mcp's headline shape: one binary that is a normal
CLI by default and an MCP server when invoked as dual-mode mcp serve
or dual-mode mcp serve-http. The same urfave tree powers all three
usage forms:
1. Plain CLI - humans run subcommands directly:
./dual-mode hello world
# hello, world
2. MCP server (stdio) - agents spawn the binary and exchange
JSON-RPC on its pipes:
./dual-mode mcp serve
3. MCP server (Streamable HTTP) - agents POST JSON-RPC:
./dual-mode mcp serve-http --addr 127.0.0.1:8080
The mcp subcommand group is auto-excluded from the MCP tool
surface. Calling the MCP server does not expose tools named
mcp_serve and mcp_serve_http; that would let a tool call recurse
into starting another server. mcp only appears in the CLI surface
and in this rendered help.
Operating model for an agent integrating with a dual-mode binary:
- Prefer the stdio transport for editor-spawned integrations.
Lower overhead, no port management, no auth surface.
- Use HTTP when the binary runs as a persistent service that
multiple agents reach concurrently or across hosts.
- The exit code on Action error propagates through both
transports. An agent calling the MCP tool sees isError=true
and the error string; a human running the CLI sees the
stderr message and a nonzero exit.
- The single binary means CI runs `dual-mode hello world` to
smoke-test the same code path the agent will hit at runtime.
One thing to test, two ways to invoke it.
Usage:
dual-mode [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...]
COMMANDS
hello
print a greeting
--lang="": language: en or es (default: "en")
--loud: shout the greeting
mcp
MCP server transports
serve
run as MCP server over stdio
serve-http
run as MCP server over Streamable HTTP
--addr="": listen address ($ADDR env overrides) (default: "127.0.0.1:8080")
--landing="": text body for GET /
--no-health: disable the /healthz endpoint