Skip to content
Conduit

mcp server

remote-claws

Allow your agent/openclaw to do things remotely, with controls, on a Desktop machine via MCP

wentbackward/remote-clawscommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "remote-claws": {
      "command": "uvx",
      "args": [
        "remote-claws"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 25, 2026

Remote Claws

License: MIT Python 3.11+ MCP GitHub

An MCP server that gives AI agents full control of a remote desktop machine — browser automation, mouse/keyboard, command execution, and file transfer. Deploy it on a PC, connect your agent, and let it work.

What It Does

Remote Claws exposes 4 tools (39 actions) over MCP via HTTP. Each tool takes an action parameter, like a CLI subcommand:

ToolActionsWhat It Controls
remote_browser16Navigate, click, fill forms, read text, run JS, take screenshots — real Chrome with your identity
remote_desktop12Mouse clicks, keyboard input, screenshots, window management — pyautogui (cross-platform) + pywinauto (Windows-only element inspection)
remote_exec5Start processes, stream stdout/stderr, send stdin, kill — fully async
remote_files6Read, write, list, move, delete — base64 transfer with chunked reads

The remote_ prefix is deliberate: agents often have their own local browser/exec/read/write tools, and identical names cause the model to confuse which machine it's acting on.

See TOOLS.md for the complete tool reference and SKILLS.md for a high-level capability overview.

Quick Start

git clone https://github.com/wentbackward/remote-claws.git
cd remote-claws
python -m venv .venv
.venv\Scripts\activate        # Windows
# source .venv/bin/activate   # Linux/macOS
pip install -e .

No playwright install step is needed — the default browser_channel=chrome drives your system Chrome, not a bundled browser.

1. Generate Auth Token

remote-claws-setup

This prints a bearer token once — copy it now. Only the SHA-256 hash is stored on disk. The setup script also:

  • Prompts for transport choice (SSE or Streamable HTTP)
  • Offers to chain into remote-claws-browser-setup

2. Seed the Browser Profile (Optional but Recommended)

remote-claws-browser-setup
# or jump straight to a site to log into:
remote-claws-browser-setup --url https://nytimes.com

Chrome opens on a dedicated profile (separate from your daily Chrome) under %LOCALAPPDATA%\RemoteClaws\chrome-profile on Windows, ~/.local/share/remote-claws/chrome-profile on Linux, ~/Library/Application Support/RemoteClaws/chrome-profile on macOS. Sign into the services you want the agent to access, install your adblocker, accept cookie banners, then close the window. Sessions persist across server restarts.

The browser group drives system-installed Google Chrome with this persistent profile — this is the whole point of the feature: the agent browses like you do, signed into subscriptions, past bot walls, with your adblocker.

You probably do not want the bundled Playwright Chromium (browser_channel=chromium). If all you need is a plain automatable browser, run one locally where your agent lives (e.g. openclaw's built-in browser) — don't stand up a remote server for that. The chromium channel exists only for CI and similar throwaway environments, and requires playwright install chromium.

The server hard-fails at startup if browser_channel=chrome (the default) and Chrome isn't installed.

3. Start the Server

remote-claws

The server runs in the foreground on 0.0.0.0:8080 by default. Agents connect to http://<your-ip>:8080/mcp (Streamable HTTP, the default) or http://<your-ip>:8080/sse (legacy SSE) with the bearer token.

Security

Authentication

Every connection requires a bearer token in the Authorization header. The server:

  1. Stores only a SHA-256 hash of the token (never the raw token)
  2. Uses timing-safe comparison (hmac.compare_digest) to prevent timing attacks
  3. Refuses to start if no auth file exists
  4. Returns 401 for missing, malformed, or invalid tokens

To rotate the token, run remote-claws-setup again.

Permission Policy

permissions.json controls what the agent can do. Each tool group (browser, desktop, exec, files) has allow and deny lists of bare action names. Deny always supersedes allow.

{
  "permissions": {
    "browser": { "allow": ["*"], "deny": [] },
    "desktop": { "allow": ["*"], "deny": ["click_element"] },
    "exec":    { "allow": ["run", "get_output", "list"], "deny": [] },
    "files":   { "allow": ["read", "list", "info"], "deny": [] }
  }
}

Use "*" to allow/deny an entire group. Omitting a group denies it entirely.

Enforcement is two-tier:

  • Group level (registration time): a disabled or fully-denied group means its tool is never registered — it doesn't show up in tools/list at all.
  • Action level (call time): within a registered tool, a denied action returns {"error": "permission denied: <group>:<action>"}. The tool can't partially hide its own description, so the agent learns the boundary from the error.

Legacy entries using pre-consolidation tool names (browser_navigate, file_read) are auto-normalized to bare action names at load, with a deprecation warning in the server log.

Disabling Whole Tool Groups at Startup

Set enabled_groups to skip a group entirely. Disabled groups are never imported, so heavy dependencies (Playwright for browser, pyautogui for desktop) stay out of memory:

REMOTE_CLAWS_ENABLED_GROUPS="exec,files" remote-claws

Or in remote-claws.json:

{ "enabled_groups": "exec,files" }

A group is active only when it appears in enabled_groups and its permissions.json entry permits at least one tool.

IP Allowlist

Lock the server to specific source IPs. Connections from any other IP are rejected with 403 before auth is even checked:

REMOTE_CLAWS_ALLOWED_IPS="100.82.48.9,100.106.2.100" remote-claws

Or in remote-claws.json:

{ "allowed_ips": "100.82.48.9,100.106.2.100" }

When empty (the default), IP filtering is disabled and access relies on bearer token auth alone.

Defense in Depth

The server has three independent security layers, outermost first:

  1. IP allowlist — rejects connections from unknown IPs (403, before any processing)
  2. Bearer token — rejects unauthenticated requests (401, timing-safe hash comparison)
  3. Permission policy — restricts which tools are available (per-tool granularity)

For production, also consider:

  • Run behind a VPN (Tailscale, WireGuard) for encrypted transport
  • Use firewall rules as an additional network-level gate
  • Put behind a reverse proxy with TLS for encryption in transit

Connecting Agents

Remote Claws supports two transport protocols, one at a time (chosen at setup or via REMOTE_CLAWS_TRANSPORT):

TransportPathClients
Streamable HTTP (current MCP spec, default)/mcpopenclaw, Claude Code, newer SDKs
SSE (legacy, deprecated by the MCP spec)/sseClaude Desktop and older clients

remote-claws-setup prompts for the choice and writes it to remote-claws.json. To serve SSE clients, set "transport": "sse". Both transports share the same browser tabs and process tracker — reconnecting clients reattach the same session.

Claude Desktop

Claude Desktop uses the legacy SSE transport, so set "transport": "sse" in remote-claws.json first. Then add to your Claude Desktop config (%APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "remote-claws": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://YOUR_IP:8080/sse",
        "--header",
        "Authorization:${REMOTE_CLAWS_TOKEN}"
      ],
      "env": {
        "REMOTE_CLAWS_TOKEN": "Bearer YOUR_TOKEN_HERE"
      }
    }
  }
}

