Channel doctor
nexo channel is an operator CLI for debugging the MCP-channels
surface without a running daemon. Three verbs:
nexo channel list [--config=<path>] [--json]
nexo channel doctor [--config=<path>] [--binding=<id>] [--json]
nexo channel test <server> [--binding=<id>] [--content=...]
[--config=<path>] [--json]
All three read from the operator's YAML directly. They never spin up the daemon, never connect to a live MCP server, and never publish on the broker. Safe to run on production configs from any operator workstation.
nexo channel list
Walks every agent and surfaces (enabled, approved_servers, bindings) per agent. When --json is passed the output is
machine-readable; otherwise the renderer groups by agent for
human reading.
$ nexo channel list
## agent kate — channels.ENABLED (2 approved)
approved: slack
approved: telegram
binding telegram:kate_tg: 2 server(s) — slack, telegram
When an agent has no channels.approved entries the
(no approved servers) placeholder makes the gap obvious. When
no binding lists allowed_channel_servers, (no binding has allowed_channel_servers) highlights the configuration is
incomplete.
nexo channel doctor
Runs the static half of the 5-step gate against every
(agent, binding, server) triple in the YAML. The doctor
cannot probe a live MCP server, so gate 1 (capability declared)
is assumed true; gates 2/3/5 run normally; gate 4 (plugin
source) reads from the approved entry. Each row carries one of
three outcomes:
WOULD REGISTER— every static gate passes; the only thing the live daemon will check is whether the server actually declares the capability.SKIP { kind, reason }— typed reason.disabled=channels.enabled: false.session= binding doesn't list the server.marketplace=plugin_sourcemismatch.allowlist= server isn't inapproved.NOT BOUND— the server appears inapprovedbut no binding lists it. Surfaces a half-configured state where the operator vetted the server but forgot to bind it.
Filter to one binding with --binding=<plugin>:<instance>. The
binding id format mirrors what the runtime registers — the same
string that shows up in agent logs.
$ nexo channel doctor --binding=telegram:kate_tg
| Agent | Binding | Server | Outcome | Skip | Reason |
|-------|--------------------|----------|----------------|------------|--------|
| kate | telegram:kate_tg | slack | WOULD REGISTER | - | all static gates pass; live runtime must declare the capability |
| kate | telegram:kate_tg | telegram | WOULD REGISTER | - | all static gates pass; live runtime must declare the capability |
nexo channel test
Synthesises a notifications/nexo/channel payload (with sample
chat_id and user meta) and runs it through
parse_channel_notification + wrap_channel_message. Prints
the model-facing <channel> block plus the derived
session_key. Cheap dry-run for tuning meta-key whitelists or
verifying content-cap behaviour.
$ nexo channel test slack
# Channel test — server=slack
session_key: slack|chat_id=C_TEST
--- rendered XML (model-facing) ---
<channel source="slack" chat_id="C_TEST" user="operator">
hello from slack — channel test payload
</channel>
Override the body with --content="..." to test how the
content cap (agents.channels.max_content_chars) clips long
payloads. The output flags [content truncated by max_content_chars] when the cap fired.
When to use which
- Setting up channels for the first time →
listto verify the YAML structure, thendoctorto confirm the gate would let the binding register, then start the daemon. - A server stopped delivering messages →
doctorto see if the gate would still register it. Common causes:channels.enabledflipped off; binding'sallowed_channel_serversdoesn't include the server (typo); approved entry got renamed. - Tuning meta-key whitelists / content caps →
test <server>with various--contentpayloads.
Live-runtime checks
doctor is intentionally static. To check live state — what's
actually registered in the running daemon — the agent calls
channel_list / channel_status from inside a turn, or the
operator inspects the mcp.channel.> NATS subjects directly.
Live-runtime CLI is on the roadmap.
See also
- MCP channels concept — the full picture including threading, permission relay, and the hot-reload re-evaluation pass.