Skip to content

sidkandan/universal-remote-control

Repository files navigation

URC — Universal Remote Control

Control Codex and Gemini from the Claude App on your phone.

URC Demo — orchestrating Codex and Gemini from the Claude App

You know /remote-control — the Claude Code feature that lets you control a session from the Claude App on your phone. URC makes it universal. One command, and your phone controls Codex. Another command, and it controls Gemini. Same app, same conversation, same interface. No extra API keys. No extra apps.

/urc codex    # Your phone now controls Codex
/urc gemini   # Your phone now controls Gemini

Behind the scenes, URC spawns a lightweight Haiku relay that acts as a pure passthrough — your messages go through unchanged, output comes back verbatim. The relay is self-managing: it auto-clears, auto-reconnects on target death, and runs indefinitely without intervention.

Features

Phone control for every CLI

  • Bridge any Codex or Gemini pane from your phone in one command
  • Spawn new panes or bridge existing ones (/urc 875)
  • Initiate bridges from the other side too (Codex skill or Gemini /rc command)

Self-managing relay

  • Auto-clears at 25 sends — respawns itself, restores phone connection, resets counter
  • Auto-reconnects on target death — spawns replacement, retries your message (3 attempts)
  • Type "status" to check capacity, target health, and respawn count
  • No manual maintenance, no /clear, no restarts

Real-time visibility

  • Push attribution shows who dispatched each message and what was asked
  • Instant "message received" confirmation when target gets your message
  • Responses stream back to your phone as turns complete

$0 relay mode

  • Type >codex: message or >gemini: message for hook-based passthrough
  • Zero model invocation, zero cost — a UserPromptSubmit hook handles dispatch
  • Setup: bash urc/core/relay-ctl.sh add codex %NNN && bash urc/core/relay-ctl.sh on

Cross-CLI coordination

  • 11-tool MCP server for pane-level communication (dispatch, read, register, heartbeat, messaging)
  • Agents across Claude Code, Codex, and Gemini can send messages, share work, and coordinate
  • 5-layer inbox notification stack ensures no message is missed

Tested and validated

  • 160 assertions across 10 test suites
  • 23-check plugin validation
  • Post-Enter stuck-input detection for TUI reliability

Quick Start

Prerequisites

  • tmux: brew install tmux (macOS) or sudo apt install tmux (Linux)
  • Python 3.10+, jq
  • Claude Code CLI: curl -fsSL https://claude.ai/install.sh | bash
  • Claude Max plan (for phone control — see claude.com/pricing)
  • Codex CLI and/or Gemini CLI (optional — install whichever you want to bridge)

Install

tmux new -s urc                # Start a tmux session (required)
git clone https://github.com/siddharthkandan/universal-remote-control
cd universal-remote-control
./setup.sh                     # Detects your CLIs, generates configs

Or install as a Claude Code plugin:

/plugin marketplace add siddharthkandan/universal-remote-control
/plugin install urc

Use It

From Claude Code (inside tmux):

/urc codex                    # Spawn Codex pane + bridge it to your phone
/urc gemini                   # Spawn Gemini pane + bridge it to your phone
/urc 875                      # Bridge an existing pane by ID
/urc                          # List unbridged panes

Open the Claude App on your phone — the relay automatically activates Remote Control. Type a message and it goes straight to Codex or Gemini. Responses stream back as they complete.

Aliases: /rc-bridge, /rc-any, /rc-relay all work.

You can also bridge from the other side:

  • From Codex: activate the rc-bridge skill
  • From Gemini: type /rc

Your Claude plan covers the Haiku relay — no separate API key needed.

Gemini Setup Note

Gemini CLI requires additional configuration beyond what setup.sh generates:

  • setup.sh creates the project config and policy rules automatically
  • If ~/.gemini/settings.json has a tools.allowed whitelist, remove the "tools" block
  • Verify: gemini mcp list (outside session) or /mcp list (in session)
  • Note: Gemini's /tools command intentionally hides MCP tools — don't use it to check

How It Works

Phone (Claude App)
    |  Remote Control
    v
Haiku Relay (rc-bridge agent)        ← self-managing, auto-clears at 25 sends
    |  send.sh              push files
    |  (dispatch) ←———————— (response)
    v                           |
Target pane (tmux)              |
    |  hook.sh fires ———————————┘    ← captures response, writes push file

The relay is a stateless passthrough. State lives in tmux pane options (@bridge_target, @bridge_cli, @bridge_relays). Dispatch and response reading are handled through a UserPromptSubmit hook (bridge-push-hook.sh) — the relay's phone message triggers the hook, which dispatches via send.sh and reads any pending push files, all without consuming a model turn. When the target completes its turn, hook.sh captures the response and writes a push file. The relay picks it up on its next wake and displays it on your phone.

The MCP server (urc-coordination, 11 tools) provides the cross-pane infrastructure: dispatch messages, read pane output, register agents, track heartbeats, and send/receive async messages via SQLite. Any agent in any CLI can use these tools to coordinate with others.

