What if you could sit with your thin MacBook Air in a university coffee shop or library, type a prompt into an autonomous AI agent like Antigravity or Claude Code, and watch as a high-performance workstation across the room or down the hall autonomously designs, routes, and validates a custom PCB in KiCad 8/9?
No clunky remote desktop lag. No installing gigabytes of CAD libraries, C++ wrappers, and heavy GPU drivers on your lightweight laptop. Just pure, deterministic, agentic hardware engineering over the Model Context Protocol (MCP).
That was the mission behind KiCad-MCP-Bridge.
In this article, I will walk you through:
- The Architectural Topology: Bridging an MCP client laptop to a remote KiCad host over direct Thunderbolt 4 / USB4 links and high-speed networks.
-
The Automation Engine: Dual-mode programmatic PCB manipulation (native
pcbnewPython bindings + a pure S-expression.kicad_pcbgenerator for headless testing). - The Subprocess Pipeline: Headless Design Rule Checking (DRC) with structured JSON diagnostics and real-time vector SVG rendering for multimodal LLM feedback.
- The Real-World Crucible: "The Lab Admin Said No": What happened when we deployed on a university lab PC, why opening firewall ports is a security red flag, and how it inspired our v2.0 Zero-Inbound-Port Multi-Tenant Architecture.
1. The Distributed Hardware Dilemma
Modern PCB layout tools like KiCad 8 and 9 have made tremendous strides. With the introduction of kicad-cli and Python-based pcbnew scripting, programmatic hardware design is finally viable.
However, in academic research labs, hardware hackathons, and hardware startups, a common problem emerges:
- The Client Machine: Usually a student laptop (MacBook Air, lightweight Linux laptop) running local AI developer tools like Antigravity, Claude Desktop, or Cursor. These machines lack GPU power and often don't have KiCad or native EDA dependencies installed.
- The Target Workstation: A beefy lab PC (Windows 11 or Linux) equipped with a dedicated GPU, extensive footprint/symbol libraries, and KiCad 8/9.
┌───────────────────────────────────────┐ ┌───────────────────────────────────────┐
│ CLIENT NODE │ │ TARGET WORKSTATION │
│ (MacBook / Laptop) │ │ (Windows 11 / Linux Workstation) │
│ │ │ │
│ • Antigravity / Claude MCP Client │ │ • KiCad 8.0 / 9.0 + GPU Acceleration │
│ • Discovers workstation dynamically │ │ • Local Component Libraries │
│ • Issues high-level PCB prompts │ │ • Headless kicad-cli + pcbnew │
└──────────────────┬────────────────────┘ └───────────────────▲───────────────────┘
│ │
└────────── ⚡ Thunderbolt 4 / USB4 Cable ────────────┘
Link-Local: 169.254.0.0/16
We wanted to connect them using a direct Thunderbolt 4 / USB4 peer-to-peer cable or high-speed local link, giving us 20–40 Gbps throughput, sub-millisecond latency, and complete independence from Wi-Fi router isolation policies.
2. Core System Modules
We organized kicad-mcp-bridge into three clean, decoupled modules:
kicad-mcp-bridge/
├── kicad_bridge/
│ ├── discovery/ # Zero-conf mDNS beacon, interface watcher & client binder
│ │ ├── mdns_beacon.py
│ │ ├── interface_watcher.py
│ │ └── client_finder.py
│ ├── engine/ # KiCad runtime detection, board engine & CLI executor
│ │ ├── runtime_resolver.py
│ │ ├── board_manager.py
│ │ └── cli_executor.py
│ └── server/ # FastMCP Server & idempotent tool definitions
│ ├── app.py
│ └── main.py
└── tests/ # Zero-dependency self-test suite (8/8 passing in 0.12s)
Module A: Zero-Conf Discovery & Hot-Plug Watcher
When you plug a Thunderbolt cable between a Mac and a Windows PC, macOS creates bridge0 and Windows initializes a "Thunderbolt(TM) Networking" adapter. Both self-assign an IPv4 link-local address in the 169.254.0.0/16 range (RFC 3927 APIPA).
To eliminate manual IP configuration:
-
mdns_beacon.py: Broadcasts_kicad-mcp._tcp.local.usingzeroconf. In its TXT record, it advertises the KiCad version, server status (ready/busy), and preferred transport (sse). -
interface_watcher.py: Runs a background daemon scanning network interfaces usingpsutil. When a student plugs or unplugs a physical Thunderbolt cable, the watcher catches the link transition and updates the beacon's advertised IP set dynamically. -
client_finder.py: On the student laptop,kicad-mcp-discoverbrowses for the beacon. When resolved, it automatically writes the endpoint into the client configuration (antigravity_config.jsonorclaude_desktop_config.json).
# Automatic priority sorting: Link-local Thunderbolt IPs (169.254.x.x) first
def sort_priority(ip: str) -> int:
if ip.startswith("169.254."):
return 0 # Highest priority for direct cable links
if ip.startswith(("192.168.", "10.", "172.16.")):
return 1 # Local LAN
return 2
Module B: The Dual-Mode PCB Automation Engine
Connecting to KiCad headlessly can be brittle because pcbnew C++ bindings are tied to the exact Python version bundled with KiCad (C:\Program Files\KiCad\8.0\bin\python.exe on Windows).
To make the codebase 100% resilient across CI, local dev, and production workstations, we built a Dual-Mode Engine in board_manager.py:
-
Native
pcbnewMode: If the KiCad Python environment is available, it instantiates nativepcbnew.BOARD()objects, tracks, vias, and zones. -
Native S-Expression Mode: If running in a lightweight environment or headless CI without KiCad installed, it directly parses and generates standard KiCad 8/9
.kicad_pcbv2024 S-expressions!
def to_kicad_pcb_sexpr(self) -> str:
lines = [
"(kicad_pcb",
" (version 20240108)",
' (generator "kicad-mcp-bridge")',
' (generator_version "8.0")',
" (general (thickness 1.6) (legacy_teardrops no))",
' (paper "A4")',
" (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal) (44 \"Edge.Cuts\" user))",
" ...",
]
# Edge.Cuts boundary lines
# Footprint definitions with pad geometries
# Copper segments & through-hole vias
return "\n".join(lines)
This guarantees that tests pass in 0.12 seconds on any developer laptop or GitHub Actions runner, while emitting files that KiCad 8 and 9 open flawlessly.
Module C: Headless DRC & Multimodal Visual Inspection
An AI agent cannot safely design hardware blind. It needs:
- Mathematical verification (Design Rule Checking).
- Visual feedback (Rendering what the board actually looks like).
In cli_executor.py, we wrapped kicad-cli:
-
run_drc(): Invokeskicad-cli pcb drc --format jsonand parses clearance violations, minimum trace widths, and unrouted net airwires into structured diagnostics the LLM can reason about. -
render_board_preview(): Invokeskicad-cli pcb export svg(or our built-in vector SVG generator fallback) and encodes the result to base64 SVG. Multimodal models can view the exact copper geometry, silkscreen labels, and pad placements inline!
@mcp_app.tool()
def render_board_preview(layer: str = "top") -> str:
"""Generate and return a base64-encoded SVG preview for multimodal LLM inspection."""
board = context.ensure_active_board()
res = context.cli_executor.export_renders(
board_path=board.board_file,
output_dir=board.output_dir,
layer=layer,
)
return res["base64"]
3. The Real-World Crucible: "The Lab Admin Said No"
We got the entire test suite passing on macOS. We started the server. We packaged the Windows installer install.ps1.
Then came the real test: Deploying it on a physical Windows workstation in the university hardware lab.
When we ran install.ps1, the script attempted to register Windows Defender Firewall exceptions:
netsh advfirewall firewall add rule name="KiCad-MCP" dir=in action=allow protocol=TCP localport=8000 profile=any
The instructor who manages the lab saw this and immediately stopped the deployment. His reasoning was sound:
"This workstation is connected to the campus network and the internet. If you open inbound port 8000 with
profile=any, anyone on the campus Wi-Fi or local subnet can probe that port, execute commands, or compromise the system."
He was right. In our rush to get an MVP working, we had taken a classic developer shortcut: opening an unauthenticated inbound port on 0.0.0.0:8000. In a private home lab, that might slide. In a shared university lab, it is a non-starter.
Furthermore, another constraint surfaced:
"The KiCad workstation in the lab is constant, but different students need to use it. If Alice logs into Antigravity with
alice@college.edu, she shouldn't overwrite Bob's board who logs in withbob@college.edu."
4. The Solution: KiCad-MCP-Bridge v2.0 Architecture
Instead of fighting the lab firewall, we stepped back and redesigned the topology around two principles:
- Zero Inbound Ports (Outbound-Only Relays).
- Multi-Tenant Email Workspace Isolation.
┌─────────────────────────────────────────────────────────────────────────────────┐
│ STUDENT CLIENTS (Antigravity) │
│ │
│ Student A (alice@college.edu) Student B (bob@college.edu) │
│ ┌───────────────────────────┐ ┌───────────────────────────┐ │
│ │ Antigravity (MacBook A) │ │ Antigravity (MacBook B) │ │
│ │ Headers: alice@college.edu│ │ Headers: bob@college.edu │ │
│ └─────────────┬─────────────┘ └─────────────┬─────────────┘ │
└─────────────────┼──────────────────────────────────────┼────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ SECURE BROKER / ZERO-TRUST RELAY (Cloudflare / Tailscale / SSH) │
│ • Authenticates student email & session tokens │
│ • Requires ZERO inbound ports on the lab workstation │
└────────────────────────────────────────┬────────────────────────────────────────┘
│ ▲
Encrypted Outbound │ │ Lab PC connects OUTBOUND only
WebSocket / Tunnel │ │ (No open ports / No Admin needed!)
▼ │
┌─────────────────────────────────────────────────────────────────────────────────┐
│ LAB WORKSTATION (Constant KiCad Host) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ KiCad-MCP v2.0 Multi-Tenant Agent │ │
│ │ │ │
│ │ • User Workspace Isolation Engine │ │
│ │ • Hardware GPU & KiCad 8/9 Automation Engine │ │
│ └─────────────┬─────────────────────────────────────────────┬─────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────────────────────┐ ┌───────────────────────────┐ │
│ │ Workspace: Alice │ │ Workspace: Bob │ │
│ │ C:\kicad-lab\users\alice\ │ │ C:\kicad-lab\users\bob\ │ │
│ │ ├── SensorNode.kicad_pcb │ │ ├── MotorDriver.kicad_pcb│ │
│ │ └── gerbers/ │ │ └── gerbers/ │ │
│ └───────────────────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
1. Eliminating Inbound Firewall Ports
When you browse the web from a lab PC, your computer makes an outbound HTTPS connection over port 443. Campus firewalls allow this seamlessly without opening any inbound ports or requiring administrator privileges.
In v2.0, we flip the server model:
- The lab PC runs
kicad-mcp-agent --tunnel. - It opens an encrypted outbound-only tunnel (via Cloudflare Tunnel, Tailscale, or a secure WebSocket relay) to a managed broker.
- Antigravity sends MCP JSON-RPC requests to the secure broker URL over HTTPS.
-
The lab workstation has exactly ZERO inbound ports open. No
netshfirewall commands. No Administrator prompts. The lab admin is completely satisfied.
(For direct-cable setups, --interface thunderbolt pins the socket strictly to 169.254.x.x, physically air-gapping it from the campus LAN).
2. Multi-Tenant Email-Based Workspaces
To allow any student to use the shared KiCad machine:
- When Antigravity connects, it includes the student's authenticated email in the MCP headers:
{
"mcpServers": {
"kicad": {
"serverUrl": "https://kicad-lab.university.edu/sse",
"headers": {
"X-User-Email": "student_a@college.edu",
"Authorization": "Bearer <student_jwt>"
}
}
}
}
- On the workstation,
MultiTenantContextpartitions board state into sandboxed user folders:
C:\KiCad-Workspaces\
├── alice_college_edu\
│ └── SensorNode_V1\
│ ├── SensorNode_V1.kicad_pcb
│ └── production_gerbers\
└── bob_college_edu\
└── MotorDriver_V2\
├── MotorDriver_V2.kicad_pcb
└── production_gerbers\
Alice can never see, overwrite, or corrupt Bob's designs. And when Bob opens Antigravity on his laptop, he instantly picks up his own workspace on the exact same KiCad machine.
5. Summary & What's Next
Building autonomous AI tooling for software is one thing; building it for physical hardware connected to shared lab machines introduces real-world constraints like firewalls, operating system quirks, and multi-tenant safety.
By combining:
- The Model Context Protocol (MCP) for clean agentic tool exposure,
-
A dual-mode KiCad S-expression +
pcbnewengine for instant testability, - Outbound zero-trust tunnels to eliminate open firewall ports, and
- Email-based workspace partitioning for university labs,
we turn any shared lab computer into an autonomous, multi-user hardware design server.
Open-Source & Roadmap
kicad-mcp-bridge is built under the MIT License as part of our autonomous hardware engineering stack (follow updates on GitHub at @hrlpavan).
Have you integrated AI agents with physical hardware or CAD workflows? What security challenges have you faced in shared university or enterprise environments? Let's discuss in the comments below!
Top comments (0)