smolmux
This server gives read-only visibility into a serial device attached to a smolmux broker — watching output, waiting for patterns, checking link/boot state, and surfacing anomalies.
Read buffered output (
serial_read) — drain what this session buffered since the last read (lossy, best for quick looks after your own command).Read past output losslessly (
serial_output_history) — page the broker's shared history with asince_seqcursor (checkingdropped/has_more), or grab prose bysecondsorlast_bytes.Wait for known text (
serial_wait_for) — block on a regex in device output (prompt,login:, log line) with an optional timeout; aborts early on a critical anomaly; works for observers.Open-ended listening (
serial_monitor) — listen for a fixed window (default 30 s, max 300 s) and return what was printed plus any anomalies.Check link health (
serial_port_status) — port, baud, connected/suspended, pin states, by-id identity strength, who holds write control, and connected clients.Track boot progress (
serial_boot_status) — which boot stages were reached, furthest stage, and whether boot stalled (requires a profile declaringboot_stages).Inspect crashes/errors (
serial_get_incidents) — detected anomalies (kernel panic, oops, hard fault, assert, watchdog, brownout, resets) with match text and pre-context, optionally filtered by recency.List host serial ports (
serial_list_ports) — paths, by-id/by-path, USB VID/PID, manufacturer/product, and identity strength; works without a broker.Get a snapshot (
serial_generate_report) — Markdown summary of device profile, port status, and incidents for opening a debug session or handing to a human.
Note: everything here is read-only and non-destructive — no tool sends data, controls pins, or changes device state.
smolmux
A portable C11 device multiplexer. Holds a device connection open (serial UART, GDB stub) and multiplexes access to multiple clients over Unix sockets using newline-delimited JSON.
Single static binary, low latency, small footprint - built for daily serial and GDB bring-up on Linux.
New here? docs/START-HERE.md is a one-screen router - find your intent (run it, bring up a new board with an AI agent, understand the architecture, hack on the code) and it points you to the right doc.
One port, three clients