A second MCP server (urc-teams, 17 tools) provides structured team creation, typed messages, and task dependencies. Currently dormant — the coordination server handles all active messaging.

Project Structure

The root directory includes CLI-specific files (CLAUDE.md, AGENTS.md, GEMINI.md, symlinks) because each AI CLI has its own conventions for discovering instructions. All actual code lives in urc/.

Core (urc/)

urc/
├── core/
│   ├── server.py                 Coordination MCP server (11 tools)
│   ├── db.py                     SQLite foundation
│   ├── teams_protocol.py         Teams data layer (dormant)
│   ├── teams_server.py           Teams MCP server (dormant)
│   ├── send.sh                   Pane dispatch (bracketed paste + post-Enter verify)
│   ├── hook.sh                   Turn completion + push attribution + respawn
│   ├── wait.sh                   Blocking wait with self-wake
│   ├── dispatch-and-wait.sh      Atomic dispatch + wait + read
│   ├── cli-adapter.sh            CLI detection + paste behavior
│   ├── urc-spawn.sh              Fire-and-forget relay+target spawner
│   ├── urc-dispatch.sh           ! mode CLI dispatcher
│   ├── urc-status.sh             Fleet status display
│   ├── inbox-watcher.sh          Background inbox notification
│   ├── circuit.sh                Circuit breaker for dispatch failures
│   ├── relay-ctl.sh              $0 relay configuration
│   └── test-*.sh                 8 test suites (94 assertions)
├── lib/
│   └── state-write.sh            Atomic JSON write helper
└── schemas/
    └── response.md               Response file schema

CLI Integration

File/Dir Required by Purpose
CLAUDE.md Claude Code Agent instructions
AGENTS.md Codex Agent instructions
GEMINI.md Gemini Agent instructions
.claude/agents/ Claude Code RC Bridge agent definition
.claude/skills/ Claude Code /urc command
.claude/hooks/ Claude Code Session init + inbox hooks
.agents/skills/ Codex Codex bridge skill

Plugin System

.claude-plugin/plugin.json           Plugin manifest
hooks/hooks.json                     Plugin hooks (Stop + SessionStart)
hooks/scripts/bridge-push-hook.sh    Hook-based dispatch + push reading (UserPromptSubmit)
hooks/scripts/plugin-setup.sh        Auto-setup on first session

Documentation

Glossary

Term What it means
Remote Control A Claude Code feature that lets you control a session from the Claude App on your phone
MCP Model Context Protocol — how AI agents talk to external tools and services
Haiku Claude's fastest model — used for the relay since it just passes messages through
tmux Terminal multiplexer — runs multiple sessions in panes within one window
Pane A terminal session inside tmux, identified by an ID like %875
Push Response delivery from the target pane back to the relay when a turn completes

Troubleshooting

MCP tools not showing in Gemini? Run gemini mcp list. If empty, verify ~/.gemini/policies/urc-mcp.toml exists (created by setup.sh). Remove any tools.allowed whitelist from ~/.gemini/settings.json.

Relay shows "pane does not exist"? The target died. Type "status" to confirm, then "reconnect %NNN" with a new pane ID, or let auto-reconnect handle it.

Text stuck in a pane's input field? A TUI timing edge case. send.sh retries automatically in most cases. If still stuck, press Enter manually.

setup.sh fails on venv creation? Needs Python 3.10+. On some systems: sudo apt install python3.12-venv.

Plugin validation fails? Run bash scripts/validate-plugin.sh for diagnostics. Most common fix: re-run setup.sh.

Known Issues

RC session idle timeout (~20 minutes) — #32982

Remote Control sessions are deregistered server-side after ~20 minutes of idle time, even when the local CLI process is alive. This affects ALL /remote-control sessions (not just URC relays) and is caused by a keepalive bug in Claude Code:

  1. CLAUDE_CODE_REMOTE (set internally for all RC sessions) disables the 5-minute WebSocket keepalive
  2. CLAUDE_CODE_REMOTE_SEND_KEEPALIVES (the replacement) is refcount-gated — only sends keepalives during active model processing, not during idle
  3. Result: idle RC sessions have zero keepalive mechanism and the server deregisters them

Impact on URC: Relay sessions lose their phone connection after ~20 minutes of inactivity. The relay process stays alive but the phone shows "Disconnected" / "unknown network error."

Status: Bug report filed with full source analysis. Waiting for Anthropic's response before implementing workarounds. A watchdog (periodic activity to reset the server-side timer) is the known workaround but is deferred pending upstream clarity.

Known Limitations

  • Gemini auto-reconnect race: send.sh can report delivered for a dying pane. The next message triggers reconnect.
  • Prompt injection surface: The relay passes phone messages in bash variables via send.sh. Low risk (phone user is machine owner), but content is not sanitized.
  • No automated tests for prompt-based features: Push attribution, auto-reconnect, and the health dashboard are behavioral instructions in the agent prompt, not executable code.

License

MIT