Content
# hw-sw-bridge
Two Claude Code agents on two laptops exchanging messages over an encrypted
Tailscale tunnel, so a hardware-side agent (Windows + Vivado + FPGA board)
can coordinate with a software-side agent (macOS + Xcode + iOS app) on a
cross-platform embedded project.
## Architecture
```
┌─────────────── Windows laptop ───────────────┐ ┌─────────────── MacBook ─────────────────┐
│ │ │ │
│ Claude Code ──stdio──► MCP server ─┐ │ │ ┌─ MCP server ◄──stdio── Claude Code │
│ ▼ │ │ ▼ │
│ WebSocket :8765 │◄────►│:8765 WebSocket │
│ │ │ │
└──────────────────────▲───────────────────────┘ └──────────────────────▲──────────────────┘
│ │
└──────── Tailscale WireGuard tunnel ─────────────────┘
(encrypted, any network, auto-discovery)
```
## MCP tools
| Tool | Description |
|---|---|
| `send_message(text)` | Send a text message to the peer |
| `wait_for_message(timeout)` | Block until a message arrives |
## Setup
1. Install Tailscale on both laptops, sign in with same account
2. `tailscale status` shows both peers with `100.x.y.z` IPs
3. Clone this repo to both laptops
4. On each laptop: `pip install -e .`
5. Register the MCP server on each laptop:
**Windows laptop** (peer = Mac's Tailscale IP):
```
claude mcp add --scope user hw-sw-bridge \
--env HW_SW_PEER=100.114.22.33 \
-- python -m hw_sw_bridge.server
```
**Mac laptop** (peer = Windows laptop's Tailscale IP):
```
claude mcp add --scope user hw-sw-bridge \
--env HW_SW_PEER=100.77.62.85 \
-- python -m hw_sw_bridge.server
```
6. Verify in Claude Code on each laptop: `/mcp` should list `hw-sw-bridge` as connected.
7. Install the coordination skill so each side's Claude knows **when** to use the tools:
**Windows:**
```
copy skills\hw-sw-bridge\SKILL.md %USERPROFILE%\.claude\skills\hw-sw-bridge\SKILL.md
```
(create the dir first if needed)
**macOS:**
```
mkdir -p ~/.claude/skills/hw-sw-bridge
cp skills/hw-sw-bridge/SKILL.md ~/.claude/skills/hw-sw-bridge/SKILL.md
```
Restart Claude Code afterward so the skill loads.
## Quick smoke test (no Claude)
On Laptop B (receiver):
```
HW_SW_PEER=<A-tailscale-ip> python -c "import asyncio, hw_sw_bridge.peer as p; q=asyncio.Queue(); asyncio.run(p.serve_inbox(q, 8765))"
```
On Laptop A (sender), in another terminal:
```
python -c "import asyncio; from hw_sw_bridge.peer import send; print(asyncio.run(send('<B-tailscale-ip>', 8765, 'hello')))"
```
Laptop B terminal should show the message appear on its queue (add a print to serve_inbox to see it).
## Design decisions
- **Tailscale over raw WebSocket + TLS** — WireGuard handles encryption, auth, NAT traversal in one install. No certs to manage.
- **Peer-to-peer over central broker** — no third machine needed, no single point of failure.
- **Python + `mcp` SDK** — smallest stack that runs cross-platform (Windows + macOS).