claude-bridge
Provides push notifications to a phone for approving or denying pending actions (read, write, exec, etc.) before they are executed on the host machine.
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-bridgecheck disk space usage"
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-bridge
Ever wanted to tell Claude to do something on your PC while you're away? It's a really bad idea, but here's a way to do it anyway: setting up a custom MCP connector. It requires a fair number of moving parts, but it's doable.
Remote-controlled shell/GUI access to your own machine from Claude (or any MCP-speaking client), gated by phone approval and audit logging. Every action — reading a file, running a command, logging into an app — requires an explicit tap on your phone via ntfy. Root-level commands additionally require a TOTP code.
This is not a toy. It gives an AI agent the ability to run real commands as root on your machine, once you approve it. Read the Security model section before you deploy this anywhere.

Unofficial, community project. Not affiliated with or endorsed by Anthropic. "Claude" and "MCP" are used here only to describe compatibility.
Quickstart
git clone <this-repo-url> claude-bridge
cd claude-bridge
cp .env.example .env # edit CLAUDE_BRIDGE_PUBLIC_URL once you have a hostname
./setup.sh # generates secrets, prints your OAuth password + TOTP QR
docker compose up -d # brings up the OAuth/MCP server + bundled ntfy
sudo ./agent/install-agent.sh "$(pwd)/data" # installs the native executorThat's the "brain" (containerized, no host access) and the "agent" (native, the only piece that actually touches your machine) both running. Now:
Put a reverse proxy or tunnel (Caddy, nginx, Cloudflare Tunnel, Tailscale Funnel, whatever you already use) in front of port 8000, and set
CLAUDE_BRIDGE_PUBLIC_URLin.envto that public hostname before runningsetup.sh/docker compose up(it becomes the OAuth issuer and the JWT'saud/issclaims — it must match exactly what you expose).If your phone needs to reach ntfy from outside your LAN, expose port 8091 too (same proxy/tunnel), and subscribe to the notify topic printed by
setup.shin the ntfy app.In claude.ai: Settings → Connectors → Add custom connector, URL
https://your-hostname/mcp, authorize with the passwordsetup.shprinted.Ask Claude to do something. Approve on your phone. Done.
Related MCP server: claude-c2
Architecture
The core design decision: the container never touches your host. Everything that needs real host access runs natively, outside Docker, installed by a plain shell script you can read top to bottom.
┌─────────────────────────── "brain" (Docker) ───────────────────────────┐
│ server.py OAuth 2.1 + PKCE, and the MCP endpoint (merged into │
│ one ASGI app -- no separate reverse proxy needed) │
│ agent_server.py Unix-socket listener: submit/status/list/cancel/ │
│ confirm_totp │
│ control_listener subscribes to ntfy, processes Approve/Deny │
│ status_server.py read-only dashboard + JSON API │
└──────────────────────────────┬──────────────────────────────────────────┘
│ shared bind-mounted ./data volume
│ (queue.db, agent.sock, secrets)
┌───────────────────────────────┴──────────────────────────────────────────┐
│ "agent" (native, installed by agent/install-agent.sh) │
│ executor.py polls the SAME queue.db, actually runs approved │
│ requests as a dedicated system user, with a │
│ narrowly-scoped sudo rule for the exec_sudo tier │
└────────────────────────────────────────────────────────────────────────┘A request's life cycle: MCP tool call → written to the shared queue →
ntfy push notification → you tap Approve → control_listener records the
decision → (exec_sudo: you also submit a TOTP code) → the native
executor.py picks it up and actually runs it → result written back to
the queue → Claude reads it via check_status.
Every request carries a content_hash (sha256 of
action+detail+path+window_name+id) that approval/TOTP steps must match
exactly, so an approval for one request can never be replayed against a
different one.
Action tiers
run_command(action, summary, detail, path, window_name, timeout_seconds):
action | uses | notes |
|
| reads a file directly, no shell, capped at 300KB |
|
| approval notification shows a unified diff |
|
| runs as the agent user; set |
|
| runs as root via a denylist-backed sudo wrapper; default timeout 120s; requires a TOTP code after phone approval |
|
| types into a specific already-open window via |
TTLs before auto-expiry: read/write 600s, exec/exec_sudo 300s,
gui_type 180s. timeout_seconds overrides the exec/exec_sudo default
(max 1800s).
MCP tools
run_command(...)— submit a request, returns{id, status: "pending", expires_at}check_status(request_id)— poll one requestlist_pending()— everything still pending or awaiting_totpcancel_request(request_id)— withdraw your own not-yet-executed requestconfirm_totp(request_id, code)— submit the TOTP code for anexec_sudorequest stuck atawaiting_totp, entirely from chat. This is what makes the whole thing phone-only capable — no terminal needed for the sudo tier.
GUI apps (optional)
To let exec/gui_type interact with a real desktop session:
On the desktop, grant the agent user X11 access (re-run this every login, e.g. via your desktop environment's autostart):
xhost +SI:localuser:claude-bridge-agentUncomment
CLAUDE_BRIDGE_DISPLAY=:0in/etc/systemd/system/claude-bridge-agent.serviceandsystemctl daemon-reload && systemctl restart claude-bridge-agent.
Understand what this means: the agent user can now draw on and interact with whatever's on that real, logged-in desktop. This is a deliberate capability, not a default — don't enable it unless you actually want it.
Security model
Nothing executes without a phone tap. No exceptions, no bypass flags.
exec_sudoadditionally requires a TOTP code after the phone approval — two independent factors. The TOTP secret lives only indata/totp_secret.json.Approval/cancel/TOTP steps are bound to
content_hash, preventing cross-request replay (cancel_requestis the one exception, by design — it can only move a request further from execution, never closer).sudo_runner.pyis the entire sudo attack surface for the agent user — root-owned so the agent can execute it (via the scopedNOPASSWDsudoers ruleinstall-agent.shinstalls) but never modify it. It has a denylist regex for catastrophic patterns (rm -rf /, raw-devicedd/mkfs, fork bombs) as a last-resort backstop — the real boundary is the approval pipeline upstream of it.The brain container has zero host access. No privileged mode, no host PID namespace, no docker socket mount. If it's fully compromised, the blast radius is "attacker controls your OAuth server and can submit fake requests to the same approval queue" — which still requires your phone tap (and TOTP, for root) to do anything.
Cross-UID data sharing: the brain container and the native agent are different UIDs sharing one bind-mounted directory.
install-agent.shand the app code both default to permissive (777/666) permissions on that directory as the pragmatic default for a single-host setup. If you want tighter isolation, put both under a shared group instead and adjust accordingly.GUI access is opt-in and explicit (see above) — a compromised or buggy agent process with
CLAUDE_BRIDGE_DISPLAYset could interact with whatever's on the real desktop session.
Configuration reference
All via .env (read by docker compose) or docker-compose.yml's
environment: block:
variable | default | what |
|
| your externally-reachable hostname, no path suffix — becomes the OAuth issuer/audience |
|
| host port for the MCP/OAuth endpoint |
|
| host port for the status dashboard |
Native agent (agent/install-agent.sh writes these into the systemd unit):
variable | what |
| paths into the shared |
| how the exec_sudo tier invokes |
| optional, e.g. |
Troubleshooting
sqlite3.OperationalError: attempt to write a readonly database— the queue.db was created by one side (container or native agent) with permissions the other side's UID can't write to.bridge_queue.pychmods it to666on everyinit_db()call, but if you're still hitting this, checkls -la ./data.PermissionErrorreaching the data directory from the native agent — the shared data directory needs to be traversable by the agent user through every parent directory, not just have open permissions itself. Putting your checkout inside a locked-down home directory (some distros ship~asdrwx--x---) will break this even if./dataitself is wide open. Either put the project somewhere globally traversable (e.g./opt/claude-bridge), or add the agent user to a group with access to your home directory.ntfy: "auth is unconfigured for this server" —
NTFY_AUTH_FILEmust be set forntfy user addto work at all;docker-compose.ymlalready sets this, but if you changed it, check that first.SELinux (Fedora/RHEL/etc): containers failing to write to bind mounts — the compose file already appends
:zto the volume mounts. If you added new mounts, do the same, or you'll seeavc: deniedentries injournalctl/ausearch -m avc.Fresh chat claims a tool (usually
confirm_totp) doesn't exist — claude.ai caches the tool list per connector session, not per conversation. Fully disconnect/reconnect the connector in Settings → Connectors rather than just starting a new chat.
License
MIT — see LICENSE.
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/Koragan/claude-bridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server