pyserial-mcp 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyserial_mcp-0.4.0/.github/workflows/ci.yml +32 -0
- pyserial_mcp-0.4.0/.github/workflows/release.yml +130 -0
- pyserial_mcp-0.4.0/.gitignore +10 -0
- pyserial_mcp-0.4.0/CLAUDE.md +72 -0
- pyserial_mcp-0.4.0/LICENSE +21 -0
- pyserial_mcp-0.4.0/PKG-INFO +290 -0
- pyserial_mcp-0.4.0/README.md +260 -0
- pyserial_mcp-0.4.0/RELEASING.md +69 -0
- pyserial_mcp-0.4.0/docs/superpowers/plans/2026-04-02-serial-mcp-improvements.md +1906 -0
- pyserial_mcp-0.4.0/docs/superpowers/specs/2026-04-02-serial-mcp-improvements-design.md +240 -0
- pyserial_mcp-0.4.0/docs/superpowers/specs/2026-04-29-pypi-release-automation-design.md +151 -0
- pyserial_mcp-0.4.0/manifest.json +27 -0
- pyserial_mcp-0.4.0/pyproject.toml +60 -0
- pyserial_mcp-0.4.0/requirements.txt +2 -0
- pyserial_mcp-0.4.0/scripts/build-mcpb.sh +29 -0
- pyserial_mcp-0.4.0/scripts/run.py +12 -0
- pyserial_mcp-0.4.0/serial_mcp/__init__.py +0 -0
- pyserial_mcp-0.4.0/serial_mcp/server.py +1055 -0
- pyserial_mcp-0.4.0/serial_mcp/session.py +485 -0
- pyserial_mcp-0.4.0/serial_mcp/xmodem.py +240 -0
- pyserial_mcp-0.4.0/tests/__init__.py +0 -0
- pyserial_mcp-0.4.0/tests/conftest.py +99 -0
- pyserial_mcp-0.4.0/tests/test_logging.py +148 -0
- pyserial_mcp-0.4.0/tests/test_server.py +63 -0
- pyserial_mcp-0.4.0/tests/test_session.py +216 -0
- pyserial_mcp-0.4.0/tests/test_xmodem.py +153 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- run: pip install ruff
|
|
18
|
+
- run: ruff check serial_mcp/ tests/
|
|
19
|
+
- run: ruff format --check serial_mcp/ tests/
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
strategy:
|
|
24
|
+
matrix:
|
|
25
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
- run: pip install -e ".[dev]"
|
|
32
|
+
- run: pytest -v
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
verify:
|
|
10
|
+
name: Verify tag matches version files
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- name: Check tag agrees with pyproject.toml and manifest.json
|
|
18
|
+
run: |
|
|
19
|
+
python3 - <<'PY'
|
|
20
|
+
import json, os, sys, tomllib
|
|
21
|
+
tag = os.environ["GITHUB_REF_NAME"]
|
|
22
|
+
if not tag.startswith("v"):
|
|
23
|
+
sys.exit(f"tag {tag!r} must start with 'v'")
|
|
24
|
+
tag_version = tag[1:]
|
|
25
|
+
with open("pyproject.toml", "rb") as f:
|
|
26
|
+
pyproject_version = tomllib.load(f)["project"]["version"]
|
|
27
|
+
with open("manifest.json") as f:
|
|
28
|
+
manifest_version = json.load(f)["version"]
|
|
29
|
+
mismatches = []
|
|
30
|
+
if pyproject_version != tag_version:
|
|
31
|
+
mismatches.append(f"pyproject.toml has {pyproject_version}")
|
|
32
|
+
if manifest_version != tag_version:
|
|
33
|
+
mismatches.append(f"manifest.json has {manifest_version}")
|
|
34
|
+
if mismatches:
|
|
35
|
+
sys.exit(f"Version mismatch — tag is v{tag_version}; " + "; ".join(mismatches))
|
|
36
|
+
print(f"OK: tag, pyproject.toml, and manifest.json all agree on {tag_version}")
|
|
37
|
+
PY
|
|
38
|
+
|
|
39
|
+
lint:
|
|
40
|
+
needs: verify
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.12"
|
|
47
|
+
- run: pip install ruff
|
|
48
|
+
- run: ruff check serial_mcp/ tests/
|
|
49
|
+
- run: ruff format --check serial_mcp/ tests/
|
|
50
|
+
|
|
51
|
+
test:
|
|
52
|
+
needs: verify
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
strategy:
|
|
55
|
+
matrix:
|
|
56
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: ${{ matrix.python-version }}
|
|
62
|
+
- run: pip install -e ".[dev]"
|
|
63
|
+
- run: pytest -v
|
|
64
|
+
|
|
65
|
+
build:
|
|
66
|
+
needs: [lint, test]
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
- uses: actions/setup-python@v5
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.12"
|
|
73
|
+
- uses: actions/setup-node@v4
|
|
74
|
+
with:
|
|
75
|
+
node-version: "20"
|
|
76
|
+
- name: Build sdist and wheel
|
|
77
|
+
run: |
|
|
78
|
+
pip install build
|
|
79
|
+
python -m build
|
|
80
|
+
- name: Build MCPB bundle
|
|
81
|
+
run: bash scripts/build-mcpb.sh
|
|
82
|
+
- name: Stage MCPB artifact
|
|
83
|
+
run: |
|
|
84
|
+
mkdir -p artifacts
|
|
85
|
+
cp build/mcpb/*.mcpb artifacts/
|
|
86
|
+
ls -la artifacts/ dist/
|
|
87
|
+
- name: Upload Python distributions
|
|
88
|
+
uses: actions/upload-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: python-dist
|
|
91
|
+
path: dist/
|
|
92
|
+
- name: Upload MCPB
|
|
93
|
+
uses: actions/upload-artifact@v4
|
|
94
|
+
with:
|
|
95
|
+
name: mcpb
|
|
96
|
+
path: artifacts/*.mcpb
|
|
97
|
+
|
|
98
|
+
publish-pypi:
|
|
99
|
+
needs: build
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment:
|
|
102
|
+
name: pypi
|
|
103
|
+
url: https://pypi.org/p/pyserial-mcp
|
|
104
|
+
permissions:
|
|
105
|
+
id-token: write
|
|
106
|
+
steps:
|
|
107
|
+
- name: Download Python distributions
|
|
108
|
+
uses: actions/download-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
name: python-dist
|
|
111
|
+
path: dist/
|
|
112
|
+
- name: Publish to PyPI
|
|
113
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
114
|
+
|
|
115
|
+
github-release:
|
|
116
|
+
needs: publish-pypi
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
steps:
|
|
121
|
+
- name: Download MCPB
|
|
122
|
+
uses: actions/download-artifact@v4
|
|
123
|
+
with:
|
|
124
|
+
name: mcpb
|
|
125
|
+
path: artifacts/
|
|
126
|
+
- name: Create GitHub Release
|
|
127
|
+
uses: softprops/action-gh-release@v2
|
|
128
|
+
with:
|
|
129
|
+
generate_release_notes: true
|
|
130
|
+
files: artifacts/*.mcpb
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
serial-mcp is an MCP (Model Context Protocol) server that enables LLMs to communicate with serial devices (microcontrollers, routers, modems, embedded Linux). Python 3.10+, MIT licensed.
|
|
8
|
+
|
|
9
|
+
## Build & Run Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install (editable, with dev dependencies)
|
|
13
|
+
uv pip install -e ".[dev]"
|
|
14
|
+
|
|
15
|
+
# Run the MCP server
|
|
16
|
+
python3 -m serial_mcp.server
|
|
17
|
+
|
|
18
|
+
# Test with MCP Inspector
|
|
19
|
+
DANGEROUSLY_OMIT_AUTH=true npx @modelcontextprotocol/inspector -- python3 -m serial_mcp.server
|
|
20
|
+
|
|
21
|
+
# Install dependencies only
|
|
22
|
+
uv pip install -r requirements.txt
|
|
23
|
+
|
|
24
|
+
# Run tests
|
|
25
|
+
pytest -v
|
|
26
|
+
|
|
27
|
+
# Lint
|
|
28
|
+
ruff check serial_mcp/ tests/
|
|
29
|
+
ruff format --check serial_mcp/ tests/
|
|
30
|
+
|
|
31
|
+
# Build MCPB bundle
|
|
32
|
+
./scripts/build-mcpb.sh
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
Three-file architecture in `serial_mcp/`:
|
|
38
|
+
|
|
39
|
+
- **server.py** — FastMCP server exposing ~22 async tools (all prefixed `serial_*`) and 3 prompts. Maintains a global `_sessions` dict keyed by port name with atexit cleanup. `_resolve_session()` auto-selects when only one session is open. All tools include MCP annotations (readOnlyHint, destructiveHint, etc.). Blocking serial I/O is wrapped in `asyncio.to_thread()`. Tools are grouped: port discovery, connection management, text read/write, binary/hex read/write, hardware signal control, session utilities, XMODEM file transfer, and logging. Supports elicitation for interactive port and baud selection.
|
|
40
|
+
|
|
41
|
+
- **session.py** — `SerialSession` class managing individual serial connections. Runs a daemon background reader thread that stores data in a timestamped ring buffer (10MB default cap). Supports both destructive reads (`read_buffer`) and non-destructive historical reads (`read_since`). Thread safety via `threading.Lock` for history and `threading.Event` for data availability and shutdown signaling.
|
|
42
|
+
|
|
43
|
+
- **xmodem.py** — Pure-Python XMODEM send/receive. Takes read/write callables for testability. Supports checksum and CRC-16 modes.
|
|
44
|
+
|
|
45
|
+
Entry point: `serial_mcp.server:main()` (registered as `serial-mcp` console script via pyproject.toml/Hatchling).
|
|
46
|
+
|
|
47
|
+
## Key Design Decisions
|
|
48
|
+
|
|
49
|
+
- **Timestamped ring buffer**: All received data is stored with timestamps, enabling `read_since()` for history replay without consuming the buffer. Automatic trimming adjusts the read cursor.
|
|
50
|
+
- **Pattern matching**: `serial_command()` waits for a regex match OR 300ms of silence. `serial_wait_for()` blocks until a pattern appears or timeout.
|
|
51
|
+
- **Hardware signals**: Full DTR/RTS control and CTS/DSR/RI/CD readback for reset sequences and bootloader entry.
|
|
52
|
+
- **Baud detection**: Tries 8 common rates, scores readability by printable ASCII ratio, optional `\r\n` probing.
|
|
53
|
+
- **XMODEM file transfer**: Pure-Python implementation with reader thread pause/resume. Uses callable abstraction for testability.
|
|
54
|
+
|
|
55
|
+
## Testing
|
|
56
|
+
|
|
57
|
+
27 unit tests using pytest with a `MockSerial` fixture (no real hardware needed). Tests cover session buffer management, pattern matching, history trimming, logging, XMODEM protocol, and server tool resolution.
|
|
58
|
+
|
|
59
|
+
Run: `pytest -v` (requires dev dependencies: `uv pip install -e ".[dev]"`)
|
|
60
|
+
|
|
61
|
+
## Gotchas
|
|
62
|
+
|
|
63
|
+
- **XMODEM pauses the reader thread**: During `serial_xmodem_send`/`serial_xmodem_receive`, the background reader is stopped for exclusive port access. Logging and `read_buffer` won't capture data during transfers.
|
|
64
|
+
- **Elicitation fallback**: `serial_open` (portless) and `serial_detect_baud` use elicitation when supported. If the host doesn't support it, they return data for the LLM to relay — not an error.
|
|
65
|
+
|
|
66
|
+
## Dependencies
|
|
67
|
+
|
|
68
|
+
Only two runtime deps: `mcp >= 1.0.0` and `pyserial >= 3.5`. Dev: `ruff`, `pytest`, `pytest-asyncio`.
|
|
69
|
+
|
|
70
|
+
## Releasing
|
|
71
|
+
|
|
72
|
+
Releases are automated by `.github/workflows/release.yml` on tag push (`v*.*.*`). To cut a release: bump `version` in **both** `pyproject.toml` and `manifest.json`, commit, then `git tag vX.Y.Z && git push origin vX.Y.Z`. The workflow verifies the tag matches both files, runs lint + tests, builds sdist/wheel/`.mcpb`, publishes to PyPI via Trusted Publishing (gated by a manual approval), and creates a GitHub Release with the `.mcpb` attached. See [RELEASING.md](RELEASING.md) for full procedure and recovery steps.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Alex Gompper
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyserial-mcp
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: MCP server for serial device communication
|
|
5
|
+
Project-URL: Homepage, https://github.com/alxgmpr/serial-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/alxgmpr/serial-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/alxgmpr/serial-mcp/issues
|
|
8
|
+
Author: Alex Gompper
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: embedded,iot,mcp,pyserial,serial,uart
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications
|
|
21
|
+
Classifier: Topic :: System :: Hardware
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: mcp>=1.0.0
|
|
24
|
+
Requires-Dist: pyserial>=3.5
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# serial-mcp
|
|
32
|
+
|
|
33
|
+
MCP server for serial port communication. Lets LLMs talk to hardware — microcontrollers, routers, modems, embedded Linux, anything with a UART.
|
|
34
|
+
|
|
35
|
+
Why use this and not any of the many others out there?
|
|
36
|
+
|
|
37
|
+
1. Real useful tools that the LLM can use like waiting/expecting data.
|
|
38
|
+
2. Better test suite, and I test the commands myself.
|
|
39
|
+
3. I actually use this day to day for real hardware hacking.
|
|
40
|
+
|
|
41
|
+
<img width="1456" height="1132" alt="image" src="https://github.com/user-attachments/assets/17e948ae-4888-4748-8694-77c1e257e329" />
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## What it does
|
|
45
|
+
|
|
46
|
+
Exposes serial ports as MCP tools so an AI assistant can:
|
|
47
|
+
|
|
48
|
+
- **Discover** connected USB-serial adapters and identify them by VID/PID
|
|
49
|
+
- **Connect** to devices with configurable baud rate, data bits, stop bits, parity
|
|
50
|
+
- **Send commands** and wait for responses (with regex-based expect patterns)
|
|
51
|
+
- **Read/write raw hex** for binary protocols (Modbus, bootloader commands, etc.)
|
|
52
|
+
- **Control hardware signals** (DTR/RTS) — reset Arduinos, enter ESP32 bootloader mode
|
|
53
|
+
- **Auto-detect baud rate** by trying common rates and scoring readability
|
|
54
|
+
- **Transfer files** with XMODEM (checksum or CRC-16)
|
|
55
|
+
- **Log received data** to a file for capture / postmortem analysis
|
|
56
|
+
- **Manage multiple sessions** simultaneously across different ports
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
### With `uv` (recommended)
|
|
61
|
+
|
|
62
|
+
Install globally so the `serial-mcp` command is available everywhere:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
uv tool install serial-mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or from a local clone:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
uv tool install /path/to/serial-mcp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With pip
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
pip install serial-mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### From source (editable)
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
git clone https://github.com/alxgmpr/serial-mcp.git
|
|
84
|
+
cd serial-mcp
|
|
85
|
+
uv pip install -e .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Configure
|
|
89
|
+
|
|
90
|
+
### Claude Code
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
claude mcp add serial-mcp -- serial-mcp
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That's it. Verify with `claude mcp list`.
|
|
97
|
+
|
|
98
|
+
If you installed from source instead of globally, use the full path:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
claude mcp add serial-mcp -- python3 -m serial_mcp.server
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Claude Desktop (`claude_desktop_config.json`)
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"mcpServers": {
|
|
109
|
+
"serial": {
|
|
110
|
+
"command": "serial-mcp"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### With `uvx` (no install)
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"serial": {
|
|
122
|
+
"command": "uvx",
|
|
123
|
+
"args": ["serial-mcp"]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Tools
|
|
130
|
+
|
|
131
|
+
All tools are prefixed with `serial_` to avoid name collisions with other MCP servers. Each tool includes MCP annotations (`readOnlyHint`, `destructiveHint`, etc.).
|
|
132
|
+
|
|
133
|
+
### Port discovery
|
|
134
|
+
|
|
135
|
+
| Tool | Description |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `list_serial_ports` | List available serial ports with USB metadata (VID/PID, manufacturer) |
|
|
138
|
+
| `serial_detect_baud` | Auto-detect baud rate by trying common rates and scoring ASCII readability |
|
|
139
|
+
|
|
140
|
+
### Connection management
|
|
141
|
+
|
|
142
|
+
| Tool | Description |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `serial_open` | Open a serial connection (baud, data bits, stop bits, parity, timeout) |
|
|
145
|
+
| `serial_close` | Close a connection |
|
|
146
|
+
| `serial_change_settings` | Change baud/parity/etc. on a live connection without closing it |
|
|
147
|
+
| `serial_list_sessions` | List all open sessions |
|
|
148
|
+
| `serial_status` | Detailed connection health, byte counts, uptime |
|
|
149
|
+
|
|
150
|
+
### Read / write (text)
|
|
151
|
+
|
|
152
|
+
| Tool | Description |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `serial_command` | Send a string, wait for response. Supports `expect` regex for prompt detection |
|
|
155
|
+
| `serial_write` | Fire-and-forget text write |
|
|
156
|
+
| `serial_read` | Read buffered data (advances cursor) |
|
|
157
|
+
| `serial_read_since` | Read historical data since a timestamp (non-destructive) |
|
|
158
|
+
| `serial_wait_for` | Block until a regex pattern appears in incoming data |
|
|
159
|
+
|
|
160
|
+
### Read / write (binary)
|
|
161
|
+
|
|
162
|
+
| Tool | Description |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `serial_write_hex` | Write raw bytes as hex (`"AA 55 01 03"`) |
|
|
165
|
+
| `serial_read_hex` | Read buffered data as hex string |
|
|
166
|
+
|
|
167
|
+
### Hardware signals
|
|
168
|
+
|
|
169
|
+
| Tool | Description |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `serial_set_signals` | Control DTR/RTS (reset micros, enter bootloader, etc.) |
|
|
172
|
+
| `serial_get_signals` | Read DTR, RTS, CTS, DSR, RI, CD |
|
|
173
|
+
| `serial_send_break` | Send a serial break (interrupt U-Boot, Cisco ROMMON, etc.) |
|
|
174
|
+
|
|
175
|
+
### Session utilities
|
|
176
|
+
|
|
177
|
+
| Tool | Description |
|
|
178
|
+
|---|---|
|
|
179
|
+
| `serial_clear_history` | Flush the receive buffer and free memory |
|
|
180
|
+
|
|
181
|
+
### Logging
|
|
182
|
+
|
|
183
|
+
| Tool | Description |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `serial_log_start` | Capture all received data to a file (like minicom's capture) |
|
|
186
|
+
| `serial_log_stop` | Stop logging and return file path, byte count, and duration |
|
|
187
|
+
|
|
188
|
+
### File transfer
|
|
189
|
+
|
|
190
|
+
| Tool | Description |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `serial_xmodem_send` | Send a file via XMODEM (checksum or CRC-16 mode) |
|
|
193
|
+
| `serial_xmodem_receive` | Receive a file via XMODEM (checksum or CRC-16 mode) |
|
|
194
|
+
|
|
195
|
+
The reader thread is paused for the duration of an XMODEM transfer so the protocol has exclusive port access — `serial_read` and logging won't capture anything during the transfer.
|
|
196
|
+
|
|
197
|
+
## Prompts
|
|
198
|
+
|
|
199
|
+
Three prompts are registered to guide common workflows:
|
|
200
|
+
|
|
201
|
+
| Prompt | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `scan_devices` | Walk through identifying all connected serial devices by VID/PID |
|
|
204
|
+
| `detect_baud_rate` | Run baud detection on a port and interpret the results |
|
|
205
|
+
| `interactive_shell` | Open a connection and probe for the device prompt |
|
|
206
|
+
|
|
207
|
+
## Usage examples
|
|
208
|
+
|
|
209
|
+
### Interactive shell on a Linux device
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
1. list_serial_ports() → find /dev/ttyUSB0
|
|
213
|
+
2. serial_open(port="/dev/ttyUSB0") → connect at 115200 8N1
|
|
214
|
+
3. serial_command(data="", expect="[$#]") → get the shell prompt
|
|
215
|
+
4. serial_command(data="uname -a", expect="\\$")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Arduino / microcontroller
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
1. list_serial_ports() → find /dev/ttyACM0
|
|
222
|
+
2. serial_open(port="/dev/ttyACM0", baud_rate=9600)
|
|
223
|
+
3. serial_command(data="STATUS", timeout=2)
|
|
224
|
+
4. serial_set_signals(dtr=False) → reset the board
|
|
225
|
+
5. serial_set_signals(dtr=True)
|
|
226
|
+
6. serial_wait_for(pattern="Ready", timeout=5)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Unknown baud rate
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
1. serial_detect_baud(port="/dev/ttyUSB0") → recommends 9600
|
|
233
|
+
2. serial_open(port="/dev/ttyUSB0", baud_rate=9600)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Binary protocol (Modbus, etc.)
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
1. serial_open(port="/dev/ttyUSB0", baud_rate=9600)
|
|
240
|
+
2. serial_write_hex(hex_string="01 03 00 00 00 0A C5 CD")
|
|
241
|
+
3. serial_read_hex(timeout=2)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### ESP32 bootloader entry
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
1. serial_open(port="/dev/ttyUSB0", baud_rate=115200)
|
|
248
|
+
2. serial_set_signals(dtr=False, rts=True)
|
|
249
|
+
3. serial_set_signals(dtr=True, rts=False)
|
|
250
|
+
4. serial_set_signals(dtr=False)
|
|
251
|
+
5. serial_wait_for(pattern="waiting for download", timeout=3)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## How it works
|
|
255
|
+
|
|
256
|
+
Each `serial_open()` call creates a `SerialSession` with a background thread that continuously reads from the port into a timestamped ring buffer (default 10MB cap). This means:
|
|
257
|
+
|
|
258
|
+
- **No data loss** — bytes are captured even between tool calls
|
|
259
|
+
- **Non-destructive reads** — `serial_read_since()` can replay history without advancing the cursor
|
|
260
|
+
- **Pattern matching** — `serial_command()` and `serial_wait_for()` scan the buffer for regex matches in real-time
|
|
261
|
+
- **Multiple sessions** — each port gets its own thread and buffer
|
|
262
|
+
|
|
263
|
+
All tools are async. Blocking serial I/O runs in `asyncio.to_thread()` so the event loop stays free.
|
|
264
|
+
|
|
265
|
+
## Testing
|
|
266
|
+
|
|
267
|
+
Run the unit tests (no hardware required — they use a `MockSerial` fixture):
|
|
268
|
+
|
|
269
|
+
```sh
|
|
270
|
+
uv pip install -e ".[dev]"
|
|
271
|
+
pytest -v
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Smoke-test the live server with the MCP Inspector:
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
DANGEROUSLY_OMIT_AUTH=true npx @modelcontextprotocol/inspector -- python3 -m serial_mcp.server
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Set command to `python3` and args to `-m serial_mcp.server` in the inspector UI, then connect.
|
|
281
|
+
|
|
282
|
+
## Requirements
|
|
283
|
+
|
|
284
|
+
- Python >= 3.10
|
|
285
|
+
- [pyserial](https://pyserial.readthedocs.io/) >= 3.5
|
|
286
|
+
- [mcp](https://github.com/modelcontextprotocol/python-sdk) >= 1.0.0
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|