tinysa-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tinysa-mcp scan 500k to 25M and list the strongest peaks"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tinySA MCP and CLI
A standalone, headless Python USB driver shared by a command-line tool and an MCP server. No TinySA Saver, Qt, or GUI is required. Designed for scripting and agent-controlled measurements on macOS, Linux, and Windows. Automated and hardware verification procedures are described in VALIDATION.md. Console guide translates the firmware's command list into routine measurement operations.
Install
Python 3.11+ and uv are required for the development setup:
git clone https://github.com/ikatkov/tinysa-mcp.git
cd tinysa-mcp
uv sync --locked
uv run tinysa ports
uv run tinysa infoFor a command available outside the project directory:
uv tool install .
tinysa --helpAlternatively, python3 -m venv .venv and .venv/bin/python -m pip install -e .
install the runtime without uv. The Python API is TinySA(USBTransport(port));
always call close() in a finally block.
Related MCP server: pico-mcp
USB preparation
Select analyzer/input mode on the tinySA, set CONFIG → CONNECTION → USB if present, and connect a data-capable cable. Close TinySA Saver, NanoVNA Saver, serial terminals, and other clients. Only one process should use the instrument. Serial baud rate is 115200 (USB CDC generally ignores it).
Auto-detection actively tries USB serial ports: products named tinySA first, then
devices with STM32 USB ID 0483:5740, then other USB serial endpoints. It sends an
empty prompt request and info, accepting the first instrument that identifies as
tinySA. Busy, silent, and unrelated devices are closed and skipped. Each prompt and
identity probe has a deadline of at most two seconds. Bluetooth and built-in console
ports are skipped. If nothing matches, the error lists attempted ports and why each
failed. If multiple tinySAs are attached, choose one explicitly instead of relying
on the deterministic first match.
To override detection, use tinysa --port PORT info with a port returned by
tinysa ports, or set TINYSA_PORT. An explicit port is verified and never falls
back to another device. The account needs permission to open the serial device.
Discovery opens no serial device.
On macOS, these answer different questions:
system_profiler SPUSBDataType # all USB devices and bus topology
tinysa ports # serial endpoints and candidate identities
python3 -m serial.tools.list_ports -v # pyserial's serial inventoryMeasure and script
Global options (--port, --json, timeouts) go before the subcommand:
# Save the current actual/displayed trace to CSV + metadata, with an ASCII chart.
tinysa capture
tinysa capture --output measurements.csv --width 120
# Set bandwidth explicitly (Hz API, kHz console command).
tinysa configure --rbw 10k --attenuation auto --unit dbm
# New scan, not a snapshot. Firmware leaves the instrument paused by default.
tinysa --scan-timeout 120 scan --start 500k --stop 25M --points 450
tinysa resume
# Resume the configured display sweep immediately after a fresh acquisition.
tinysa scan --start 1M --stop 10M --points 100 --resume-after
# Complete measurement and artifact paths as JSON; no chart on stdout.
tinysa --json capture --no-chart > measurement.json
# CSV to stdout for pipelines; no metadata file is created for stdout export.
tinysa capture --output - > spectrum.csv
# Repeated snapshots: one CSV + metadata per acquisition; NDJSON with --json.
tinysa --json record --count 10 --interval 2 --directory readings > captures.ndjson
tinysa record --count 0 --interval 1 # until Ctrl-C
# A completed fresh scan for every record (interval is a minimum, not a sampling rate).
tinysa --json record --fresh --start 500k --stop 25M --points 450 --count 10 --interval 2
# Strongest sampled local maxima, optionally enforce frequency separation.
tinysa --json peaks --limit 10 --min-level -90 --min-separation 100k
tinysa status
tinysa console-helpEach saved CSV has exactly frequency_hz,level_dbm followed by all sampled points.
Its .json sidecar records schema version, source, device/firmware identity, UTC
start/end times, settings readback, state effects, and a peak summary. Default files
are timestamped in ./readings; TINYSA_EXPORT_DIR or --directory changes this.
Existing CSV and metadata files are protected. Timestamps describe host acquisition
and transfer times; individual sample timestamps are not provided by the USB protocol.
The ASCII chart bins samples to terminal columns and preserves each bin's minimum
and maximum. Narrow spurs remain visible; screen geometry cannot be reproduced exactly
at terminal resolution. The CSV retains all samples. Zero span uses sample order on
the horizontal axis, not calibrated time. Use --no-chart, --width, and --height.
With --json --chart or --output - --chart, the chart goes to stderr.
Exit status: 0 success, 1 measurement/connection/file error, 2 argument syntax
error, 130 Ctrl-C. With --json, runtime errors have an error field. Successful
single commands produce one JSON object; record produces one per capture. Parser
errors still use argparse's stderr. No retries on a failed scan.
Use with an AI agent
The server uses the official MCP Python SDK and stdio transport. After uv tool install .,
make tinysa-mcp available on the MCP client's PATH. For clients with a JSON
mcpServers configuration:
{
"mcpServers": {
"tinysa": {
"command": "tinysa-mcp",
"args": []
}
}
}USB detection is automatic unless --port or TINYSA_PORT specifies a device.
Exports default to readings under the server's working directory; choose the
destination at runtime with --export-dir or TINYSA_EXPORT_DIR.
If the client cannot find the command, configure its PATH or supply the executable
location in that client's configuration. tinysa serve is equivalent. USB opens
lazily on the first instrument tool, so discovery works when no instrument is attached.
The server retains the connection until disconnect_device or shutdown. Diagnostics
go to stderr; stdout is reserved for MCP. There is no network listener or GUI.
For Codex, register the installed command with:
codex mcp add tinysa -- tinysa-mcp
codex mcp get tinysaIn the Codex configuration, set tool_timeout_sec = 180.0 in [mcp_servers.tinysa]
to allow the server's 120-second scan deadline plus readout overhead.
For Claude Code, register it for your user account across projects:
claude mcp add --scope user --transport stdio tinysa -- tinysa-mcp
claude mcp get tinysaUse /mcp in Claude Code to inspect the connection. Client registrations and export
destinations belong in each user's configuration rather than in the repository.
Tools:
Tool | Purpose / effects |
| Serial inventory; no USB open |
| Verified model and firmware identity |
| Current state and raw settings readback |
| Current trace; temporarily pauses and restores running state |
| Fresh scan; replaces trace and leaves paused unless |
| Explicit RBW/attenuation/dBm changes and readback |
| Snapshot plus sampled local maxima |
| Explicit display control |
| Release the serial connection |
| Commands actually available on the attached firmware |
capture_spectrum and scan_spectrum support save=true, an optional simple .csv
filename, and include_points=false for compact results. File names stay within
the configured export directory. Tools return structured JSON plus MCP text content.
Read tinysa://guide before measurement; tinysa://export-directory shows the artifact
directory. Measurement tools declare state changes in MCP annotations.
Acquisition tool results also include ten sampled local maxima from that exact capture,
so an agent can discuss peaks without taking a second measurement.
Example agent request: “Identify the tinySA, read its settings, capture the current trace and save it, then report the five strongest sampled peaks with frequencies.”
Expert raw console access is opt-in: start the server with --allow-raw to expose
execute_console_command, or use tinysa command --allow-raw 'rbw 10'. Arbitrary
commands can alter calibration, persistent settings, and generator output. Binary
commands (capture, scanraw, sd_read) and reset are excluded from this text tool.
Measurement semantics and limits
A snapshot reads
frequenciesand actualdata 2while paused, then restores the earlier running state. It does not initiate a new scan. It requires firmware withstatus. A previously paused display remains paused. Pausing can interrupt an ongoing sweep; the displayed trace can contain samples from different sweep passes. Use a fresh scan when a complete acquisition matters. Frequent snapshot polling can keep interrupting the display; userecord --freshfor repeated completed acquisitions.A scan uses
scan START STOP POINTS 3. Frequencies and actual levels arrive in one response, including the legacy zero imaginary component on some firmware. The old point count is restored afterwards. Resume the display before a subsequent snapshot if the scan used a different point count. Display settings and frequency arrays are not a full transactional instrument-state snapshot. Within a connected driver session, snapshots after a different-point-count scan are refused until the sweep is resumed. Across independent CLI invocations, the driver cannot know earlier console operations; resume explicitly before capture.Data follows the selected display unit. This API requires dBm and rejects other units instead of mislabelling them.
configure --unit dbmchanges the display explicitly. Input/analyzer mode is selected by the user, not changed automatically.Existing trace processing (average, max hold, etc.) can affect data; raw
calcreadback accompanies every capture. A fresh scan does not reset this processing.Basic allows up to 290 scan points; Ultra family up to 450. Frequencies accept integer Hz up to a validation ceiling of 12 GHz; usable ranges depend on the actual model, input mode, firmware, and RF front end. This ceiling is not a claim that every instrument supports 12 GHz.
RBW requests are Hz; the console accepts kHz. Firmware chooses available filters; read actual bandwidth from
rbw_reply/rbw_after_reply. Some settings take effect at the next sweep. Query replies are retained verbatim for firmware differences.Peak finding uses local sampled maxima, plateaus, and frequency separation. It does not interpolate frequencies, compute integrated power, or identify the origin of spurs. A reported peak can be at a scan edge.
USB reliability and troubleshooting
The transport reads until a complete ch> prompt, even if split across packets or
without a newline. Compound operations share one lock. POSIX exclusive opens prevent
cooperating clients from sharing the port; close already-running GUIs yourself.
Replies have a 1 MiB bound and a finite deadline. Ordinary timeout is 5 seconds;
fresh scan timeout defaults to 120 seconds and accepts up to 3600. A timeout closes
the connection; the instrument may still be scanning and its state is then unknown.
Wait for completion and reconnect rather than immediately starting another scan.
Malformed values (including the older firmware's -:.000000e+01 formatter bug),
non-finite values, length mismatches, and incorrect endpoints are reported as errors.
Values are never guessed or silently repaired. If a device is silent, check physical
USB connection mode and cable, then power cycle. Narrow RBW and repeated/long sweeps
can require much more time than a simple settings query.
Development
uv sync --locked
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv buildUnit tests use a fragmented fake serial device. The stdio MCP test launches a real server process against a pseudo-terminal instrument, exercising initialization, tool schemas, structured measurements, error delivery, resources, and shutdown. No hardware is required for tests.
uv run python examples/client.py demonstrates a real MCP client capturing and
exporting the current trace. uv run python examples/measure.py demonstrates the
direct Python API. uv run python tools/verify_hardware.py performs opt-in hardware
verification with one fresh scan and restores the earlier running/paused state.
The implementation was written independently after reviewing the
experimental tinySA_mcp project, the
official USB interface,
Ultra console examples,
and firmware source. It avoids a mandatory
Tkinter GUI and arbitrary short sleeps for reply framing. Those firmware commands
vary by release; console-help and settings readback describe the attached instrument.
Available Tools
11 toolscapture_spectrumB
Snapshot current actual dBm trace; briefly pause and restore previous running state.
Does not guarantee a fresh scan. save writes CSV and metadata to the export directory.
Inputs: save, filename, include_points. Returns: object
configure_measurementA
Explicitly set bandwidth in Hz, attenuation in dB, or dBm display units; read back settings.
Firmware rounds bandwidth to available filters. None leaves a setting unchanged. Auto and explicit value are mutually exclusive. Changes are not rolled back on error.
Inputs: rbw_hz, rbw_auto, unit_dbm, attenuation_db, +1 more. Returns: object
disconnect_deviceA
Release USB; the next device tool reconnects. Does not change the current sweep state.
Inputs: no named parameters. Returns: object
find_spectrum_peaksB
Snapshot current trace and find strongest sampled local maxima; temporarily pauses readout.
Inputs: limit, min_level_dbm, min_separation_hz. Returns: object
get_console_helpARead-only
Read the connected firmware's actual supported console commands.
Inputs: no named parameters. Returns: object
get_device_infoBRead-only
Connect lazily, verify tinySA identity, and report firmware and maximum scan points.
Inputs: no named parameters. Returns: object
get_statusARead-only
Read sweep state, bandwidth, attenuation, display units, and processing settings.
Inputs: no named parameters. Returns: object
list_devicesARead-only
List serial ports and USB candidates. Does not connect; STM32 IDs can also be NanoVNAs.
Inputs: no named parameters. Returns: object
pause_sweepA
Pause the displayed sweep explicitly.
Inputs: no named parameters. Returns: object
resume_sweepA
Resume the instrument's configured display sweep (including frequency array restoration).
Inputs: no named parameters. Returns: object
scan_spectrumA
Acquire a new dBm spectrum with paired frequencies/levels; default leaves device paused.
Endpoints are integer Hz. Basic <=290 points, Ultra <=450. resume_after restarts the
configured display sweep. No automatic change to input mode, RBW, or processing.
Inputs: stop_hz (required), start_hz (required), save, points, +3 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 11 tools
The sweep-control pair (pause_sweep/resume_sweep) and lifecycle tools (list_devices/get_device_info/disconnect_device) are clearly distinct. However, capture_spectrum, scan_spectrum, and find_spectrum_peaks all deal with trace acquisition and could be confused, though the descriptions do clarify snapshot vs. fresh scan vs. peak analysis.
All tools use consistent snake_case verb_noun or verb patterns (list_devices, get_status, configure_measurement, scan_spectrum). No mixed conventions or vague single-word verbs.
11 tools is well-scoped for a device-control server, covering connection, status, configuration, acquisition, and sweep control without redundancy.
The surface covers the full device lifecycle: list, connect/info, disconnect, status, configuration, acquisition, pause/resume, and console help. Minor gaps exist (configuration is limited to bandwidth/attenuation/units with no broader settings, and saving is folded into capture_spectrum rather than a dedicated export tool).
Related MCP Connectors
CVE lookups (NVD) and dependency-manifest audits (OSV) for AI agents. No API keys.
CVE lookups (NVD) and dependency-manifest audits (OSV) for AI agents. No API keys.
AI agent infrastructure for discovery, authorization, execution, identity, and signed receipts.
Free, privacy-respecting software and public discovery tools for AI agents.
Related MCP Servers
- AlicenseBqualityAmaintenanceEnables AI agents to control Copper Mountain Vector Network Analyzers over TCP/IP SCPI, offering 45 tools for sweep configuration, calibration, measurement, and Touchstone export.492AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables LLMs to control a PicoScope 5000A USB oscilloscope for signal generation, block capture, measurements, and frequency response sweeps.1MIT
- AlicenseBqualityBmaintenanceEnables AI agents to process RF signals, perform spectral analysis, and execute radar DSP primitives such as Range-Doppler processing and CFAR detection, supporting both file-based and live SDR capture.14MIT
- FlicenseAqualityBmaintenanceEnables AI assistants to operate a Fishball7020/PlutoSky software-defined radio, including tuning, spectrum analysis, band scanning, IQ capture, FPGA filtering, and optional transmission.164-