@muzwalls/mcp-bridge
v0.1.0
Published
Stdio MCP bridge for the MUZWalls SketchUp plugin. Connects Claude Desktop / Cursor / Codex CLI to a running MUZWalls Ruby server inside SketchUp.
Maintainers
Readme
@muzwalls/mcp-bridge
Stdio MCP bridge for the MUZWalls SketchUp plugin. Connects Claude Desktop, Cursor, Codex CLI, and other MCP-aware clients to a running MUZWalls Ruby server inside SketchUp.
What it is
The Ruby plugin inside SketchUp can't be a stdio subprocess — it lives inside the SketchUp process. So it exposes its MCP surface as a line-delimited JSON-RPC server on 127.0.0.1:5723. This bridge is the small (~250-line) Node program that:
- Reads MCP requests from stdin (newline-delimited JSON-RPC 2.0).
- Forwards each request to the Ruby plugin over TCP.
- Writes the response back to stdout.
- Handles MCP's
initializehandshake locally so the Ruby side doesn't have to know about the wire protocol.
Why two processes: SketchUp Ruby is single-threaded on the UI thread; trying to host a stdio protocol from inside it would either block the UI or fight the GIL. A separate Node process per MCP client is cheap, easy to restart, and isolates protocol concerns from plugin code.
Install
npm install -g @muzwalls/mcp-bridgeRequires Node 18 or newer.
Usage
Start MUZWalls in SketchUp. From the Ruby Console:
MUZ::Walls.start_mcp_serverThat opens 127.0.0.1:5723. Then point your MCP client at the bridge.
Claude Desktop
Edit claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"muzwalls": {
"command": "muzwalls-mcp-bridge",
"args": ["--port", "5723"]
}
}
}Restart Claude Desktop. You should see muzwalls in the MCP server list with muz_list_walls available.
Cursor
Edit ~/.cursor/mcp.json (macOS / Linux) or %USERPROFILE%\.cursor\mcp.json (Windows):
{
"mcpServers": {
"muzwalls": {
"command": "muzwalls-mcp-bridge",
"args": ["--port", "5723"]
}
}
}Codex CLI
codex --mcp-server muzwalls "list the walls in my current SketchUp model"(With muzwalls defined in your Codex MCP config the same way as above.)
CLI flags
| Flag | Default | Purpose |
|---|---|---|
| --host <host> | 127.0.0.1 | TCP host — leave as localhost for security |
| --port <port> | 5723 | Match McpServer::DEFAULT_PORT in the plugin |
| --connect-timeout <ms> | 3000 | How long to wait for the initial Ruby connect |
| --help | | Print usage and exit |
How it routes
| MCP request | Handled by |
|---|---|
| initialize | Bridge (answers locally with protocolVersion / serverInfo / capabilities) |
| notifications/initialized | Bridge (swallowed — no-op) |
| tools/list | Forwarded to Ruby |
| tools/call | Forwarded to Ruby |
| Any other method | Forwarded to Ruby (Ruby returns -32601 method not found if unknown) |
Request IDs are passed through unchanged. The bridge maintains one TCP connection per process, so it's a clean per-client isolation: each MCP client (Claude, Cursor, etc.) spawns its own bridge instance.
Smoke test without an MCP client
Once MUZ::Walls.start_mcp_server is running, you can talk to the bridge directly:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize"}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| muzwalls-mcp-bridge --port 5723You'll see two JSON responses on stdout — the initialize result and the tools/list result — and the bridge will exit when stdin closes.
Development
cd mcp-bridge
node --test test/Tests use a tiny in-process fake of the Ruby server (test/fake-ruby-server.js) and inject PassThrough streams for stdin/stdout, so no real SketchUp needed.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean shutdown (stdin closed, no errors) |
| 1 | Could not reach Ruby server, or connection dropped mid-session |
| 2 | Bad command-line flags |
Security
- Binds localhost only (
127.0.0.1). The bridge cannot reach a remote Ruby server unless you override--host, and even then the Ruby server itself only binds localhost. - No tokens, no shared secrets. Trust is "you're on the same machine, you can read the same files."
- License gating happens on the Ruby side, not in the bridge — see
docs/MCP_DESIGN.mdin the plugin repo for the license model (reads free, writes require an active MUZWalls license).
