Skip to content

The mcp command tree

gitlab.com/phpboyscout/go/mcp/cli builds the mcp command a Cobra application mounts to serve itself. The spellings are ophis's, so scripts and editor entries written for it keep working.

root.AddCommand(mcpcli.Command(
    mcpcli.WithBinding(mcpcobra.WithExposure(isExposed)),
    mcpcli.WithServer(server.WithIdentity("my-tool", version)),
    mcpcli.WithLogLevel(levelVar),
))

The tree is stamped cobra.Omit, so it never publishes itself. A serving command binds the completed root when it runs, which is after every command is registered.

Commands

Command Does
mcp start [--log-level L] Serves on stdin/stdout. Logs go to stderr. Exits when stdin closes.
mcp stream [--host H] [--port P] [--origin O]... [--log-level L] Serves Streamable HTTP on H:P (default all interfaces, 8080) under a controls controller tied to the command context.
mcp tools [--output F] [--log-level L] Writes the full authorised catalogue to F (default mcp-tools.json in the working directory).
mcp claude enable\|disable\|list Manages the entry in Claude Desktop's claude_desktop_config.json (mcpServers).
mcp cursor enable\|disable\|list [--workspace] Manages ~/.cursor/mcp.json, or .cursor/mcp.json with --workspace (mcpServers, typed stdio).
mcp vscode enable\|disable\|list [--workspace] Manages VS Code's user mcp.json, or .vscode/mcp.json with --workspace (servers, typed stdio).

On Linux, XDG_CONFIG_HOME overrides the configuration directory these paths start from; macOS and Windows ignore it and use their own conventions.

Every helper takes --config-path to name the file and --server-name to name the entry (default: the root command's name, or WithServerName). enable also takes --log-level, appended to the entry's arguments, and --env KEY=value (repeatable).

What enable writes

{
  "servers": {
    "my-tool": {
      "type": "stdio",
      "command": "/absolute/path/to/my-tool",
      "args": ["mcp", "start", "--log-level", "debug"],
      "env": {"A": "1"}
    }
  }
}

The executable is this binary's absolute path and the arguments are the mcp command's real path in the tree followed by start. Every other key in the file is preserved as JSON (the file is re-indented); running enable again replaces this entry only; disable removes it and is not an error when it is absent. A file that is not a JSON object, or whose servers key is not an object, is refused untouched rather than repaired. Writes are atomic.

The export

mcp tools reads the binding's operations directly and writes every one of them: the set an AllowRegistered registry publishes, which is the policy the command tree uses. It does not run the registry's search or inspection, and it does not consult any other policy, so a tool that mounts the binding under a stricter policy of its own exports more than that policy would show a client. Each entry carries name, title, description, inputSchema, outputSchema and annotations in the shape ophis's export used. It is an offline export: the compact wire mode still publishes the three discovery tools.

stream

The HTTP host is the transport module's server registered with a controls controller, and the MCP server and binding are registered beside it with a StopErr and a Status, so one shutdown budget covers the protocol drain and the subprocess cleanup and unresolved cleanup stays visible. When either half fails to release within the budget, both errors are reported together. Interrupt or terminate the process (the CLI owns its signals; the controller installs none) and it stops within the budget. The write timeout is disabled because SSE responses stay open for the life of a call.

MCP's Origin rule is applied by the server's cross-origin protection: a request with no Origin header (a non-browser client) passes; a browser origin passes only when it is the server's own or listed with --origin (or WithTrustedOrigins); anything else is refused with 403 before the protocol sees it.

Options

Option Purpose
WithBinding(opts...) Options for the Cobra binding: exposure, flag filter, codecs, limits, group.
WithServer(opts...) Options for the protocol server: mode, identity, resources, Apps.
WithLogLevel(*slog.LevelVar) The variable --log-level sets, so a host's own logging follows.
WithLogger(*slog.Logger) Replaces the default stderr text logger. Never stdout on the stdio transport.
WithShutdownBudget(d) Drain and cleanup budget on exit (default 10s).
WithServerName(name) The entry name the editor helpers write.
WithTrustedOrigins(origins...) Browser origins stream trusts in addition to --origin.