mcp¶
Feature: Mcp, registered by the rtb-mcp crate. Passthrough subtree. No
subcommand means serve over stdio.
mcp rejects --output. Its inner parser does not strip the global flag, so
mytool mcp list --output json fails with unexpected argument '--output'.
mcp list already emits JSON.
mcp serve¶
Runs the Model Context Protocol server in the foreground until the transport closes
or App::shutdown fires.
| Flag | Default | Meaning |
|---|---|---|
--transport <T> |
stdio |
stdio, sse or http |
--bind <ADDR> |
none | Socket address. Required for sse and http, ignored for stdio |
--transport sse or http without --bind fails immediately with
`mcp serve --transport sse` requires `--bind ADDR` — the message names the
transport you asked for, so http gets
`mcp serve --transport http` requires `--bind ADDR`.
Only stdio is implemented¶
| Transport | Status |
|---|---|
stdio |
Shipped. What MCP clients that spawn a server as a subprocess expect |
sse |
Accepted by the parser, then fails with an McpError::Transport |
http |
Accepted by the parser, then fails with an McpError::Transport |
Supplying --bind for those two gets you past argument validation and no further.
Streamable-HTTP wiring is listed as future work in the crate; there is no
configuration that makes it work in this release.
mcp list¶
Prints one JSON object per line — a JSON Lines stream, not a JSON array — for every
command whose Command::mcp_exposed() returns true.
| Key | Source |
|---|---|
name |
CommandSpec::name |
description |
CommandSpec::about |
input_schema |
Command::mcp_input_schema(), or {"type":"object"} when None |
list walks BUILTIN_COMMANDS directly and does not consult the runtime Features
set. A command whose feature is disabled — so it does not appear in --help — is
still listed here if it opts into MCP exposure.
Both defaults are false / None, so a tool that has not opted anything in prints
nothing at all and exits 0.
What a tools/call runs¶
Each exposed command becomes one MCP tool. A tools/call builds a fresh instance
from the command's registry factory and runs Command::run against a clone of the
host App — the same code path the CLI dispatches, in the same process. A tool can
be addressed by its CommandSpec::name or by any of its aliases.
Three constraints worth knowing before exposing a command:
- Arguments are not delivered. The
input_schemais published to clients, and the arguments a client sends are then discarded:call_toolreads the tool name and nothing else.Command::runreceives anAppwhosetrailing_args()is empty. Only commands that need no input do anything useful over MCP today. - The reply is a fixed string, not the command's output. A successful call
returns the text
<name> ok; a failing one returns<name>: <error>withis_errorset. Whatever the command printed went to the process's stdout — which, under the stdio transport, is the protocol stream itself. A command that prints will corrupt the session. - Failures do not stop the server. A command error is reported as that call's error result and the service loop continues.
Failure modes¶
| Variant | Cause |
|---|---|
McpError::Transport(String) |
Bind, accept or I/O failure — including the sse/http stubs |
McpError::Protocol(String) |
No registered tool with the requested name, via McpServer::dispatch |
McpError::Command { command, message } |
Command::run returned an error, via McpServer::dispatch |
McpServer::dispatch is the library-side entry point for the same dispatch logic
and is what surfaces those last two variants. Over the wire, an unknown tool name
becomes an rmcp invalid_params error and a command failure becomes an error
result on the call.