Cursor must be launched with the Chrome DevTools Protocol remote debugging port enabled. This is required for both extension and standalone setups.
- Right-click your Cursor desktop shortcut > Properties
- In "Target", append
--remote-debugging-port=9222 - Click OK
open -a Cursor --args --remote-debugging-port=9222Or create an alias in your shell profile:
alias cursor='open -a Cursor --args --remote-debugging-port=9222'cursor --remote-debugging-port=9222Fully quit and restart Cursor after adding the flag. On macOS, use Cmd+Q (not just close the window) — Cursor runs in the background otherwise.
Open http://localhost:9222/json in a browser. You should see a JSON array. If it doesn't work, make sure Cursor was fully restarted.
The CursorRemote extension provides the easiest setup experience with built-in status UI, auto-start, and a configuration wizard.
Download the latest .vsix from releases and install:
cursor --install-extension cursor-remote-0.1.52.vsixOr in Cursor: Command Palette (Ctrl+Shift+P) > Extensions: Install from VSIX... > select the file.
Open the CursorRemote panel in the activity bar (left sidebar). Click "License Key Required" to enter your key. It's stored securely in the OS credential store.
Get a key from the store.
The server auto-starts when Cursor launches (if cursorRemote.autoStart is true). The sidebar panel shows live status:
- Server: Running / Stopped -- with Start and Stop buttons
- CDP: Connected -- with the active workspace name
- Agent status -- current mode and model
- Clients -- number of connected browser sessions
You can also use Command Palette commands: CursorRemote: Start Server, CursorRemote: Stop Server.
Run CursorRemote: Open Setup Panel to configure:
- Server Bind Address -- choose Localhost (127.0.0.1), LAN (0.0.0.0), or a specific IP for Tailscale
- Web Client Password -- auto-generated on first install. Copy it from the Setup Panel or find it in Settings (
cursorRemote.webappPassword). You can edit it directly. - Click Save & Restart to apply changes.
Open http://<server-ip>:<port> in any browser on your phone, tablet, or another computer.
Assistant code and file-edit diffs are not copied as Cursor’s Monaco HTML. The relay sends structured codeBlocks / diffBlock; the UI shows a compact card (~seven lines with scroll inside the card, momentum scrolling on iOS). Tap the expand control to open a full-screen reader (large close control, tap outside or Escape to dismiss). This keeps long patches readable on small screens without dominating the chat.
Plan widgets in the web app now mirror the remote-control flow more closely:
- View Plan opens a web modal and loads the full saved plan file when available, not just the compact widget summary.
- Plan model opens a web picker with the real model options scraped from Cursor, then applies the selected option back in Cursor.
- Build still triggers the underlying Cursor action directly.
Connection labels are also more specific now. If the phone is still connected to the relay but Cursor/CDP extraction is stalled, the UI shows a waiting/extractor state instead of a generic browser disconnect. This is especially useful on macOS when backgrounded Cursor windows throttle CDP evaluation.
Switch to the Telegram tab in the Setup Panel for a step-by-step wizard:
- Create a bot -- paste the token from @BotFather
- Create a supergroup -- enable Topics, add bot as admin
- Register -- the panel shows the actual
/register <token>command to copy - Sync -- send
/syncin the group
The panel also shows registered users and their usernames.
Only one server instance runs across all Cursor windows:
- The first window to start becomes the owner and spawns the server process.
- Other windows detect the running server via health polling and attach as observers.
- If the owner window closes, an observer automatically takes over and spawns a new server.
- The sidebar shows "observer" next to the server status when a window is not the owner.
Run the relay server directly from the command line. Useful for headless machines, remote servers, or manual configuration via .env files.
git clone https://github.com/len5ky/CursorRemote.git cursor-ide-remote
cd cursor-ide-remote
npm install
cp .env.example .envEdit .env -- the defaults work for the web client. For Telegram, set TELEGRAM_ENABLED=true and TELEGRAM_BOT_TOKEN (see section 5).
npm run devLicense key (first run only): You'll be prompted for a license key. Get one from the store. The key is saved to data/license.key and won't be asked again on subsequent runs. For production (npm start), ensure the key file exists before starting.
[main] CDP URL: http://127.0.0.1:9222
[main] Server: http://127.0.0.1:3000
[telegram] Bot connected (sync: off, users: 0)
[telegram] To register, send: /register A1B2C3D4
Extension users: The Setup Panel handles networking with a few clicks. The manual instructions below are primarily for standalone or WSL2-specific configurations.
By default, the server binds to 127.0.0.1 and is only accessible from a browser on the same machine.
Set the bind address to 0.0.0.0:
- Extension: Open Setup Panel > Networking > select "LAN access (all interfaces)" > Save & Restart
- Standalone: Set
SERVER_HOST=0.0.0.0in.env
Then open http://<your-ip>:<port> on your phone. A password is required.
If running on WSL2, the server is isolated from your LAN. You need one of:
Add to %UserProfile%\.wslconfig on Windows:
[wsl2]
networkingMode=mirroredRestart WSL2: wsl --shutdown
# Find WSL2 IP
wsl hostname -I
# Forward port (PowerShell as Admin)
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=<WSL2-IP>New-NetFirewallRule -DisplayName "CursorRemote" -Direction Inbound -LocalPort 3000 -Protocol TCP -Action AllowTailscale (recommended) -- access over a private VPN, no port forwarding needed. See Tailscale Setup.
Password protection -- set a password in the Setup Panel (extension) or WEBAPP_PASSWORD in .env (standalone). Login is rate-limited to 10 attempts per minute per IP.
Both can be combined. See Tailscale Setup for details.
Extension users: The Setup Panel's Telegram tab provides a step-by-step wizard. The instructions below cover the manual process.
- Message
@BotFatheron Telegram >/newbot> follow prompts - Copy the bot token
- Disable privacy:
@BotFather>/mybots> Bot Settings > Group Privacy > Turn OFF
Extension: Open Setup Panel > Telegram tab > paste the bot token > Save Token. The extension enables Telegram automatically.
Standalone: Edit .env:
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxThe server prints the registration token on startup:
- Extension: Check the Output panel (CursorRemote channel) or the Setup Panel's Telegram tab
- Standalone: Check the terminal output
[telegram] To register, send in your Telegram group: /register A1B2C3D4
- Create a Telegram group
- Add your bot to the group
- Enable Topics: Group Settings > Topics > Enable
- Make bot admin: Group Settings > Administrators > Add bot with all permissions (especially Manage Topics, Delete Messages)
In the Telegram group:
/register A1B2C3D4-- register yourself with the token from the server output/sync-- enable auto-sync for this group
The bot checks permissions and creates topics for all current windows. From now on, new chat tabs get topics automatically.
| Command | Description |
|---|---|
/register <token> |
Register with the token shown in server output |
/sync |
Enable auto-sync (active tabs + last 5 messages) |
/sync_all |
Create topics for ALL tabs in all windows |
/unsync |
Disable sync, delete tracked topics, clear state |
/cleanup |
Delete stale/untracked topics, keep active ones |
/purge |
Delete ALL topics (runs in background) |
/status |
Sync state, connection, agent info, group ID |
/history [N] |
Last N messages (default 5). /history 100 for more |
/mode |
Show/switch mode (Agent/Plan/Ask/Debug) |
/model |
Show current model |
/plan <text> |
Prompt in Plan mode |
/agent <text> |
Prompt in Agent mode |
Plain text in any topic is forwarded to the mapped Cursor agent.
- Window Monitor polls all Cursor windows every 10s using parallel CDP connections (no visible window switching)
- New/changed messages are formatted as Telegram HTML and sent to the matching topic
- If HTML fails (unsupported tags), messages are retried as plain text
- A rate-limited send queue prevents 429 errors (~300ms between Telegram sends, 100ms between edits; see
send-queue.ts/ transport config) - Data files in
data/(all gitignored):license.key-- license key (required on first run)telegram-auth.json-- registration token + registered users with usernamestelegram-sync.json-- sync state and group IDtelegram-topics.json-- topic mappings with high water marktelegram-messages.json-- tracked message IDs
Option A: Token-based (default)
Share the registration token (shown in server output) with collaborators. Each person runs /register <token> once. Their username and ID are saved to data/telegram-auth.json.
Option B: Hardcoded (override)
Set TELEGRAM_ALLOWED_USERS=123456789,987654321 in .env (standalone) or cursorRemote.telegram.allowedUsers in Settings (extension). When set, this overrides the token auth — only these user IDs can use the bot. Remove the setting to go back to token-based auth.
tmux new -s cursor-remote
npm run dev
# Ctrl+B D to detachnpm run build
npm startEnsure data/license.key exists before running npm start (no prompt in production mode).
- Extension: Open the CursorRemote sidebar panel and click "License Key Required" to enter your key
- Standalone: Run
npm run dev(notnpm start) to get the interactive prompt - Get a valid key from the store
- First check
http://<server>:<port>/healthfrom the phone or tablet connected: falsemeans the relay is not attached to Cursor/CDP yetconnected: truewithextractorStatus: "waiting"means the relay is attached to Cursor but is still waiting for the first DOM snapshotconnected: truewithextractorStatus: "stale"means Cursor/CDP is still connected but DOM extraction is failing or background-throttledlastExtractionErrorshows the latest extractor failure reason
- On macOS, Electron/Chromium can throttle a backgrounded Cursor window enough for
Runtime.evaluateto time out - If
/healthshowsconnected: trueandextractorStatus: "stale", bring Cursor back to the foreground and wait for the next successful snapshot - The relay now backs off repeated extractor timeouts instead of hammering CDP continuously
curl http://<ip>:<port>/healthfrom another device- Check firewall, port forwarding, WSL2 networking
- Verify the server is bound to
0.0.0.0or your specific IP (not127.0.0.1)
- Recent builds no longer require
crypto.randomUUID()support in the browser - If the page still fails to load, open the browser console and check for other unsupported Web APIs
- Upgrade to the latest CursorRemote build before testing older iOS/Android browsers
- Open the Output panel (CursorRemote: Show Logs) and check for errors
- Try Stop > Start from the sidebar buttons
- Verify CDP is enabled:
http://localhost:9222/jsonshould return JSON
- Only one server runs. The first window is the owner; others are observers.
- The sidebar shows "observer" next to the server status for non-owner windows.
- If the owner window closes, an observer auto-recovers within ~15 seconds.
- Check the Output panel for connectivity messages
- The server tests outbound HTTPS on startup and reports if Telegram API or all HTTPS is unreachable
- Ensure no other bot instance is running with the same token
- The registration token is shown in the Output panel and the Setup Panel's Telegram tab
TELEGRAM_ENABLED=truein.env?- Bot is admin with Manage Topics permission?
- Privacy mode OFF? (
@BotFather> Bot Settings > Group Privacy) - Did you
/registerwith the correct token? - Check
temp/server.logfor errors
- Enable Topics in Group Settings first (auto-converts to supergroup)
- The bot auto-detects the correct group ID from
/sync
- Go to Group Settings > Administrators > Bot > enable all listed permissions
- Required: Manage Topics, Delete Messages
npm run buildcompiles TS and copiessrc/client/todist/client/npm startcreates thetemp/directory automatically
All output with timestamps: temp/server.log