Skip to main content
Glama

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

smolmux on an ESP32-S3: smolmux-monitor in the top pane; in the bottom pane a script sends a command and waits for the reboot, then esptool borrows the port through with-port

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 0600 under 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 a 0600 file in $XDG_RUNTIME_DIR (or /tmp), so other local users and processes cannot connect; your own smolmux-monitor / smolmux-mcp read it automatically, and smolmux-cli token prints 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-auth turns both protections off.

  • MCP servers - standalone smolmux-mcp / smolmux-gdb-mcp attach to a running broker; optional in-process --mcp sink for single-process stdio

  • Boot tracking & autoresponder - ordered boot stages, stall events, standing expect->send rules

  • Autoboot interrupt - broker-side key flood (and optional DTR/RTS reset) for bootdelay=0 U-Boot

  • Device 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-packages
cmake -B build && cmake --build build -j$(nproc)
./build/smolmux /dev/ttyUSB0

Connect a client:

# Needs a running broker (above). Auto-discovers the socket from the port name.
./build/smolmux-monitor /dev/ttyUSB0

Day-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 tests

Feature 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=OFF

Developer benchmarks (e.g. the output coalescer harness) are off by default:

cmake -B build -DSM_BUILD_BENCH=ON && cmake --build build --target bench_coalesce

Interactive configuration:

cmake --build build --target menuconfig

Static 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 help

Wire 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 esc or -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 --mcp sink embeds MCP in-process.

  • smolmux-gdb-mcp - standalone MCP server for GDB debugging: 21 tools over a broker holding a --gdb link - 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 include smolmux-gdb://board-probing and probe_unknown_board. Built when SM_ENABLE_GDB is on; chip-ID validated on a SAM C21 Xplained Pro.

  • smolmux-watcher - daemon that monitors for anomalies and saves incident reports to disk

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 tools
serial_boot_statusA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
stateYes
totalYes
stagesYes
reachedYes
furthestYes
configuredYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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_reportA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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_incidentsA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
secondsNoIf > 0, only return incidents from the last N seconds.

Output Schema

ParametersJSON Schema
NameRequiredDescription
countYes
incidentsYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_portsA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
portsYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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_monitorA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
duration_secondsNoHow long to monitor (max 300 seconds, default 30).

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_historyA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
secondsNoIf > 0, return output from the last N seconds (prose).
max_bytesNoWith since_seq: max raw bytes in this page (default broker cap).
since_seqNoCursor from a prior history response: lossless page as JSON (cursor/dropped/has_more/chunks). Prefer this over serial_read.
last_bytesNoIf > 0, return the last N bytes of output (prose).

Output Schema

ParametersJSON Schema
NameRequiredDescription
modeYes
textNo
chunksNo
cursorNo
droppedNo
has_moreNo

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_statusA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
baudYes
portYes
roleNo
boardNo
clientsYes
log_pathNo
connectedYes
link_typeNo
suspendedYes
link_up_tsNo
pin_statesNo
identity_by_idNo
last_rx_age_msNo
last_link_eventNo
takeover_clientYes
identity_by_pathNo
identity_strengthNo
bytes_rx_since_link_upNo

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_readA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_forA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
patternYesRegex to match in device output (listen-only; no TX).
timeout_msNoMax wait in ms (default 30000, clamp 100–3600000).

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 5 tool updates
    • Changedserial_boot_status1 field changed
      • changedOutput 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"
        +}
    • Changedserial_get_incidents1 field changed
      • changedOutput 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"
        +}
    • Changedserial_list_ports1 field changed
      • changedOutput 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"
        +}
    • Changedserial_output_history1 field changed
      • changedOutput 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"
        +}
    • Changedserial_port_status1 field changed
      • changedOutput 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"
        +}
  2. 9 tool updatesv0.4.0
    • First observedserial_boot_status
    • First observedserial_generate_report
    • First observedserial_get_incidents
    • First observedserial_list_ports
    • First observedserial_monitor
    • First observedserial_output_history
    • First observedserial_port_status
    • First observedserial_read
    • First observedserial_wait_for

TDQS

A4.3/5.0

Scored across 9 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Stateful 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.
    41
    41 PyPI
    10
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Agentic 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.
    44
    745 PyPI
    18
    Apache 2.0