Claude Code

claude mcp add remote-claws \
  --transport http \
  --url http://YOUR_IP:8080/mcp \
  --header "Authorization: Bearer YOUR_TOKEN_HERE"

LangChain / LangGraph

from langchain_mcp_adapters.client import MultiServerMCPClient

async with MultiServerMCPClient(
    {
        "remote-claws": {
            "url": "http://YOUR_IP:8080/mcp",
            "transport": "streamable_http",
            "headers": {
                "Authorization": "Bearer YOUR_TOKEN_HERE"
            }
        }
    }
) as client:
    tools = client.get_tools()
    # Use tools with your LangGraph agent

OpenAI Agents SDK

from agents import Agent
from agents.mcp import MCPServerStreamableHttp

mcp = MCPServerStreamableHttp(
    url="http://YOUR_IP:8080/mcp",
    headers={"Authorization": "Bearer YOUR_TOKEN_HERE"},
)

agent = Agent(
    name="desktop-controller",
    mcp_servers=[mcp],
)

n8n

  1. Add an MCP Client Tool node to your workflow
  2. Set the URL to http://YOUR_IP:8080/mcp
  3. Add a custom header: Authorization: Bearer YOUR_TOKEN_HERE
  4. The node auto-discovers all 4 tools (39 actions)

OpenClaw

Add to ~/.openclaw/openclaw.json:

{
  "mcp": {
    "servers": {
      "remote-claws": {
        "url": "http://YOUR_IP:8080/mcp",
        "transport": "streamable-http",
        "enabled": true,
        "headers": {
          "Authorization": "Bearer YOUR_TOKEN_HERE"
        }
      }
    }
  }
}

Then run openclaw mcp reload (or restart the gateway) — see the setup guide for the full verification workflow.

For a detailed walkthrough including skill installation and troubleshooting, see remote-claws-openclaw-setup-guide.md.

Any MCP Client

Remote Claws works with any MCP-compliant client. Connect to either transport:

Streamable HTTP (default): http://YOUR_IP:8080/mcp
SSE (legacy):              http://YOUR_IP:8080/sse
Header: Authorization: Bearer YOUR_TOKEN_HERE

Configuration

Config File (remote-claws.json)

Drop a remote-claws.json in the working directory to set defaults. Every setting from the env var table below can be set here. Values support ${ENV_VAR} expansion and ${ENV_VAR:-default} fallback:

{
  "host": "192.168.1.50",
  "port": 9090,
  "allowed_hosts": "localhost,${TAILSCALE_IP:-127.0.0.1}",
  "browser_headless": false,
  "screenshot_quality": 85,
  "auth_file": "${HOME}/.remote-claws-auth.json"
}

Copy remote-claws.example.json as a starting point:

cp remote-claws.example.json remote-claws.json

Environment Variables

Env vars override the config file. All use the REMOTE_CLAWS_ prefix:

VariableDefaultDescription
REMOTE_CLAWS_HOST0.0.0.0Bind address
REMOTE_CLAWS_PORT8080Listen port
REMOTE_CLAWS_TRANSPORTstreamable-httpMCP transport: streamable-http or legacy sse (one at a time)
REMOTE_CLAWS_ALLOWED_IPS(empty)Comma-separated source IPs allowed to connect. Empty = no IP filtering (rely on token auth). Checked before auth.
REMOTE_CLAWS_ALLOWED_HOSTS*Comma-separated trusted Host headers. * disables host checking.
REMOTE_CLAWS_AUTH_FILE.remote-claws-auth.jsonPath to auth hash file
REMOTE_CLAWS_PERMISSIONS_FILEpermissions.jsonPath to permission policy
REMOTE_CLAWS_ENABLED_GROUPSbrowser,desktop,exec,filesTool groups loaded at startup. Groups not listed are not imported and expose no tools.
REMOTE_CLAWS_BROWSER_CHANNELchromeWhich browser to drive. chrome = system Google Chrome (real fingerprint, persistent profile) — almost always what you want. chromium = bundled Playwright build for CI (requires playwright install chromium; visibly automated, will trip bot walls).
REMOTE_CLAWS_BROWSER_PROFILE_DIROS defaultOverride the Chrome user-data directory. Empty = OS-appropriate default.
REMOTE_CLAWS_BROWSER_STEALTHtrueApply tf-playwright-stealth patches to every page. Disable only if a site misbehaves under them.
REMOTE_CLAWS_BROWSER_HEADLESSfalseRun Chrome headless. Strongly discouraged when browser_channel=chrome — anti-bot vendors fingerprint headless rendering.
REMOTE_CLAWS_SCREENSHOT_MAX_WIDTH1280Max screenshot width
REMOTE_CLAWS_SCREENSHOT_HEIGHT960Max screenshot height
REMOTE_CLAWS_SCREENSHOT_QUALITY75JPEG quality (1-100)
REMOTE_CLAWS_SCREENSHOT_DIR(empty)Audit directory for remote_browser screenshot copies (when set)
REMOTE_CLAWS_SHOT_TTL_SECONDS600Seconds a /dl/ capability-URL download stays valid
REMOTE_CLAWS_MAX_INLINE_BYTES1048576Max bytes remote_files read returns inline (base64) before hard-erroring with an as_url recovery
REMOTE_CLAWS_CONFIG_FILEremote-claws.jsonPath to the JSON config file

Priority

Highest wins: env vars → config file → built-in defaults

Troubleshooting: 421 Errors

If agents connecting over a VPN or remote IP get 421 Misdirected Request, the server is rejecting the Host header. Fix:

# Allow your Tailscale IP
REMOTE_CLAWS_ALLOWED_HOSTS="localhost,127.0.0.1,100.82.48.9" remote-claws

# Or disable host checking entirely (if you trust your network)
REMOTE_CLAWS_ALLOWED_HOSTS="*" remote-claws

Or set it in remote-claws.json:

{ "allowed_hosts": "localhost,127.0.0.1,100.82.48.9" }

Smoke Testing

scripts/smoke_browser.py connects to a running server over SSE and drives a short real-world browsing session (X.com posts, Bloomberg headlines, follow a story link). It saves a screenshot per step under ./smoke-screenshots/ so you can visually confirm that the persistent Chrome profile is signed in and that paywalls / bot walls aren't blocking you.

pip install "mcp[cli]>=1.20"
export REMOTE_CLAWS_URL="http://<remote-host>:8080/mcp"
export REMOTE_CLAWS_TOKEN="<bearer token>"
python scripts/smoke_browser.py

How It Works

The server embeds comprehensive instructions that tell connected agents how to use the tools effectively — which tool group to pick, recommended workflows, and important gotchas. Agents receive these instructions automatically when they connect via MCP.

Browser tools use Playwright with CSS selectors — reliable and resolution-independent. The browser launches lazily on first use and stays open across tool calls.

Desktop tools use pyautogui for mouse/keyboard and pywinauto for Windows UI element targeting. Agents can click by screen coordinates (from screenshots) or by element name (more reliable).

Exec tools are fully async — start a process, do other work, check back for output, send input if interactive.

Screenshots are downscaled to JPEG and returned inline so vision-capable models can see and act on them.

License

MIT

↑↓ navigate openesc close