One broker holds an ESP32-S3's UART. The monitor (top) watches it live. In the
bottom pane a script sends temp and gets the reply on the same wire, resets
the board and waits for its prompt, then esptool reads the flash ID through
with-port: the broker suspends, esptool runs, the broker resumes, and the
monitor never loses the port.
Related MCP server: Board Terminal Skill
Example: probe an unknown board
One broker holds SWD; smolmux-gdb-mcp runs probe_unknown_board. On a
SAM C21 Xplained Pro that path decoded Cortex-M0+ from CPUID, named the
part via SAM DSU DID, rejected a false STM32 match, and wrote a starter
*.gdb-profile.json. Tools, register values, and profile shape:
docs/demo-samc21-probe-transcript.md.
Day-to-day serial (multi-client, U-Boot break-in, flasher handoff): docs/daily-driver.md.
Features
Serial UART, GDB MI, and serial-over-TCP (telnet + RFC2217) device links via vtable polymorphism
Multiple concurrent clients over Unix sockets with role-based access (observer/controller/takeover)
Expect engine - concurrent regex matching on the device byte stream with timeouts
Anomaly detection - pattern-based crash/error detection with cooldown and incident tracking
Output history - timestamped ring buffer for replay by late-joining clients
Structured logging - JSONL I/O log + human-readable text log with rotation (the I/O log records everything sent to and from the device, including anything typed at a login prompt — it is created
0600under your private state directory, and smolmux refuses to write it through a symlink or to a file owned by another user)Network sinks - TCP and WebSocket for remote access (loopback by default). The wire protocol is cleartext, and any client that completes the handshake gets full control of the device — console writes, pins, BREAK, SysRq, GDB. On loopback without
--auth-token, the broker generates a token into a0600file in$XDG_RUNTIME_DIR(or/tmp), so other local users and processes cannot connect; your ownsmolmux-monitor/smolmux-mcpread it automatically, andsmolmux-cli tokenprints it. For remote use, keep the bind on loopback and reach it over an SSH tunnel or WireGuard rather than exposing the port; smolmux refuses to serve a non-loopback TCP bind with no--auth-token.--insecure-no-authturns both protections off.MCP servers - standalone
smolmux-mcp/smolmux-gdb-mcpattach to a running broker; optional in-process--mcpsink for single-process stdioBoot tracking & autoresponder - ordered boot stages, stall events, standing expect->send rules
Autoboot interrupt - broker-side key flood (and optional DTR/RTS reset) for
bootdelay=0U-BootDevice profiles / board manifests - JSON configs for prompts, anomalies, multi-wire boards
Auto-reconnect - exponential backoff recovery on USB-serial disconnect
Build-time feature selection - Kconfig-based; UART-only builds carry no GDB/TCP/WebSocket code
Quick start
Prerequisites: Linux, a C11 compiler, CMake 3.21+, Python 3 with
kconfiglib (the configure step
generates the feature header with it). libpcre2-8 is optional; without it
regex matching falls back to POSIX.
# Debian/Ubuntu
sudo apt install build-essential cmake python3 python3-pip libpcre2-dev
pip install kconfiglib # or in a venv; Debian's system Python may need
# --break-system-packagescmake -B build && cmake --build build -j$(nproc)
./build/smolmux /dev/ttyUSB0Connect a client:
# Needs a running broker (above). Auto-discovers the socket from the port name.
./build/smolmux-monitor /dev/ttyUSB0Day-to-day workflows (profiles, logs, U-Boot break-in, multi-wire boards): docs/daily-driver.md. Intent router: docs/START-HERE.md.
Build
cmake -B build && cmake --build build -j$(nproc) # Default (all features)
ctest --test-dir build # Run testsFeature profiles
cp configs/defconfig.minimal .config && cmake -B build # UART only, no sinks/watcher
cp configs/defconfig.uart .config && cmake -B build # UART + MCP sink + watcher
cp configs/defconfig.embedded .config && cmake -B build # Embedded target profile
cp configs/defconfig.full .config && cmake -B build # Everything (GDB, TCP, WS, MCP, watcher)Or toggle features directly:
cmake -B build -DSM_ENABLE_GDB=OFF -DSM_ENABLE_SINK_WS=OFFDeveloper benchmarks (e.g. the output coalescer harness) are off by default:
cmake -B build -DSM_BUILD_BENCH=ON && cmake --build build --target bench_coalesceInteractive configuration:
cmake --build build --target menuconfigStatic builds
cmake -B build -DSM_MUSL_STATIC=ON # Fully static musl binary (zero runtime deps)
cmake -B build -DSM_STATIC=ON # Static linking (except glibc)Usage
smolmux <port> [options]
Options:
-b, --baud <rate> Baud rate (default: 115200)
-s, --socket <path> Unix socket path
-l, --log-dir <dir> I/O log directory
(default: $XDG_STATE_HOME/smolmux or
~/.local/state/smolmux)
-t, --text-log-dir <dir> Text log directory
-p, --profile <path> Device profile JSON file
--board <name> Group this wire under a board (for discovery)
--role <label> This wire's role on the board (console, swd, ...)
--gdb Use GDB MI link instead of UART
--gdb-path <path> Path to gdb binary (default: gdb)
--gdb-target <spec> GDB target (e.g., localhost:3333)
--serial-tcp <host:port> Connect to a serial-over-TCP device server
(ser2net, socat, terminal server; telnet +
RFC2217 baud/DTR/RTS/break control)
--mcp Enable in-process MCP stdio sink (prefer standalone
smolmux-mcp against a daemon broker for daily use)
--tcp-port <port> Enable TCP sink (default: 5555)
--tcp-bind <addr> TCP bind address (default: 127.0.0.1)
--auth-token <token> Require token in hello from TCP clients
(prefer env SMOLMUX_AUTH_TOKEN - hidden from ps)
--auth-token-file <path> Read the token from a file
--insecure-no-auth Serve TCP/WS with no token. Without it, a
loopback listener gets a generated token
(0600 file; smolmux-cli token prints it) and
a non-loopback --tcp-bind is refused.
--ws-port <port> Enable WebSocket sink (default: 5556)
--no-text-log Disable text log
--no-io-log Disable JSONL I/O log
--no-reconnect Don't auto-reconnect on disconnect
--wait-device <seconds> Wait for the device path before open
--gdb-allow-shell Permit GDB shell/python/eval (off by default)
--list-ports List available serial ports and exit
--list-profiles List available device profiles and exit
--help-protocol Show wire protocol documentation
-v, --verbose Enable debug logging
-V, --version Show version
-h, --help Show this helpWire protocol
Newline-delimited JSON over Unix sockets (also TCP/WS sinks). Binary data is base64-encoded. Message set covers session control, expect, history, anomaly, boot stages, autoboot flood, and autoresponder.
Full reference: ./build/smolmux --help-protocol (always matches this binary).
Client -> Broker: hello, send, send_expect, takeover, release, status, pin_control, set_baud, suspend, resume, history_request, incidents_request, configure_anomaly, interrupt_autoboot, configure_autoresponder, autoresponders_request
Broker -> Client: welcome, output, input_echo, expect_result, status_response, error, history_response, incidents_response, anomaly, autoboot_result, boot_stage, boot_stall, autoresponders_response, autoresponder_fired, suspended, resumed, link_down, link_up
Example session:
-> {"type":"hello","name":"my-tool","role":"controller","protocol_version":1}
<- {"type":"welcome","broker_version":"x.y.z","protocol_version":1,"port":"/dev/ttyUSB0","baud":115200,"your_role":"controller"}
-> {"type":"send","id":"1","data":"dW5hbWUgLWEK"}
<- {"type":"output","data":"TGludXggNC4xOS4w...","timestamp":1709654321.123}Architecture
┌───────────┐ ┌───────────┐
│ uart link │ │ gdb link │ <- device-facing, compiled in/out via Kconfig
└─────┬─────┘ └─────┬─────┘
│ │
▼ ▼
┌──────────────────────────────┐
│ smolmux core │ <- epoll event loop, message bus,
│ history · logging · anomaly │ role enforcement, expect engine
└──┬───────┬───────┬───────┬───┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────┐┌─────┐┌─────┐┌──────┐
│ unix ││ tcp ││ ws ││ mcp │ <- sinks, also compiled in/out
│socket││sink ││sink ││ sink │
└──────┘└─────┘└─────┘└──────┘See DESIGN.md for full architecture documentation.
Dependencies
Required: cJSON (vendored, single file)
Auto-detected: PCRE2 (libpcre2-8). Used as the regex engine when
present, because it bounds backtracking internally; the build falls back to
POSIX ERE automatically when it is absent, so it is never required. Force
either way with -DSM_ENABLE_PCRE2=ON (error if missing) or
-DSM_ENABLE_PCRE2=OFF.
Core has zero required external dependencies beyond POSIX + cJSON.
Companion tools
smolmux-cli - command-line client (send commands, read output;
with-port <cmd>suspends the port, runs an external tool like a flasher, then always resumes)smolmux-monitor - interactive terminal client with escape sequences (prefix key then a command; prefix defaults to Ctrl-], change with
-e, e.g.-e escor-e ^A)smolmux-mcp - standalone MCP server: connects to a running broker and exposes serial tools (
serial_send_command,serial_read,serial_boot_status,serial_add_autoresponder, ...). Alternative: broker--mcpsink embeds MCP in-process.smolmux-gdb-mcp - standalone MCP server for GDB debugging: 21 tools over a broker holding a
--gdblink - breakpoints, stepping, backtrace, name-labeled registers, memory, expression eval, fault-register decode (where the core has them), peripheral reads,gdb_interrupt, and unknown-board probing on ARM Cortex-M today (gdb_identify_target/gdb_generate_profile; other architectures are planned). Resources and prompts includesmolmux-gdb://board-probingandprobe_unknown_board. Built whenSM_ENABLE_GDBis on; chip-ID validated on a SAM C21 Xplained Pro.smolmux-watcher - daemon that monitors for anomalies and saves incident reports to disk
Related Documentation
Start here - One-screen intent router (daily use, new board, architecture, hacking).
MCP setup - Register
smolmux-mcp/smolmux-gdb-mcpwith Claude Code, Claude Desktop, or Cursor.Daily driver - Recommended build, runtime flags, U-Boot break-in, boot stages, boards, coexistence with flashers.
Board Exploration Workflow - Runbook for a fresh board (manually or with an AI agent): wires, SWD identify, console, peripherals.
Board Bring-up Template - Copy-per-board fact-capture template.
Persistent Serial Device Names - Stable names for UART dongles (
/dev/serial/rpi-consoleetc.).Hardware validation matrix - What is validated on real hardware vs not yet proven.
Free vs Pro
Everything in this repository is MIT-licensed - full source, wire protocol, generic device profiles, build system. Build it yourself and you have the complete product.
smolmux Pro is convenience, not a feature gate: prebuilt static binaries
(x86_64 + aarch64, musl, zero runtime dependencies), the curated profile pack
with per-profile notes, and 6 months of email support. One-time purchase
($79). MCP setup for zip paths is docs/MCP-SETUP-FULL.md (also in
this public tree).
Buy smolmux Pro - $79 one-time - download includes current static binaries and the profile pack.
License
MIT. cJSON is vendored under its own MIT license
(deps/cJSON/LICENSE).
Available Tools
9 toolsserial_boot_statusARead-only
Report cold-boot progress: which boot stages the device has reached, the furthest stage, and whether the boot has stalled. Requires the device profile to declare boot_stages; otherwise reports none. Use after a reset or power cycle; if stalled, check serial_get_incidents.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| state | Yes | |
| total | Yes | |
| stages | Yes | |
| reached | Yes | |
| furthest | Yes | |
| configured | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish this as a safe read, so the description's job is to add context, which it does: it discloses the precondition that the device profile must declare boot_stages and that it reports none otherwise, plus the stall-detection behavior. That is meaningful disclosure beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: the return content is front-loaded, followed by the precondition and the usage/routing guidance. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be restated, and the description still covers the precondition, the trigger condition, and the fallback sibling. Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4; there is nothing for the description to clarify, and it correctly avoids inventing parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Report cold-boot progress') and enumerates exactly what it returns: stages reached, furthest stage, and stall status. This is clearly distinguishable from siblings like serial_read or serial_port_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('Use after a reset or power cycle') and routes the agent to serial_get_incidents when the boot is stalled. It stops short of stating when not to use it, but the context and alternative are concrete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_generate_reportARead-only
Markdown summary of device profile, port status and incidents. Use to open a debugging session or hand a human a snapshot; for one fact, call the single-purpose tool.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds value beyond them by disclosing the return format (Markdown) and the aggregated content, which matters since there is no output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with no filler. The deliverable is front-loaded and the routing rule follows, so an agent can act after one read.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With zero parameters and no output schema, the description carries the return-value burden and largely meets it by naming the sections in the summary. It stops short of stating size, format details, or how it relates to serial_monitor, but nothing needed to call it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4 and there is no parameter syntax to explain. The description correctly implies a no-argument, whole-snapshot call rather than leaving the agent hunting for a target selector.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific output artifact (a Markdown summary) and enumerates its contents: device profile, port status, and incidents. That differentiates it from the single-fact siblings in spirit, though it refers to them only generically as 'the single-purpose tool' rather than naming any of the eight.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('open a debugging session or hand a human a snapshot') and a when-not-to-use rule ('for one fact, call the single-purpose tool'). The exclusion is directional but unnamed, so the agent must infer which sibling covers a given fact.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_get_incidentsARead-only
List anomalies the broker detected in output (kernel panic, oops, hard fault, assert, watchdog, brownout, ESP resets, plus profile patterns) with the match and pre-context. Use when a boot stalls, a wait aborts, or output looks like a crash; pair with serial_output_history for the surrounding log.
| Name | Required | Description | Default |
|---|---|---|---|
| seconds | No | If > 0, only return incidents from the last N seconds. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| incidents | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so safety is covered. The description adds meaningful behavior beyond that: it specifies the detector's classification taxonomy and that each result carries the match plus pre-context, which tells the agent what a returned incident contains. It does not disclose limits on the detection window or buffering, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler: the first front-loads what is detected and what each result includes, the second front-loads when to use it and what to pair it with. Every clause earns its place by aiding selection or interpretation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return formatting need not be explained, and the description still covers detection scope, trigger conditions, and sibling pairing. For a single-optional-parameter read tool, nothing an agent needs to select or invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and there is only one optional parameter ('seconds'), which the schema already documents fully. The description adds no extra syntax, default, or format detail about that parameter, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List anomalies the broker detected in output') and enumerates exactly what counts as an anomaly (kernel panic, oops, hard fault, assert, watchdog, brownout, ESP resets, profile patterns). It also distinguishes itself from the sibling serial_output_history by framing that tool as the source of surrounding log context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete triggering conditions ('when a boot stalls, a wait aborts, or output looks like a crash') and explicitly routes the agent to pair with serial_output_history for surrounding logs. The when-to-use condition and the relationship to the alternative are both stated directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_list_portsARead-only
List serial ports on this host with by-id paths and USB VID/PID (works without a broker). Use to pick a port before starting a broker, or when output is garbage (wrong port). Bridge chips name the adapter, not the MCU.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| ports | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, openWorldHint=false, so safety is covered. The description still adds real behavioral context beyond them: no broker dependency required, and the caveat that bridge chips report the adapter rather than the MCU, which materially affects how an agent interprets the returned names.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with no filler: purpose and return content first, then usage triggers, then the interpretive caveat. Every clause earns its place and nothing is buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and there are no parameters to document. Purpose, invocation timing, and the interpretation caveat are all present, leaving no material gap for an agent deciding to call it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so there is nothing to disambiguate; the description instead spends its words on the returned fields (by-id path, VID/PID), which is the right allocation. Baseline of 4 applies for a zero-parameter tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ('List serial ports on this host') plus the exact payload returned (by-id paths, USB VID/PID), which cleanly separates it from siblings like serial_port_status or serial_read. The parenthetical '(works without a broker)' further scopes it as a standalone discovery tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two concrete triggering conditions: before starting a broker, and when output is garbage due to a wrong port. No sibling tool is named as an alternative, and there is no explicit 'when not to use', so it falls short of the 5-level routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_monitorARead-only
Listen for a fixed time (default 30 s, max 300 s) and return what the device printed plus anomalies in that window. Use when you do not know what to expect; when you do, serial_wait_for returns as soon as it matches.
| Name | Required | Description | Default |
|---|---|---|---|
| duration_seconds | No | How long to monitor (max 300 seconds, default 30). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations already declaring readOnlyHint, openWorldHint=false, and destructiveHint=false, the description adds valuable behavioral context: the default and maximum listening duration, and that it returns both raw output and anomalies. It doesn't cover potential blocking behavior or output format details, but the added context is meaningful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly written sentences with no waste. The core purpose and duration constraints are front-loaded, followed immediately by the usage rule and alternative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (one optional parameter, no output schema), the description provides everything an agent needs: what it does, when to use it, how long it runs, and what it returns. Annotations already cover safety, so no critical gaps remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the single parameter is fully documented in the schema. The description repeats the default (30 s) and max (300 s) without adding new syntax or format details beyond what the schema provides, which is the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (listen), resource (device output and anomalies), and scope (fixed time window), clearly distinguishing it from serial_wait_for. It also implicitly differentiates from read-once siblings like serial_read by emphasizing monitoring over a duration.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use this tool ('when you do not know what to expect') and names the alternative serial_wait_for for when you do know what to expect. This is a textbook when/when-not/alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_output_historyARead-only
Read past output from the broker's shared history without consuming it. For anything you must not miss, page with since_seq (start at 0; pass the returned cursor back; check dropped and has_more). Without since_seq: text by seconds or last_bytes. serial_read is drain-only.
| Name | Required | Description | Default |
|---|---|---|---|
| seconds | No | If > 0, return output from the last N seconds (prose). | |
| max_bytes | No | With since_seq: max raw bytes in this page (default broker cap). | |
| since_seq | No | Cursor from a prior history response: lossless page as JSON (cursor/dropped/has_more/chunks). Prefer this over serial_read. | |
| last_bytes | No | If > 0, return the last N bytes of output (prose). |
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | Yes | |
| text | No | |
| chunks | No | |
| cursor | No | |
| dropped | No | |
| has_more | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=true, destructiveHint=false, openWorldHint=false), but the description adds the non-obvious behavioral fact that reads do NOT consume the buffer, and exposes paging state (dropped, has_more). It omits error conditions and cursor expiry, so not a full 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences with no filler; the core non-consuming read behavior leads, followed by the paging recipe and the fallback, then the sibling contrast. Every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values needn't be explained, yet the description still surfaces the meaningful response fields (cursor, dropped, has_more). For a 4-param, all-optional read tool this is complete enough to invoke correctly on the first try.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real semantics the schema does not: since_seq must start at 0, the returned cursor must be passed back, and dropped/has_more must be checked. The seconds/last_bytes fallback condition is also stated in prose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read past output from the broker's shared history') plus the differentiating trait 'without consuming it'. It explicitly contrasts itself with serial_read, which is described as drain-only, so an agent can pick between the two without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use routing: use since_seq paging 'for anything you must not miss' with start-at-0 and cursor-passback instructions, and fall back to seconds/last_bytes when since_seq is absent. It also names the alternative (serial_read) and its exclusive behavior (drain-only).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_port_statusARead-only
Check the link before sending, or when output looks wrong: port, baud, connected/suspended, by-id identity strength, pins, who holds write control, and connected clients. Boot progress: serial_boot_status. Crashes: serial_get_incidents.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| baud | Yes | |
| port | Yes | |
| role | No | |
| board | No | |
| clients | Yes | |
| log_path | No | |
| connected | Yes | |
| link_type | No | |
| suspended | Yes | |
| link_up_ts | No | |
| pin_states | No | |
| identity_by_id | No | |
| last_rx_age_ms | No | |
| last_link_event | No | |
| takeover_client | Yes | |
| identity_by_path | No | |
| identity_strength | No | |
| bytes_rx_since_link_up | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds real value beyond that by disclosing the actual inspection surface, including contested state like write-control ownership and identity strength, which tells an agent what diagnostic questions this call can answer. It does not mention latency or caching, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the imperative use case, then compresses the reported fields into one scannable list, then closes with two sibling redirects. Every clause carries selection or invocation value; nothing is restated from the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and annotations carry the safety profile. What remains for the description, namely when to call this versus the boot/incident siblings, is fully covered, so an agent has everything needed to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The listed output fields could be mistaken for inputs, but the schema is empty and the description's framing ('check') makes the direction of data clear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('Check the link') and enumerates exactly what the tool reports: port, baud, connected/suspended, by-id identity, pins, write-control ownership, and connected clients. That enumeration makes it unmistakably distinct from siblings like serial_read and serial_monitor without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two explicit trigger conditions ('before sending' and 'when output looks wrong') and routes two adjacent needs to named alternatives ('Boot progress: serial_boot_status', 'Crashes: serial_get_incidents'). Nothing about when-to-use or when-to-defer is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_readARead-only
Drain the output this MCP session buffered since the last read, without sending. Quick look after your own command; lossy across turns. Lossless capture: serial_output_history with since_seq. Waiting for known text: serial_wait_for.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnly, non-destructive, closed-world), and the description adds genuinely non-obvious behavior beyond them: it consumes/drains the buffer, is lossy across turns, and does not transmit on the port. It omits return formatting and empty-buffer behavior, which is why it is not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight clauses, zero filler, and the core action plus its main caveat ('lossy across turns') are front-loaded before the alternative-tool pointers.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-param, fully annotated read tool with no output schema, the description covers purpose, consumption semantics, and alternatives. Only minor operational details (empty-buffer result, encoding/format of drained output) are absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline of 4 applies and there is no schema coverage gap to compensate for. The description adds no parameter detail, but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('drain the output this MCP session buffered since the last read') plus the critical scope qualifier 'without sending'. It immediately differentiates itself from siblings by naming what it is not (lossless history, text-waiting).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('Quick look after your own command'), an explicit limitation ('lossy across turns'), and routes to the two correct alternatives by name with the parameter that selects them ('serial_output_history with since_seq', 'serial_wait_for'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
serial_wait_forARead-only
Wait for a regex in serial output without sending. Use when you know the text to expect (a prompt, login:, a log line). Works for observers. Ends early on a critical anomaly ([ABORTED anomaly:...]). Open-ended listening: serial_monitor. Output that already arrived: serial_output_history.
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | Yes | Regex to match in device output (listen-only; no TX). | |
| timeout_ms | No | Max wait in ms (default 30000, clamp 100–3600000). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, openWorldHint=false, so the safety profile is covered. The description adds real behavioral context beyond that: it transmits nothing ('no TX', 'Works for observers') and terminates early on a critical anomaly ('[ABORTED anomaly:...]'). It does not describe the return value on a normal timeout, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four compact sentences, front-loaded with the core action, then usage, then behavior, then alternatives. No filler or restatement of the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description carries the return-shape burden; it partially covers this by noting early termination on anomalies, and the timeout semantics live in the schema. The only gap is what a successful match returns to the caller.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters are fully documented in the schema, including the default and clamp range for timeout_ms. The description adds no syntax or format detail beyond what the schema provides, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('wait for') and resource ('a regex in serial output') plus a distinguishing constraint ('without sending'), which separates it from siblings like serial_read and serial_monitor. The parenthetical examples (prompt, login:, log line) make the trigger condition concrete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('when you know the text to expect') and routes two named alternatives: 'Open-ended listening: serial_monitor' and 'Output that already arrived: serial_output_history.' Both siblings exist in the tool list, so the routing is actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
- Changed
serial_boot_status1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "configured": { + "type": "boolean" + }, + "furthest": { + "type": [ + "string", + "null" + ] + }, + "reached": { + "type": "integer" + }, + "stages": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "reached": { + "type": "boolean" + }, + "timestamp": { + "type": "number" + } + }, + "required": [ + "name", + "reached" + ], + "type": "object" + }, + "type": "array" + }, + "state": { + "enum": [ + "unconfigured", + "not started", + "in progress", + "stalled", + "complete" + ], + "type": "string" + }, + "total": { + "type": "integer" + } + }, + "required": [ + "configured", + "state", + "reached", + "total", + "furthest", + "stages" + ], + "type": "object" +}
- Changed
serial_get_incidents1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "count": { + "type": "integer" + }, + "incidents": { + "items": { + "properties": { + "incident_id": { + "type": "string" + }, + "match_text": { + "type": "string" + }, + "pattern_name": { + "type": "string" + }, + "pre_context": { + "type": "string" + }, + "severity": { + "type": "string" + }, + "timestamp": { + "type": "number" + } + }, + "required": [ + "pattern_name", + "severity", + "timestamp", + "match_text", + "pre_context" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "count", + "incidents" + ], + "type": "object" +}
- Changed
serial_list_ports1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "ports": { + "items": { + "properties": { + "by_id": { + "type": "string" + }, + "by_path": { + "type": "string" + }, + "identity": { + "enum": [ + "STRONG", + "WEAK", + "n/a" + ], + "type": "string" + }, + "manufacturer": { + "type": "string" + }, + "path": { + "type": "string" + }, + "pid": { + "type": "string" + }, + "product": { + "type": "string" + }, + "vid": { + "type": "string" + } + }, + "required": [ + "path", + "vid", + "pid", + "identity" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "ports" + ], + "type": "object" +}
- Changed
serial_output_history1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "chunks": { + "items": { + "properties": { + "seq_start": { + "type": "integer" + }, + "text": { + "type": "string" + }, + "timestamp": { + "type": "number" + } + }, + "required": [ + "text", + "timestamp" + ], + "type": "object" + }, + "type": "array" + }, + "cursor": { + "type": "integer" + }, + "dropped": { + "type": "integer" + }, + "has_more": { + "type": "boolean" + }, + "mode": { + "enum": [ + "cursor", + "text" + ], + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" +}
- Changed
serial_port_status1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "baud": { + "type": "integer" + }, + "board": { + "type": "string" + }, + "bytes_rx_since_link_up": { + "type": "number" + }, + "clients": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "pid": { + "type": "integer" + }, + "role": { + "type": "string" + } + }, + "required": [ + "name", + "role" + ], + "type": "object" + }, + "type": "array" + }, + "connected": { + "type": "boolean" + }, + "identity_by_id": { + "type": "string" + }, + "identity_by_path": { + "type": "string" + }, + "identity_strength": { + "type": "string" + }, + "last_link_event": { + "type": "string" + }, + "last_rx_age_ms": { + "type": "number" + }, + "link_type": { + "type": "string" + }, + "link_up_ts": { + "type": "number" + }, + "log_path": { + "type": "string" + }, + "pin_states": { + "type": "object" + }, + "port": { + "type": "string" + }, + "role": { + "type": "string" + }, + "suspended": { + "type": "boolean" + }, + "takeover_client": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "port", + "baud", + "connected", + "suspended", + "takeover_client", + "clients" + ], + "type": "object" +}
9 tool updates
v0.4.0- First observed
serial_boot_status - First observed
serial_generate_report - First observed
serial_get_incidents - First observed
serial_list_ports - First observed
serial_monitor - First observed
serial_output_history - First observed
serial_port_status - First observed
serial_read - First observed
serial_wait_for
TDQS
Scored across 9 tools
The four output-reading tools (serial_read, serial_output_history, serial_wait_for, serial_monitor) occupy adjacent territory, but their descriptions explicitly cross-reference each other and draw sharp boundaries (drain vs. lossless capture, known text vs. open-ended listen). The remaining tools target distinct concerns (ports, status, boot, incidents, report), so an agent can reliably pick apart.
All nine tools share the serial_ prefix, giving a strong predictable namespace. Most are verb_noun (serial_read, serial_wait_for, serial_list_ports, serial_get_incidents), though a few are noun_status forms (serial_port_status, serial_boot_status) — a minor deviation, not a genuine inconsistency.
Nine tools is well-scoped for a serial-debugging broker: each covers a distinct facet (port discovery, status, read/history, waiting/listening, boot, incidents, report). No tool appears redundant or padded.
Observational coverage is thorough — discovery, liveness, historical and live capture, waiting, anomaly detection, boot tracking, and reporting. The notable gap is that the surface is entirely read-only (no send/write tool), though the descriptions frame the server as an observer/broker, so this may be scoped intent rather than a true gap.
Maintenance
Related MCP Connectors
Find MCP servers for ESP32, Arduino, 3D printers, Home Assistant, cameras and other hardware.
2,000+ MCP servers read at source level. Know what one does before you connect. Free, no key.
MCP Server for an Agent Task Marketplace
Free, read-only security scanner for remote MCP servers, before you connect them.
Related MCP Servers
- AlicenseAqualityCmaintenanceStateful MCP server for driving debug probes (J-Link) to flash, debug, and inspect embedded targets. Enables AI agents to perform flash, memory, breakpoint, and ELF/SVD-aware operations conversationally.4141 PyPI10MIT
- FlicenseBqualityDmaintenanceMCP server for running shell commands on a Linux development board via UART serial.1-
- AlicenseAqualityAmaintenanceAgentic Hardware-in-the-Loop (Agentic HIL) lets coding agents develop firmware on real embedded boards: flash, reset, read UART and exchange CAN traffic, diagnose and fix. Local, policy-gated MCP tools support OpenOCD, pyOCD and STM32CubeProgrammer, with pytest for CI.44745 PyPI18Apache 2.0
- AlicenseBqualityBmaintenanceMCP server for embedded board debugging, providing structured serial capture, crash decoding, and flash-safe port arbitration via bounded CLI and MCP tools.102MIT