serial-console-mcp 0.1.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.
@@ -0,0 +1,20 @@
1
+ # Python
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.py[cod]
5
+ .venv/
6
+ venv/
7
+
8
+ # PyInstaller
9
+ build/
10
+ dist/
11
+ *.spec
12
+
13
+ # Installer build artifacts
14
+ Output/
15
+ pkgroot/
16
+ *.pkg
17
+ *-component.pkg
18
+
19
+ # OS
20
+ .DS_Store
@@ -0,0 +1,116 @@
1
+ # BUILD — making the two installers
2
+
3
+ Goal: a single file the user double-clicks. No Python, no terminal, no JSON.
4
+ Everything here is wired up; you mostly just run it.
5
+
6
+ ## Why not a `.mcpb`?
7
+
8
+ A `.mcpb` extension runs in Claude Desktop's sandbox — no LAN, no `/dev/cu.*`, no
9
+ COM ports. So it can't drive serial. The install therefore registers a **host
10
+ stdio subprocess** (the route that works for reaching real hardware), but hides
11
+ every manual step inside a clicker.
12
+
13
+ ## One binary, two jobs
14
+
15
+ The `serial_console_mcp` package (entry script `packaging/entry.py`) is frozen
16
+ into a single executable:
17
+ - launched with **no args** -> runs the MCP stdio server (what Claude calls);
18
+ - launched as **`serial-console-mcp configure --command <self>`** -> writes the
19
+ `claude_desktop_config.json` entry and exits (what the installer calls).
20
+
21
+ The configurator merges into existing servers (won't touch your other MCP
22
+ entries) and backs up the old file first.
23
+
24
+ ## The one non-obvious trap: elevated installer vs. per-user config
25
+
26
+ Installer postinstall steps run **elevated** (root on macOS, admin on Windows),
27
+ but Claude's config is per-user. Writing it from the elevated context lands in the
28
+ wrong home and silently does nothing. Both installers correct for this:
29
+ - **Windows:** the `[Run]` entry has `Flags: runasoriginaluser`. Inno does not allow
30
+ that flag in `[UninstallRun]`, so the uninstall-time `configure --remove` runs
31
+ elevated; it only reaches the right profile when the admin account is the user's
32
+ own (the usual single-user PC). Otherwise the stale entry is harmless: Claude
33
+ Desktop shows the server as unavailable until it is removed by hand.
34
+ - **macOS:** `scripts/postinstall` finds the console user and runs the configurator
35
+ as them via `launchctl asuser <uid> sudo -u <user> ... --config-home <home>`.
36
+
37
+ ## Windows -> SerialConsoleMCP-Setup.exe
38
+
39
+ ```
40
+ python -m venv .venv && .venv\Scripts\activate
41
+ pip install ".[freeze]"
42
+ pyinstaller --onefile --name serial-console-mcp --collect-all mcp --collect-all serial_console_mcp packaging/entry.py
43
+ ```
44
+ Then compile `installer.iss` with Inno Setup (free). Output:
45
+ `Output\SerialConsoleMCP-Setup.exe`. Edit `AppPublisher` (your name/callsign) and,
46
+ if you have a code-signing cert, add a `SignTool` directive to avoid SmartScreen
47
+ warnings (optional; unsigned works, just warns).
48
+
49
+ ## macOS -> SerialConsoleMCP-0.1.0.pkg
50
+
51
+ Unsigned (testing):
52
+ ```
53
+ ./build_macos.sh
54
+ ```
55
+ Signed + notarized (on your Mac, with your Developer ID):
56
+ ```
57
+ SIGN_IDENTITY_APP="Developer ID Application: Your Name (TEAMID)" \
58
+ SIGN_IDENTITY_INSTALLER="Developer ID Installer: Your Name (TEAMID)" \
59
+ NOTARY_PROFILE="AC" \
60
+ ./build_macos.sh
61
+ ```
62
+ `NOTARY_PROFILE` is a `notarytool store-credentials` keychain profile. The
63
+ package identifier is `org.stefanbrunner.serialconsolemcp` (override with
64
+ `IDENTIFIER=...`). Uninstall: `./uninstall_macos.sh`.
65
+
66
+ ## PyPI
67
+
68
+ `.github/workflows/release.yml` publishes the sdist and wheel to PyPI with
69
+ Trusted Publishing (OIDC, no API token) whenever a GitHub Release is published.
70
+ The tag must equal `v` + the version in `pyproject.toml`; the workflow checks.
71
+ Release steps:
72
+
73
+ ```
74
+ # bump version in pyproject.toml and src/serial_console_mcp/__init__.py, update CHANGELOG.md
75
+ git tag vX.Y.Z && git push origin vX.Y.Z # builds the installers
76
+ gh release create vX.Y.Z --title "vX.Y.Z" --notes-file <(sed -n '/^## \[X.Y.Z\]/,/^## \[/p' CHANGELOG.md)
77
+ ```
78
+
79
+ One-time setup on pypi.org: Manage project → Publishing → add a trusted publisher
80
+ for `sbrunner-atx/serial-console-mcp`, workflow `release.yml`, environment `release`.
81
+
82
+ ## CI (both at once)
83
+
84
+ `.github/workflows/build.yml` runs the pytest suite on Linux/macOS/Windows on
85
+ every push and pull request. On a `v*` tag (or manual dispatch) it additionally
86
+ freezes the binary and builds each installer, uploading them as artifacts. PyInstaller isn't a cross-compiler, so this is the easiest way to get
87
+ both from one `git tag`. The Mac job is **unsigned** in CI — for a notarized pkg,
88
+ run `build_macos.sh` locally with your Developer ID, since exporting certs into CI
89
+ is extra ceremony you don't need for a hobby tool.
90
+
91
+ ## Things to personalize if you fork this
92
+
93
+ - `installer.iss`: `AppPublisher`, `AppId` (generate a new GUID), optional signing.
94
+ - `build_macos.sh` / `uninstall_macos.sh`: `IDENTIFIER`.
95
+ - The server and configurator need no edits.
96
+
97
+ ## Testing against real hardware
98
+
99
+ `tests/` is the unit suite (fake serial port, runs in CI). `_hosttest/run_live.py`
100
+ is a developer harness that drives the real tool functions against a real port
101
+ from a JSON list of steps, without the MCP transport in the way. It ships a stub
102
+ `mcp` package so it also runs on a host Python older than 3.10; it sends only
103
+ what the step file lists.
104
+
105
+ ## The config entry produced
106
+
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "serial-console": {
111
+ "command": "/Library/Application Support/SerialConsoleMCP/serial-console-mcp",
112
+ "args": []
113
+ }
114
+ }
115
+ }
116
+ ```
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-09-14
11
+
12
+ First public, experimental release.
13
+
14
+ ### Added
15
+ - Eleven tools: `list_serial_ports`, `connect`, `reconnect_last`, `send_text`,
16
+ `send_hex`, `read_until_prompt`, `read_available`, `query_text`, `clear_buffer`,
17
+ `status`, `disconnect`.
18
+ - Interactive-console model: a background reader drains the port into a buffer
19
+ from the moment it opens; reads match a literal or regex prompt and push
20
+ leftover bytes back, or return once the line goes idle.
21
+ - Every Quick-Connect setting on `connect`: baud, data bits, parity, stop bits,
22
+ RTS/CTS and XON/XOFF. Default 9600 8N1, no flow control. Settings are
23
+ remembered for `reconnect_last`.
24
+ - Console script `serial-console-mcp`; `serial-console-mcp configure` writes the
25
+ Claude Desktop config entry (merging, with backup) and `--remove` undoes it.
26
+ - Windows (Inno Setup) and macOS (.pkg) installers built by CI on `v*` tags.
27
+ - The serial-console-mcp Field Guide (`docs/`).
28
+ - 37-case test suite on a simulated serial port; CI on Linux, macOS, Windows.
29
+
30
+ ### Notes
31
+ - The MCP SDK is pinned `<2`; the 2.x line removed `mcp.server.fastmcp`.
32
+ - A dead reader (unplugged adapter) is reported by every tool instead of a
33
+ silent timeout; the receive buffer is capped at 4 MB.
34
+ - Verified with the simulated port and a live MCP stdio handshake. A run against
35
+ physical hardware is the next milestone.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stefan Brunner (AE5VG)
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,200 @@
1
+ Metadata-Version: 2.5
2
+ Name: serial-console-mcp
3
+ Version: 0.1.0
4
+ Summary: An MCP server that lets Claude Desktop drive a serial console: network craft ports, CAT radios, Icom CI-V, rotators, microcontrollers.
5
+ Project-URL: Homepage, https://github.com/sbrunner-atx/serial-console-mcp
6
+ Project-URL: Repository, https://github.com/sbrunner-atx/serial-console-mcp
7
+ Project-URL: Issues, https://github.com/sbrunner-atx/serial-console-mcp/issues
8
+ Project-URL: Field Guide, https://github.com/sbrunner-atx/serial-console-mcp/blob/main/docs/serial-console-mcp%20Field%20Guide.pdf
9
+ Author-email: "Stefan Brunner (AE5VG)" <me@stefanbrunner.org>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cat,ci-v,console,craft-port,ham-radio,mcp,model-context-protocol,pyserial,serial,serial-port
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: Communications :: Ham Radio
21
+ Classifier: Topic :: System :: Hardware
22
+ Classifier: Topic :: Terminals :: Serial
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: mcp<2,>=1.2.0
25
+ Requires-Dist: pyserial>=3.5
26
+ Provides-Extra: freeze
27
+ Requires-Dist: mcp[cli]<2,>=1.2.0; extra == 'freeze'
28
+ Requires-Dist: pyinstaller>=6.0; extra == 'freeze'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # serial-console-mcp
32
+
33
+ **Let Claude drive your serial console.**
34
+
35
+ [![build](https://github.com/sbrunner-atx/serial-console-mcp/actions/workflows/build.yml/badge.svg)](https://github.com/sbrunner-atx/serial-console-mcp/actions/workflows/build.yml)
36
+ [![PyPI](https://img.shields.io/pypi/v/serial-console-mcp.svg)](https://pypi.org/project/serial-console-mcp/)
37
+ &nbsp;MIT licensed &nbsp;·&nbsp; Python 3.10+ &nbsp;·&nbsp; **status: experimental (0.1.0)**
38
+
39
+ This adds a few tools to **Claude Desktop** so you can talk to anything on a serial
40
+ port — a network device's console/craft port (Juniper, Cisco, etc.), a radio,
41
+ rotator, amplifier, antenna switch, or a microcontroller — just by *asking Claude*.
42
+
43
+ You do **not** need to know any programming. After it's installed you talk to Claude
44
+ normally:
45
+
46
+ > **You:** What serial ports do you see?
47
+ > **Claude:** I found two. One looks like a Silicon Labs CP210x on COM4 — that's
48
+ > probably your device.
49
+ >
50
+ > **You:** Connect to COM4 at 9600 baud.
51
+ > **Claude:** Connected.
52
+ >
53
+ > **You:** Log in and show me the version.
54
+
55
+ ## How it works (the important part)
56
+
57
+ A serial console isn't a simple question-and-answer channel. It echoes what you
58
+ type, prints unsolicited messages on its own (logs, interface flaps), and can dump
59
+ pages of output. So the moment a port is open, a background reader keeps draining
60
+ it into a buffer. That means Claude can:
61
+
62
+ - **send** a command (writes only — doesn't guess when the reply is done), then
63
+ - **read until a prompt** appears (`# `, `> `, `login:` …) to capture the whole
64
+ reply — even a long one — without cutting it off, or
65
+ - **read whatever's waiting** for streaming/unsolicited output.
66
+
67
+ This is the same model `minicom` and `expect` use, which is why it handles
68
+ interactive CLIs properly.
69
+
70
+ ### The tools
71
+
72
+ | Tool | What it does |
73
+ | --- | --- |
74
+ | `list_serial_ports` | Enumerate ports with description and USB hardware id |
75
+ | `connect` / `reconnect_last` / `disconnect` | Open a port. Defaults to 9600 8N1, no flow control; baud, data bits, parity, stop bits, RTS/CTS and XON/XOFF are all settable by asking. Remembers the last one |
76
+ | `send_text` | Write an ASCII command with CR / LF / CRLF / no line ending. Write-only |
77
+ | `send_hex` | Write raw bytes given as hex (Icom CI-V and other binary protocols) |
78
+ | `read_until_prompt` | Return buffered output up to a literal or regex prompt, leaving the rest |
79
+ | `read_available` | Return whatever has arrived, as text and hex |
80
+ | `query_text` | Clear, send, then read until a prompt or until the line goes idle |
81
+ | `clear_buffer` / `status` | Housekeeping |
82
+
83
+ One port is open at a time. The receive buffer is capped at 4 MB; if a device
84
+ streams for hours unread, the oldest bytes are dropped and `status` says how many.
85
+
86
+ ## The Field Guide
87
+
88
+ [serial-console-mcp Field Guide (PDF)](docs/serial-console-mcp%20Field%20Guide.pdf) is the
89
+ operator's manual: what each tool does, every connection setting said in plain
90
+ language, the console rules, a per-device playbook (craft ports, text CAT,
91
+ Icom CI-V, rotators and microcontrollers), four worked sessions, and a
92
+ troubleshooting table. Source is `docs/brand/` (HTML + CSS, rendered with
93
+ WeasyPrint).
94
+
95
+ ## Installing
96
+
97
+ 1. Download the installer for your computer from the
98
+ [Releases page](https://github.com/sbrunner-atx/serial-console-mcp/releases)
99
+ (the `.exe` on Windows, or the `.pkg` on a Mac) and click through it like any
100
+ normal program. It sets everything up for you. The installers are unsigned for
101
+ now, so expect a Gatekeeper / SmartScreen warning.
102
+ 2. **Completely quit Claude Desktop** — not just closing the window. On Windows,
103
+ right-click the Claude icon near the clock and choose Quit. On a Mac, press
104
+ ⌘Q or choose **Claude → Quit**.
105
+ 3. Open Claude Desktop again.
106
+ 4. In a new chat, type: **"What serial ports do you see?"** If Claude lists your
107
+ ports, you're done.
108
+
109
+ That's the whole thing. There's no separate program to keep open and nothing to
110
+ configure by hand.
111
+
112
+ ## Installing from PyPI
113
+
114
+ If you already have Python 3.10+ and [uv](https://docs.astral.sh/uv/) or pipx,
115
+ you don't need the installer:
116
+
117
+ ```bash
118
+ uvx serial-console-mcp --version # fetches and runs it
119
+ uvx serial-console-mcp configure --command uvx --arg serial-console-mcp
120
+ ```
121
+
122
+ or
123
+
124
+ ```bash
125
+ pipx install serial-console-mcp
126
+ serial-console-mcp configure --command "$(which serial-console-mcp)"
127
+ ```
128
+
129
+ `configure` writes a `serial-console` entry into `claude_desktop_config.json`
130
+ (merging with whatever is already there and backing the old file up first).
131
+ Quit and reopen Claude Desktop. `serial-console-mcp configure --remove` undoes
132
+ it. Any other MCP client can launch the same command over stdio.
133
+
134
+ ## Installing from source (developers)
135
+
136
+ ```bash
137
+ git clone https://github.com/sbrunner-atx/serial-console-mcp.git
138
+ cd serial-console-mcp
139
+ uv sync # or: python3 -m venv .venv && . .venv/bin/activate && pip install -e . pytest
140
+ uv run pytest # fake serial port, no hardware needed
141
+ uv run serial-console-mcp configure --command "$PWD/.venv/bin/serial-console-mcp"
142
+ ```
143
+
144
+ The package lives in `src/serial_console_mcp/`: `server.py` is the MCP server,
145
+ `configure.py` the Claude Desktop registrar. See [BUILD.md](BUILD.md) for the
146
+ installers.
147
+
148
+ ## Using it
149
+
150
+ Plain-English requests work. Some examples:
151
+
152
+ - "List my serial ports."
153
+ - "Connect to the console on /dev/cu.usbserial-10 at 9600 baud."
154
+ - "Connect to /dev/cu.BLTH at 38400, 8 data bits, no parity, 1 stop bit, XON/XOFF flow control."
155
+ - "Reconnect to the same port as last time." (it remembers)
156
+ - "Send a return, then read until the login prompt."
157
+ - "Log in as admin and run `show interfaces terse`, then show me all of it."
158
+ - "Just read whatever the device is printing right now."
159
+ - "Disconnect when you're done."
160
+
161
+ For a router/switch console, tell Claude the prompt it should wait for (often `# `
162
+ for enable mode or `> ` for user mode) and it will read until it sees it. For an
163
+ Icom radio (CI-V), tell Claude — it can send the hex commands those radios expect.
164
+
165
+ ## If something doesn't work
166
+
167
+ **"No serial ports found."**
168
+ - Is the device turned on?
169
+ - Is the USB cable a real *data* cable, not a charge-only one? (A very common gotcha.)
170
+ - On Windows, open Device Manager and look under **Ports (COM & LPT)**. If nothing's
171
+ there, Windows needs the cable's driver (often FTDI, CP210x, or CH340).
172
+
173
+ **"Could not open the port" / "access denied."**
174
+ - A serial port can only be used by one program at a time. Close anything else that
175
+ might be holding it: a terminal (PuTTY/minicom/screen), WSJT-X, your contest
176
+ logger, the device's own software.
177
+
178
+ **Claude says it can't access serial ports at all.**
179
+ - Make sure you fully quit and reopened Claude Desktop after installing.
180
+ - Start a brand-new chat and ask "What serial ports do you see?" again.
181
+
182
+ **It connected but a command gets no reply.**
183
+ - Almost always the **baud rate** is wrong, or the **line ending** is wrong for your
184
+ gear. Most Unix-style consoles want a plain newline (LF); most rigs want a carriage
185
+ return (CR). Ask Claude to send a return first to draw a fresh prompt.
186
+
187
+ ## A word on safety
188
+
189
+ These tools send exactly what you (through Claude) ask them to send, to whatever
190
+ device is on the cable. Claude Desktop asks you to approve each tool call, so you
191
+ see every command before it runs. Read it. A console session on a router or a
192
+ rig can reconfigure, reboot, or transmit, and this server does not try to guess
193
+ which commands are dangerous. If something looks wrong, decline it, and keep a
194
+ real terminal handy for anything you would not want an assistant to type.
195
+
196
+ ## License
197
+
198
+ MIT. See [LICENSE](LICENSE).
199
+
200
+ 73!
@@ -0,0 +1,170 @@
1
+ # serial-console-mcp
2
+
3
+ **Let Claude drive your serial console.**
4
+
5
+ [![build](https://github.com/sbrunner-atx/serial-console-mcp/actions/workflows/build.yml/badge.svg)](https://github.com/sbrunner-atx/serial-console-mcp/actions/workflows/build.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/serial-console-mcp.svg)](https://pypi.org/project/serial-console-mcp/)
7
+ &nbsp;MIT licensed &nbsp;·&nbsp; Python 3.10+ &nbsp;·&nbsp; **status: experimental (0.1.0)**
8
+
9
+ This adds a few tools to **Claude Desktop** so you can talk to anything on a serial
10
+ port — a network device's console/craft port (Juniper, Cisco, etc.), a radio,
11
+ rotator, amplifier, antenna switch, or a microcontroller — just by *asking Claude*.
12
+
13
+ You do **not** need to know any programming. After it's installed you talk to Claude
14
+ normally:
15
+
16
+ > **You:** What serial ports do you see?
17
+ > **Claude:** I found two. One looks like a Silicon Labs CP210x on COM4 — that's
18
+ > probably your device.
19
+ >
20
+ > **You:** Connect to COM4 at 9600 baud.
21
+ > **Claude:** Connected.
22
+ >
23
+ > **You:** Log in and show me the version.
24
+
25
+ ## How it works (the important part)
26
+
27
+ A serial console isn't a simple question-and-answer channel. It echoes what you
28
+ type, prints unsolicited messages on its own (logs, interface flaps), and can dump
29
+ pages of output. So the moment a port is open, a background reader keeps draining
30
+ it into a buffer. That means Claude can:
31
+
32
+ - **send** a command (writes only — doesn't guess when the reply is done), then
33
+ - **read until a prompt** appears (`# `, `> `, `login:` …) to capture the whole
34
+ reply — even a long one — without cutting it off, or
35
+ - **read whatever's waiting** for streaming/unsolicited output.
36
+
37
+ This is the same model `minicom` and `expect` use, which is why it handles
38
+ interactive CLIs properly.
39
+
40
+ ### The tools
41
+
42
+ | Tool | What it does |
43
+ | --- | --- |
44
+ | `list_serial_ports` | Enumerate ports with description and USB hardware id |
45
+ | `connect` / `reconnect_last` / `disconnect` | Open a port. Defaults to 9600 8N1, no flow control; baud, data bits, parity, stop bits, RTS/CTS and XON/XOFF are all settable by asking. Remembers the last one |
46
+ | `send_text` | Write an ASCII command with CR / LF / CRLF / no line ending. Write-only |
47
+ | `send_hex` | Write raw bytes given as hex (Icom CI-V and other binary protocols) |
48
+ | `read_until_prompt` | Return buffered output up to a literal or regex prompt, leaving the rest |
49
+ | `read_available` | Return whatever has arrived, as text and hex |
50
+ | `query_text` | Clear, send, then read until a prompt or until the line goes idle |
51
+ | `clear_buffer` / `status` | Housekeeping |
52
+
53
+ One port is open at a time. The receive buffer is capped at 4 MB; if a device
54
+ streams for hours unread, the oldest bytes are dropped and `status` says how many.
55
+
56
+ ## The Field Guide
57
+
58
+ [serial-console-mcp Field Guide (PDF)](docs/serial-console-mcp%20Field%20Guide.pdf) is the
59
+ operator's manual: what each tool does, every connection setting said in plain
60
+ language, the console rules, a per-device playbook (craft ports, text CAT,
61
+ Icom CI-V, rotators and microcontrollers), four worked sessions, and a
62
+ troubleshooting table. Source is `docs/brand/` (HTML + CSS, rendered with
63
+ WeasyPrint).
64
+
65
+ ## Installing
66
+
67
+ 1. Download the installer for your computer from the
68
+ [Releases page](https://github.com/sbrunner-atx/serial-console-mcp/releases)
69
+ (the `.exe` on Windows, or the `.pkg` on a Mac) and click through it like any
70
+ normal program. It sets everything up for you. The installers are unsigned for
71
+ now, so expect a Gatekeeper / SmartScreen warning.
72
+ 2. **Completely quit Claude Desktop** — not just closing the window. On Windows,
73
+ right-click the Claude icon near the clock and choose Quit. On a Mac, press
74
+ ⌘Q or choose **Claude → Quit**.
75
+ 3. Open Claude Desktop again.
76
+ 4. In a new chat, type: **"What serial ports do you see?"** If Claude lists your
77
+ ports, you're done.
78
+
79
+ That's the whole thing. There's no separate program to keep open and nothing to
80
+ configure by hand.
81
+
82
+ ## Installing from PyPI
83
+
84
+ If you already have Python 3.10+ and [uv](https://docs.astral.sh/uv/) or pipx,
85
+ you don't need the installer:
86
+
87
+ ```bash
88
+ uvx serial-console-mcp --version # fetches and runs it
89
+ uvx serial-console-mcp configure --command uvx --arg serial-console-mcp
90
+ ```
91
+
92
+ or
93
+
94
+ ```bash
95
+ pipx install serial-console-mcp
96
+ serial-console-mcp configure --command "$(which serial-console-mcp)"
97
+ ```
98
+
99
+ `configure` writes a `serial-console` entry into `claude_desktop_config.json`
100
+ (merging with whatever is already there and backing the old file up first).
101
+ Quit and reopen Claude Desktop. `serial-console-mcp configure --remove` undoes
102
+ it. Any other MCP client can launch the same command over stdio.
103
+
104
+ ## Installing from source (developers)
105
+
106
+ ```bash
107
+ git clone https://github.com/sbrunner-atx/serial-console-mcp.git
108
+ cd serial-console-mcp
109
+ uv sync # or: python3 -m venv .venv && . .venv/bin/activate && pip install -e . pytest
110
+ uv run pytest # fake serial port, no hardware needed
111
+ uv run serial-console-mcp configure --command "$PWD/.venv/bin/serial-console-mcp"
112
+ ```
113
+
114
+ The package lives in `src/serial_console_mcp/`: `server.py` is the MCP server,
115
+ `configure.py` the Claude Desktop registrar. See [BUILD.md](BUILD.md) for the
116
+ installers.
117
+
118
+ ## Using it
119
+
120
+ Plain-English requests work. Some examples:
121
+
122
+ - "List my serial ports."
123
+ - "Connect to the console on /dev/cu.usbserial-10 at 9600 baud."
124
+ - "Connect to /dev/cu.BLTH at 38400, 8 data bits, no parity, 1 stop bit, XON/XOFF flow control."
125
+ - "Reconnect to the same port as last time." (it remembers)
126
+ - "Send a return, then read until the login prompt."
127
+ - "Log in as admin and run `show interfaces terse`, then show me all of it."
128
+ - "Just read whatever the device is printing right now."
129
+ - "Disconnect when you're done."
130
+
131
+ For a router/switch console, tell Claude the prompt it should wait for (often `# `
132
+ for enable mode or `> ` for user mode) and it will read until it sees it. For an
133
+ Icom radio (CI-V), tell Claude — it can send the hex commands those radios expect.
134
+
135
+ ## If something doesn't work
136
+
137
+ **"No serial ports found."**
138
+ - Is the device turned on?
139
+ - Is the USB cable a real *data* cable, not a charge-only one? (A very common gotcha.)
140
+ - On Windows, open Device Manager and look under **Ports (COM & LPT)**. If nothing's
141
+ there, Windows needs the cable's driver (often FTDI, CP210x, or CH340).
142
+
143
+ **"Could not open the port" / "access denied."**
144
+ - A serial port can only be used by one program at a time. Close anything else that
145
+ might be holding it: a terminal (PuTTY/minicom/screen), WSJT-X, your contest
146
+ logger, the device's own software.
147
+
148
+ **Claude says it can't access serial ports at all.**
149
+ - Make sure you fully quit and reopened Claude Desktop after installing.
150
+ - Start a brand-new chat and ask "What serial ports do you see?" again.
151
+
152
+ **It connected but a command gets no reply.**
153
+ - Almost always the **baud rate** is wrong, or the **line ending** is wrong for your
154
+ gear. Most Unix-style consoles want a plain newline (LF); most rigs want a carriage
155
+ return (CR). Ask Claude to send a return first to draw a fresh prompt.
156
+
157
+ ## A word on safety
158
+
159
+ These tools send exactly what you (through Claude) ask them to send, to whatever
160
+ device is on the cable. Claude Desktop asks you to approve each tool call, so you
161
+ see every command before it runs. Read it. A console session on a router or a
162
+ rig can reconfigure, reboot, or transmit, and this server does not try to guess
163
+ which commands are dangerous. If something looks wrong, decline it, and keep a
164
+ real terminal handy for anything you would not want an assistant to type.
165
+
166
+ ## License
167
+
168
+ MIT. See [LICENSE](LICENSE).
169
+
170
+ 73!
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+ # Build a (optionally signed + notarized) macOS .pkg.
3
+ #
4
+ # Unsigned local/CI build: ./build_macos.sh
5
+ # Signed + notarized build (on your Mac with your Developer ID):
6
+ # SIGN_IDENTITY_APP="Developer ID Application: Your Name (TEAMID)" \
7
+ # SIGN_IDENTITY_INSTALLER="Developer ID Installer: Your Name (TEAMID)" \
8
+ # NOTARY_PROFILE="AC" ./build_macos.sh
9
+ set -euo pipefail
10
+
11
+ APP_NAME="serial-console-mcp"
12
+ VERSION="${VERSION:-0.1.0}"
13
+ IDENTIFIER="${IDENTIFIER:-org.stefanbrunner.serialconsolemcp}"
14
+ INSTALL_LOCATION="/Library/Application Support/SerialConsoleMCP"
15
+
16
+ python3 -m venv .venv
17
+ # shellcheck disable=SC1091
18
+ source .venv/bin/activate
19
+ pip install --quiet ".[freeze]"
20
+ pyinstaller --onefile --name "$APP_NAME" --collect-all mcp --collect-all serial_console_mcp packaging/entry.py
21
+
22
+ # Sign the Mach-O with hardened runtime (required for notarization).
23
+ if [ -n "${SIGN_IDENTITY_APP:-}" ]; then
24
+ codesign --force --options runtime --timestamp \
25
+ --sign "$SIGN_IDENTITY_APP" "dist/$APP_NAME"
26
+ else
27
+ echo "WARN: SIGN_IDENTITY_APP not set -> unsigned binary (fine for testing)."
28
+ fi
29
+
30
+ rm -rf pkgroot
31
+ mkdir -p "pkgroot${INSTALL_LOCATION}"
32
+ cp "dist/$APP_NAME" "pkgroot${INSTALL_LOCATION}/"
33
+ chmod +x "pkgroot${INSTALL_LOCATION}/$APP_NAME"
34
+ chmod +x scripts/postinstall
35
+
36
+ pkgbuild --root pkgroot \
37
+ --identifier "$IDENTIFIER" \
38
+ --version "$VERSION" \
39
+ --scripts scripts \
40
+ --install-location "/" \
41
+ "SerialConsoleMCP-component.pkg"
42
+
43
+ productbuild --package "SerialConsoleMCP-component.pkg" "SerialConsoleMCP-$VERSION.pkg"
44
+ rm -f "SerialConsoleMCP-component.pkg" # intermediate; keep only the product pkg
45
+
46
+ if [ -n "${SIGN_IDENTITY_INSTALLER:-}" ]; then
47
+ productsign --sign "$SIGN_IDENTITY_INSTALLER" \
48
+ "SerialConsoleMCP-$VERSION.pkg" "SerialConsoleMCP-$VERSION-signed.pkg"
49
+ mv "SerialConsoleMCP-$VERSION-signed.pkg" "SerialConsoleMCP-$VERSION.pkg"
50
+ if [ -n "${NOTARY_PROFILE:-}" ]; then
51
+ xcrun notarytool submit "SerialConsoleMCP-$VERSION.pkg" \
52
+ --keychain-profile "$NOTARY_PROFILE" --wait
53
+ xcrun stapler staple "SerialConsoleMCP-$VERSION.pkg"
54
+ fi
55
+ else
56
+ echo "WARN: SIGN_IDENTITY_INSTALLER not set -> unsigned .pkg (Gatekeeper will warn)."
57
+ fi
58
+
59
+ echo "Built SerialConsoleMCP-$VERSION.pkg"
@@ -0,0 +1,48 @@
1
+ ; Inno Setup script for Serial Console MCP (Windows)
2
+ ; Build the binary first: pip install ".[freeze]" && pyinstaller --onefile --name serial-console-mcp --collect-all mcp --collect-all serial_console_mcp packaging/entry.py
3
+ ; Then compile this with Inno Setup -> Output\SerialConsoleMCP-Setup.exe
4
+
5
+ #define MyAppName "Serial Console MCP"
6
+ #define MyAppVersion "0.1.0"
7
+ #define MyAppPublisher "Stefan Brunner (AE5VG)"
8
+
9
+ [Setup]
10
+ AppId={{B3F1A2C4-7E5D-4A19-9C2B-1D6E8F0A4C77}
11
+ AppName={#MyAppName}
12
+ AppVersion={#MyAppVersion}
13
+ AppPublisher={#MyAppPublisher}
14
+ DefaultDirName={autopf}\SerialConsoleMCP
15
+ DisableProgramGroupPage=yes
16
+ DisableDirPage=yes
17
+ OutputDir=Output
18
+ OutputBaseFilename=SerialConsoleMCP-Setup
19
+ Compression=lzma2
20
+ SolidCompression=yes
21
+ PrivilegesRequired=admin
22
+ ArchitecturesInstallIn64BitMode=x64compatible
23
+ WizardStyle=modern
24
+
25
+ [Files]
26
+ Source: "dist\serial-console-mcp.exe"; DestDir: "{app}"; Flags: ignoreversion
27
+
28
+ [Run]
29
+ ; Register with Claude Desktop AS THE REAL USER (runasoriginaluser), so %APPDATA%
30
+ ; resolves to the logged-in user's profile rather than the elevated admin's.
31
+ Filename: "{app}\serial-console-mcp.exe"; \
32
+ Parameters: "configure --command ""{app}\serial-console-mcp.exe"""; \
33
+ Flags: runhidden runasoriginaluser; \
34
+ StatusMsg: "Registering with Claude Desktop..."
35
+
36
+ [UninstallRun]
37
+ ; Runs before files are removed, so the exe still exists here. Inno does not
38
+ ; allow runasoriginaluser in this section, so this runs in the uninstaller's
39
+ ; (elevated) context: it cleans the entry when the admin and the user are the
40
+ ; same account, which is the common single-user case. Otherwise Claude Desktop
41
+ ; simply shows the server as unavailable until the entry is removed by hand.
42
+ Filename: "{app}\serial-console-mcp.exe"; \
43
+ Parameters: "configure --remove"; \
44
+ Flags: runhidden; \
45
+ RunOnceId: "RemoveClaudeEntry"
46
+
47
+ [Messages]
48
+ FinishedLabel=Setup is done. Now FULLY QUIT Claude Desktop (right-click the tray icon, Quit) and reopen it. Then ask Claude: "What serial ports do you see?"