Skip to main content

Prerequisites

Before you start, make sure you have:
  • A Tailscale account with your devices enrolled in a mesh network
  • A VPS or always-on machine to host the server (must be reachable from Claude.ai)
  • Node.js 18+ or Docker installed on that machine
  • A Tailscale API key and a GitHub personal access token
1

Clone the repository

git clone --branch v0.1.0 https://github.com/thezem/reacher.git
cd reacher
2

Copy and configure your environment files

Copy both config files from their examples:
cp .env.example .env
cp reacher.config.example.yaml reacher.config.yaml
Open .env and fill in your credentials. Every required variable is listed below.Required environment variables
VariableRequiredDescription
MCP_SECRETYesShared secret that Claude.ai sends with every request. Generate with openssl rand -hex 32.
TAILSCALE_API_KEYYesAPI key for querying your Tailscale network. Needs “Devices (read)” scope. Get one at tailscale.com/admin/settings/keys.
GITHUB_TOKENYesPersonal access token for GitHub API calls and gist_kb read/write. Needs gist scope. Create at github.com/settings/tokens.
PROXY_ALLOWED_DOMAINSYesComma-separated list of domains fetch_external is allowed to call (e.g. api.github.com,api.linear.app).
PORTNoHTTP port to listen on. Defaults to 3000.
DRY_RUNNoSet to true to have ssh_exec log commands without executing them. Defaults to false.
AUDIT_ENABLEDNoEnable audit logging. Defaults to true.
AUDIT_LOG_PATHNoPath to the audit log file. Defaults to ./reacher-audit.log.
SSH_BLOCKED_COMMANDSNoComma-separated list of commands to block from ssh_exec (e.g. rm -rf /,shutdown,reboot).
SSH_ALLOWED_DIRSNoComma-separated list of directories SSH operations are restricted to. Empty means no restriction.
FETCH_EXTERNAL_TOKEN_MAPNoJSON mapping of domain → env var name for automatic token injection (e.g. {"api.github.com":"GITHUB_TOKEN"}).
BROWSER_CDP_HOSTNoHost of the CDP-compatible browser. Defaults to 127.0.0.1.
BROWSER_CDP_PORTNoPort of the CDP-compatible browser. Defaults to 9222.
MCP_SECRET, TAILSCALE_API_KEY, and GITHUB_TOKEN are validated at startup. The server will refuse to start if any of these are missing.
Optional: edit reacher.config.yamlThe YAML config controls safety settings. The defaults are reasonable, but you can tighten them:
ssh:
  blocked_commands:
    - "rm -rf"
    - "dd"
    - ":(){ :|:& };:"  # fork bomb
  allowed_dirs: []     # empty = no directory restriction

audit:
  enabled: true
  log_path: "./reacher-audit.log"

dry_run: false
Environment variables always take precedence over YAML config values.
3

Enable Tailscale SSH on target devices

For ssh_exec to reach a machine, Tailscale SSH must be enabled on it. Run this on each device you want Reacher to control:
sudo tailscale up --ssh
Then verify SSH access works from your server:
ssh user@device-hostname
If that succeeds manually, Reacher will be able to reach it too.
You can skip this step initially and come back to it. The tailscale_status tool works without SSH being enabled, and you can enable SSH per device as you need it.
4

Start the server

Choose your runtime:
docker compose up -d
Check the server started correctly:
docker compose logs -f reacher
If startup fails with “Missing required environment variables”, check that MCP_SECRET, TAILSCALE_API_KEY, and GITHUB_TOKEN are all set in your .env file.
5

Verify the server is responding

Run a tools/list request against the local server to confirm everything is working:
curl -X POST "http://localhost:3000/mcp?token=YOUR_MCP_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
Replace YOUR_MCP_SECRET with the value you set in .env. You should get back a JSON response listing all six tools: ssh_exec, tailscale_status, fetch_external, github_search, gist_kb, and browser.You can also check the health endpoint (requires the same token):
curl "http://localhost:3000/health?token=YOUR_MCP_SECRET"
Expected response:
{ "status": "ok", "timestamp": "...", "dry_run": false }
6

Expose the server over HTTPS

Claude.ai requires a public HTTPS URL to connect to your server. If you’re running on a VPS, set up a reverse proxy in front of port 3000.
# /etc/caddy/Caddyfile
mcp.yourdomain.com {
  reverse_proxy localhost:3000
}
Caddy handles HTTPS certificate provisioning automatically via Let’s Encrypt. With Nginx, you’ll need to run Certbot separately to obtain and renew a certificate.
After setting up the proxy, confirm it’s reachable from the public internet:
curl -X POST "https://mcp.yourdomain.com/mcp?token=YOUR_MCP_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
7

Connect to Claude.ai

  1. Go to Claude.ai > Settings > Integrations
  2. Click Add custom connector
  3. Paste your server URL: https://mcp.yourdomain.com/mcp?token=YOUR_MCP_SECRET
  4. Save and start a new conversation
Try asking: “What devices are on my Tailscale network?” — Claude should call tailscale_status and list your devices.See Connecting to Claude for full instructions and troubleshooting.

Next steps

Connect to Claude

Step-by-step instructions for adding the connector in Claude.ai settings.

Tools reference

Learn what each tool does and how to use it effectively.

Configuration

Full reference for all environment variables and YAML config options.

Safety settings

Configure blocked commands, directory restrictions, and audit logging.