claude-mac-bridge
Provides secure SSH connectivity between the server and the remote Mac over the Tailscale network.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@claude-mac-bridgeBuild the auth module"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
claude-mac-bridge
MCP server that lets an AI agent delegate tasks to Claude Code on a remote Mac via SSH over Tailscale. Persistent sessions, 200k context, full Mac filesystem access.
Agent (server) → ask_claude tool → SSH → claude -p (Mac) → JSON response + session_idWhy
Your agent runs on a Linux server. Claude Code runs on a Mac with Homebrew, Docker, Xcode, and 200k context. Sometimes you need both.
This bridge:
Gives your agent Mac access: file system, tools, local services
Persists sessions: pass
session_idback to continue conversations across hoursZero config on the Mac: Claude Code runs headless via
-p, no GUI neededFull audit trail: every delegation logged to
bridge.logwith cost + duration
Related MCP server: mcp-home-server
Quick Start
# 1. Install
pip install mcp
# 2. Set up SSH key
ssh-keygen -t ed25519 -f ~/.ssh/mac_bridge
ssh-copy-id -i ~/.ssh/mac_bridge.pub user@<mac-tailscale-ip>
# 3. Set env vars
export CLAUDE_BRIDGE_SSH_HOST="user@100.x.x.x"
export CLAUDE_BRIDGE_CLAUDE_BIN="/opt/homebrew/bin/claude"
# 4. Run the server
python3 server.py
# 5. Wire into your MCP config (see Setup below)Requirements
Claude Code on the Mac (
npm install -g @anthropic-ai/claude-codeor Homebrew)GNU
timeoutfor remote process cleanup, install viabrew install coreutilson macOS (providesgtimeout). Falls back gracefully if unavailable, but remote Claude processes may outlive timed-out SSH connections without it.Tailscale on both machines
Passwordless SSH (key-based auth) from server → Mac
Python 3.10+ on the server
mcppackage (pip install mcp)
Setup
1. SSH key auth
# On server: generate a dedicated key
ssh-keygen -t ed25519 -f ~/.ssh/mac_bridge
# Copy to Mac
ssh-copy-id -i ~/.ssh/mac_bridge.pub user@<mac-tailscale-ip>
# Verify
ssh -i ~/.ssh/mac_bridge user@<mac-tailscale-ip> "echo ok"2. Configure
Env var | Required | Default | Description |
| ✅ | , | SSH target: |
| ❌ |
| Path to Claude binary on the Mac |
| ❌ |
| Max seconds per delegation (wall-clock, never pauses) |
| ❌ |
| Path for the delegation audit log |
| ❌ |
| Size-rotate the log (one |
| ❌ |
| Run Claude with |
3. Wire into MCP config
mcp_servers:
claude_bridge:
command: "python3"
args: ["/path/to/server.py"]
env:
CLAUDE_BRIDGE_SSH_HOST: "user@100.x.x.x"
CLAUDE_BRIDGE_CLAUDE_BIN: "/opt/homebrew/bin/claude"
timeout: 620 # must exceed CLAUDE_BRIDGE_TIMEOUTAPI
ask_claude(task, context?, resume_session_id?)
Parameter | Required | Description |
| ✅ | The task or question. Be specific. |
| ❌ | Extra context: code, error messages, background. |
| ❌ | From a previous call, continues that conversation. |
Returns: Claude's response with session_id, cost, and duration appended.
# Basic
result = ask_claude(task="Write a Python script that reads a CSV and...")
# With context
result = ask_claude(
task="Find the bug",
context="def foo(): ...\nError: TypeError..."
)
# Persistent session
r1 = ask_claude(task="Review this PR")
r2 = ask_claude(
task="Fix the first issue you found",
resume_session_id="<session_id from r1>"
)Session Chaining
Every response includes a session_id. Pass it back as resume_session_id to continue the same conversation, Claude remembers all prior context, files read, and decisions made.
Rule: always chain session_ids for related tasks. Without resume_session_id, each call is a cold start.
r1 = ask_claude(task="Build the auth module")
# r1 contains: session_id = "abc123"
r2 = ask_claude(task="Add rate limiting to the auth module", resume_session_id="abc123")
r3 = ask_claude(task="Write tests for all of it", resume_session_id="abc123")Long Tasks: tmux Blocking Loop
The MCP tool has a hard wall-clock timeout (default 600s). For tasks that may run longer, project scaffolding, npm installs, multi-file builds, use this tmux pattern instead. It blocks until Claude finishes, auto-handles confirmation prompts, and returns the full result + session_id.
#!/usr/bin/env bash
MAC="user@<mac-tailscale-ip>"
TMUX="/opt/homebrew/bin/tmux"
CLAUDE="/opt/homebrew/bin/claude"
SESSION="agent-work"
MAX_WAIT=3600 # 60 min ceiling
POLL=45
# 1. write task to file (avoids SSH quoting hell)
scp /tmp/task.md "$MAC:~/task.md"
# 2. start fresh tmux session
ssh "$MAC" "$TMUX kill-session -t $SESSION 2>/dev/null; \
$TMUX new-session -d -s $SESSION -x 220 -y 50"
# 3. launch claude
ssh "$MAC" "$TMUX send-keys -t $SESSION \
'$CLAUDE -p "Read ~/task.md and execute every step." \
--output-format json --dangerously-skip-permissions 2>&1 | tee ~/task_output.json' Enter"
# 4. blocking poll -- auto-answer confirmations, exit on shell prompt
elapsed=0
while [ $elapsed -lt $MAX_WAIT ]; do
sleep $POLL
elapsed=$((elapsed + POLL))
pane=$(ssh "$MAC" "$TMUX capture-pane -t $SESSION -p 2>/dev/null | tail -10")
# auto-approve confirmation prompts
if echo "$pane" | grep -qiE 'do you want|allow|yes.*no|\[y/n\]|approve|1\).*2\)'; then
ssh "$MAC" "$TMUX send-keys -t $SESSION '2' Enter"
sleep 3; continue
fi
# shell prompt = claude exited
echo "$pane" | grep -qE '^\s*(%|\$|>)\s*$' && break
done
# 5. grab result + session_id
result=$(ssh "$MAC" "cat ~/task_output.json 2>/dev/null")
session_id=$(echo "$result" | python3 -c \
"import json,sys; print(json.load(sys.stdin).get('session_id',''))" 2>/dev/null)
# 6. cleanup
ssh "$MAC" "$TMUX kill-session -t $SESSION 2>/dev/null; rm -f ~/task.md ~/task_output.json"
echo "done in ${elapsed}s | session_id: $session_id"When to use tmux vs MCP tool:
Task type | Use |
Quick question, code review, research | MCP tool |
Multi-step work with session chaining | MCP tool + |
Project scaffolding / | tmux blocking loop |
Multi-file writes across a codebase | tmux blocking loop |
Unknown duration / likely >5 min | tmux blocking loop |
If unsure → tmux. A 30s task in tmux costs nothing extra. A 12-min task in the MCP tool gets killed at 10 min.
Monitoring
python3 monitor.py # live feed (tails bridge.log)
python3 monitor.py /path/to/bridge.logColor-coded live TUI showing start/done/error/timeout events with task previews, response length, cost, and duration.
Troubleshooting
Symptom | Cause | Fix |
| Mac host key unknown |
|
| SSH key not deployed | Run |
| Wrong binary path | Set |
Bridge times out | Task too long for | Increase timeout via env var, or use the tmux pattern for tasks >10 min (see Long Tasks section) |
| Claude returned an error | Check |
Session not continuing | Claude process ended on Mac | Sessions live as long as the daemon on Mac, restarting Mac or Claude clears them |
| Prompt has unmatched quotes or shell metacharacters | The JSON output format should handle escaping, if hit, wrap task in a temp file |
Security
bridge.logcontains full task/response history, it is gitignored, keep it localNever commit
.envor SSH private keys--dangerously-skip-permissionsis passed to Claude so it can use tools without interactive prompts, only use this on a trusted Mac you controlThe SSH command runs as-is on your Mac, do not expose this bridge to untrusted clients
Known Limitations
Mac must be awake + on Tailscale. If the Mac sleeps or goes offline, delegations timeout.
Session lifetime depends on Claude process. If Claude restarts (Mac reboot, crash), session IDs are lost.
bridge.loggrows unbounded. No log rotation, monitor size or add your own.Async server, concurrent tool calls are supported but each delegation opens its own SSH connection to the Mac.
--dangerously-skip-permissionsis hardcoded. If you want to review permissions per-call, fork and modify.
License
MIT.
Trust boundary
By default the bridge runs Claude Code under the Mac's own permission
configuration. Setting CLAUDE_BRIDGE_SKIP_PERMISSIONS=true removes that
layer: any agent that can call this MCP tool then has arbitrary code
execution on the Mac. Only enable it if the MCP server is reachable
exclusively by agents you would hand a shell to.
The audit log (bridge.log) records the full task, context, and response
text of every delegation. Treat it as sensitive and leave it out of any
repo (it is gitignored here).
Status
What this is: a single-tool MCP server that delegates tasks to Claude Code on one Mac over SSH, with session continuity and an audit log.
What this is NOT: an authentication layer, a sandbox, or a multi-host router. The argument building, output parsing, and log rotation are unit-tested; the SSH path is exercised by supervised live delegations, not CI. This file has a sibling repo, see PORTING.md.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/freeze1999/claude-mac-bridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server