Documentation
¶
Overview ¶
Package climcp projects a urfave/cli v3 command tree as a Model Context Protocol (MCP) server. Every leaf command becomes an MCP tool; the tool name is the underscore-joined command path, the input schema is derived from the command's flags and positional arguments. Set Options.NameJoiner to "." for dot-joined output.
Three entry points:
- ServeStdio runs an MCP server over stdio (the default transport for editor/agent integrations). One goroutine per tool call.
- StreamableHTTPHandler returns an http.Handler that speaks the current MCP remote transport (Streamable HTTP, 2025-03-26 spec). Mount on any net/http server; put Caddy or another reverse proxy in front for TLS, auth, and rate limiting.
- NewServer returns the underlying *mcp.Server for callers who want to wire their own transport.
The deprecated HTTP+SSE transport is available via the SDK but not surfaced here; new deployments should use Streamable HTTP.
Index ¶
- func AddrFromEnv(fallback string) string
- func AttachMCP(root *cli.Command, opts Options)
- func NewServer(root *cli.Command, opts Options) (*mcp.Server, error)
- func RunStreamableHTTP(ctx context.Context, root *cli.Command, opts Options, srv HTTPServerOptions) error
- func ServeStdio(ctx context.Context, root *cli.Command, opts Options) error
- func StreamableHTTPHandler(root *cli.Command, opts Options) (http.Handler, error)
- func WriteJSON(w io.Writer, v any) error
- type HTTPServerOptions
- type Options
- type ToolInput
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddrFromEnv ¶
AddrFromEnv returns os.Getenv("ADDR") if non-empty, otherwise fallback. The convention "container reads $ADDR, local default is safe-by-default" recurs across cli-mcp consumers; this avoids re-implementing it in every main.go. Callers that want a different env var can just read it themselves.
func AttachMCP ¶
AttachMCP appends an "mcp" subcommand group to root with two leaves:
- "mcp serve" - run as MCP server over stdio.
- "mcp serve-http" - run as MCP server over Streamable HTTP.
The added subtree is automatically added to opts.SkipPaths so it is not exposed as MCP tools. The pitch: same binary is a normal CLI by default and an MCP server when invoked as `<root> mcp serve` or `<root> mcp serve-http`. Three usage shapes fall out:
./demo hello world # plain CLI ./demo mcp serve-http --addr :8080 # serve MCP over HTTP mcporter call demo hello world # MCP client, back to CLI shape
Flags on `mcp serve-http`:
- --addr listen address. Default 127.0.0.1:8080. $ADDR env overrides.
- --landing text body for GET /. Empty disables the route.
- --no-health disable the /healthz endpoint.
Callers wanting custom flags or a different subcommand name should skip this and wire ServeStdio / RunStreamableHTTP themselves; AttachMCP is the batteries-included default, not a constraint.
func NewServer ¶
NewServer wires up an *mcp.Server with one tool per leaf command in root. Caller is responsible for running the server on a transport.
Example ¶
Project a urfave/cli command tree as an MCP server. Every leaf command becomes an MCP tool; the tool name is the underscore-joined path. Caller is responsible for running the returned server on a transport.
app := &cli.Command{
Name: "demo",
Commands: []*cli.Command{
{Name: "hello", Usage: "greet someone"},
},
}
srv, err := climcp.NewServer(app, climcp.Options{Name: "demo", Version: "v0.0.0"})
fmt.Println("ok:", srv != nil && err == nil)
Output: ok: true
func RunStreamableHTTP ¶
func RunStreamableHTTP(ctx context.Context, root *cli.Command, opts Options, srv HTTPServerOptions) error
RunStreamableHTTP builds the projected MCP server from root and serves it over Streamable HTTP. Mounts the MCP handler at srv.MCPPath, a healthcheck at srv.HealthPath, and (if srv.Landing is non-empty) a text landing page at "/". Blocks until ctx is cancelled or the listener errors. On ctx cancellation the server is shutdown with a 5-second grace period.
Typical container shape:
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
err := climcp.RunStreamableHTTP(ctx, root, climcp.Options{Name: "myapp", Version: "v1.0.0"},
climcp.HTTPServerOptions{Addr: climcp.AddrFromEnv("0.0.0.0:8080"), Landing: "myapp\n"})
func ServeStdio ¶
ServeStdio is the convenience entry point: build a server from root and run it over stdio until the context is cancelled or the peer disconnects.
func StreamableHTTPHandler ¶
StreamableHTTPHandler returns an http.Handler that exposes the projected command tree over MCP's Streamable HTTP transport (the current remote transport per the 2025-03-26 spec).
Mount on a net/http server and front with a reverse proxy that handles TLS, auth, and rate limiting:
handler, _ := climcp.StreamableHTTPHandler(root, climcp.Options{Name: "myapp", Version: "v1.0.0"})
http.ListenAndServe("127.0.0.1:9090", handler)
See examples/serve-http for a runnable example and deploy/Caddyfile.example for the recommended Caddy-in-front posture.
Example ¶
StreamableHTTPHandler returns an http.Handler for the current MCP remote transport. Mount on any net/http server; front with a reverse proxy in production.
app := &cli.Command{
Name: "demo",
Commands: []*cli.Command{{Name: "hello"}},
}
handler, err := climcp.StreamableHTTPHandler(app, climcp.Options{Name: "demo", Version: "v0.0.0"})
fmt.Println("ok:", handler != nil && err == nil)
Output: ok: true
func WriteJSON ¶
WriteJSON encodes v as indented JSON onto w. Convenience for tool Action functions that return structured data: MCP renders the text content verbatim, so indented JSON is what an agent sees in its tool result.
Errors from json.Encoder.Encode (e.g. unencodable types) propagate unchanged.
Types ¶
type HTTPServerOptions ¶
type HTTPServerOptions struct {
// Addr is the listen address. Default "127.0.0.1:8080".
Addr string
// MCPPath is the route the Streamable HTTP handler is mounted on.
// The handler is mounted at both "<path>" and "<path>/" so clients
// can append a trailing slash either way. Default "/mcp".
MCPPath string
// HealthPath is a GET endpoint that returns 200 + "ok\n". Default
// "/healthz". The zero value gets the default; to opt out, set
// DisableHealth.
HealthPath string
// DisableHealth, if true, suppresses the healthcheck endpoint.
// Otherwise HealthPath (or its default) is mounted.
DisableHealth bool
// Landing, if non-empty, is served as text/plain on "GET /". If
// empty, "/" falls through to net/http's default (404). Useful for
// telling a curious operator what the service is and where the MCP
// endpoint lives.
Landing string
// ReadHeaderTimeout is forwarded to http.Server.ReadHeaderTimeout.
// Default 10s. Important for Slowloris hardening and required by
// the gosec linter in this repo's CI.
ReadHeaderTimeout time.Duration
}
HTTPServerOptions configure RunStreamableHTTP. The zero value is a usable local-dev shape: 127.0.0.1:8080, /mcp, /healthz, no landing page. Production deployments typically override Addr to "0.0.0.0:PORT" (or use AddrFromEnv) and set Landing to a human-readable string so `GET /` returns something other than 404.
type Options ¶
type Options struct {
// Name is the MCP server name (advertised to the client). Required.
Name string
// Version is the MCP server version. Required.
Version string
// SkipPaths, if non-nil, lists command paths that should NOT be
// exposed as MCP tools. Each entry is the dot-joined path from the
// root (excluding the root's own name). Example: []string{"internal.debug"}
// hides the `internal debug` subcommand.
SkipPaths []string
// IncludeGroups, if true, also exposes non-leaf command groups as
// MCP tools that print --help. Default false (leaves only).
IncludeGroups bool
// NameJoiner is the separator used to join command-path segments
// into the wire-visible MCP tool name. Default "_" (e.g. "module_latest").
// Underscore is the safe default for MCP clients whose selector
// grammar parses `server.tool` and would otherwise split inside the
// tool name (mcporter). Set to "." for dot-joined output if you know
// every consumer handles dots correctly.
// Does not affect SkipPaths, which always use dots internally.
NameJoiner string
}
Options configure the MCP server projection.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
annotated-favorites
command
Command annotated-favorites projects a CLI whose commands carry the webops.* metadata namespace, so a cli-web-ops client can render them as labeled, grouped, optionally-confirmed mobile buttons on the home screen.
|
Command annotated-favorites projects a CLI whose commands carry the webops.* metadata namespace, so a cli-web-ops client can render them as labeled, grouped, optionally-confirmed mobile buttons on the home screen. |
|
composition-with-guard
command
Command composition-with-guard shows how cli-mcp composes with cli-guard.
|
Command composition-with-guard shows how cli-mcp composes with cli-guard. |
|
dual-mode
command
Command dual-mode shows cli-mcp's headline shape: a single 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`.
|
Command dual-mode shows cli-mcp's headline shape: a single 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`. |
|
large-tree
command
Command large-tree projects a realistic multi-level CLI as an MCP server over stdio.
|
Command large-tree projects a realistic multi-level CLI as an MCP server over stdio. |
|
serve
command
Command serve runs a tiny CLI as an MCP server over stdio.
|
Command serve runs a tiny CLI as an MCP server over stdio. |
|
serve-http
command
Command serve-http runs a tiny CLI as an MCP server over the Streamable HTTP transport (2025-03-26 spec) using climcp's batteries-included runner.
|
Command serve-http runs a tiny CLI as an MCP server over the Streamable HTTP transport (2025-03-26 spec) using climcp's batteries-included runner. |
|
skip-paths
command
Command skip-paths demonstrates Options.SkipPaths (suppress specific commands from MCP projection) and Options.IncludeGroups (also expose non-leaf groups as MCP tools that print --help).
|
Command skip-paths demonstrates Options.SkipPaths (suppress specific commands from MCP projection) and Options.IncludeGroups (also expose non-leaf groups as MCP tools that print --help). |