Skip to content

cli-mcp features

Inventory of what cli-mcp does today. Scope changes should land in the same commit that touches code, so this file stays a faithful mirror of the public API.

Dual-mode (CLI + MCP, same binary)

  • climcp.AttachMCP(root, opts) - appends an mcp subcommand group to root with two leaves: serve (stdio) and serve-http (Streamable HTTP). After app.Run(ctx, os.Args), the same binary is a normal CLI by default and an MCP server when invoked as <root> mcp serve or <root> mcp serve-http. The mcp subtree is auto-excluded from the projected MCP tool surface. See examples/dual-mode.
  • mcp serve-http flags - --addr (with $ADDR env override), --landing, --no-health. Defaults match the bare RunStreamableHTTP.

Projection

  • climcp.NewServer(*cli.Command, Options) (*mcp.Server, error) - Walks a urfave/cli v3 command tree, registers every leaf command as an MCP tool. Hidden commands skipped. Options.IncludeGroups exposes non-leaves too. Options.SkipPaths hides specific dotted paths.
  • Tool name - Path segments from root's children joined by Options.NameJoiner (default _). The root command's name is dropped to avoid redundancy with Options.Name. Underscore is the safe default because mcporter and similar clients parse server.tool and would split inside a dot-joined tool name. Set NameJoiner: "." for dot-joined output when every consumer handles dots correctly.
  • Input schema - Derived from cmd.Flags: typed properties (boolean / integer / number / string / array) with aliases listed in the description and required flags marked. Positional args expose as an args array of string.
  • Tool execution - Reconstructs argv from MCP input, invokes root.Run(ctx, argv) in-process, captures stdout / stderr via cmd.Writer. Non-zero exit code surfaces via CallToolResult.IsError.

Transports

  • stdio - climcp.ServeStdio(ctx, root, opts). Default for editor/agent integrations (Claude Code, mcp-inspector, mcporter).
  • Streamable HTTP (handler) - climcp.StreamableHTTPHandler(root, opts) (http.Handler, error). Returns the bare handler; mount on any net/http server.
  • Streamable HTTP (batteries-included) - climcp.RunStreamableHTTP(ctx, root, opts, srv) error. Builds the projected server, mounts /mcp plus an optional healthcheck and landing page, blocks until ctx is cancelled with graceful shutdown. Pairs with climcp.AddrFromEnv(fallback) for the container-vs-dev binding convention.
  • Deprecated - HTTP+SSE (2024-11-05) is reachable through the SDK but not surfaced here.

Server helpers

  • climcp.AddrFromEnv(fallback string) string - returns $ADDR if non-empty, else fallback. Lets the same binary bind 127.0.0.1 in local dev and 0.0.0.0:PORT in a container without if/else in main.
  • climcp.WriteJSON(w io.Writer, v any) error - indented JSON encoder for tool Action functions that return structured data. MCP renders the text content verbatim, so indented JSON is what an agent sees.

Extension-namespace annotations

cli-mcp relays known cli.Command.Metadata keys onto MCP Tool wire fields so client extensions (notably cli-web-ops) can consume them:

Metadata key Type Where it goes
webops.favorite bool tool.Meta["webops.favorite"]
webops.label string tool.Meta["webops.label"]
webops.group string tool.Meta["webops.group"]
webops.confirm bool tool.Annotations.DestructiveHint

Composition with cli-guard

When the wrapped command tree routes through cli-guard/verb.Wrap, MCP tool calls inherit argv validation and audit logging for free. No additional wiring.

Examples (examples/)

  • serve/ - Stdio-based demo, single-binary hello tool.
  • serve-http/ - Streamable HTTP demo binding 127.0.0.1:9090/mcp.

Deployment

  • deploy/Caddyfile.example - Tailscale-bound reverse proxy with bearer-token gate. SSE-friendly (flush_interval -1).

Repo development

  • .coily/coily.yaml declares local dev verbs.
  • Makefile is the source of truth.
  • coily lint validates the yaml/Makefile contract on every CI run.
  • .golangci.yaml, staticcheck.conf mirror urfave/cli.
  • GitHub Actions CI runs vet/build/test/lint.