> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ouim.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Get Reacher running in under 10 minutes with Docker or bare Node.

## Prerequisites

Before you start, make sure you have:

* A [Tailscale](https://tailscale.com) 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

<Steps>
  <Step title="Clone the repository">
    ```bash theme={null}
    git clone --branch v0.1.0 https://github.com/thezem/reacher.git
    cd reacher
    ```
  </Step>

  <Step title="Copy and configure your environment files">
    Copy both config files from their examples:

    ```bash theme={null}
    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**

    | Variable                   | Required | Description                                                                                                                                                                 |
    | -------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MCP_SECRET`               | Yes      | Shared secret that Claude.ai sends with every request. Generate with `openssl rand -hex 32`.                                                                                |
    | `TAILSCALE_API_KEY`        | Yes      | API key for querying your Tailscale network. Needs "Devices (read)" scope. Get one at [tailscale.com/admin/settings/keys](https://login.tailscale.com/admin/settings/keys). |
    | `GITHUB_TOKEN`             | Yes      | Personal access token for GitHub API calls and `gist_kb` read/write. Needs `gist` scope. Create at [github.com/settings/tokens](https://github.com/settings/tokens).        |
    | `PROXY_ALLOWED_DOMAINS`    | Yes      | Comma-separated list of domains `fetch_external` is allowed to call (e.g. `api.github.com,api.linear.app`).                                                                 |
    | `PORT`                     | No       | HTTP port to listen on. Defaults to `3000`.                                                                                                                                 |
    | `DRY_RUN`                  | No       | Set to `true` to have `ssh_exec` log commands without executing them. Defaults to `false`.                                                                                  |
    | `AUDIT_ENABLED`            | No       | Enable audit logging. Defaults to `true`.                                                                                                                                   |
    | `AUDIT_LOG_PATH`           | No       | Path to the audit log file. Defaults to `./reacher-audit.log`.                                                                                                              |
    | `SSH_BLOCKED_COMMANDS`     | No       | Comma-separated list of commands to block from `ssh_exec` (e.g. `rm -rf /,shutdown,reboot`).                                                                                |
    | `SSH_ALLOWED_DIRS`         | No       | Comma-separated list of directories SSH operations are restricted to. Empty means no restriction.                                                                           |
    | `FETCH_EXTERNAL_TOKEN_MAP` | No       | JSON mapping of domain → env var name for automatic token injection (e.g. `{"api.github.com":"GITHUB_TOKEN"}`).                                                             |
    | `BROWSER_CDP_HOST`         | No       | Host of the CDP-compatible browser. Defaults to `127.0.0.1`.                                                                                                                |
    | `BROWSER_CDP_PORT`         | No       | Port of the CDP-compatible browser. Defaults to `9222`.                                                                                                                     |

    <Note>
      `MCP_SECRET`, `TAILSCALE_API_KEY`, and `GITHUB_TOKEN` are validated at startup. The server will refuse to start if any of these are missing.
    </Note>

    **Optional: edit `reacher.config.yaml`**

    The YAML config controls safety settings. The defaults are reasonable, but you can tighten them:

    ```yaml theme={null}
    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.
  </Step>

  <Step title="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:

    ```bash theme={null}
    sudo tailscale up --ssh
    ```

    Then verify SSH access works from your server:

    ```bash theme={null}
    ssh user@device-hostname
    ```

    If that succeeds manually, Reacher will be able to reach it too.

    <Tip>
      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.
    </Tip>
  </Step>

  <Step title="Start the server">
    Choose your runtime:

    <CodeGroup>
      ```bash Docker (recommended) theme={null}
      docker compose up -d
      ```

      ```bash Bare Node theme={null}
      npm install
      node index.js
      ```
    </CodeGroup>

    Check the server started correctly:

    <CodeGroup>
      ```bash Docker theme={null}
      docker compose logs -f reacher
      ```

      ```bash Bare Node theme={null}
      # The server prints a startup message:
      # ✅ MCP Server started on http://localhost:3000
      #    POST http://localhost:3000/mcp
      #    GET  http://localhost:3000/health
      ```
    </CodeGroup>

    <Warning>
      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.
    </Warning>
  </Step>

  <Step title="Verify the server is responding">
    Run a `tools/list` request against the local server to confirm everything is working:

    ```bash theme={null}
    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):

    ```bash theme={null}
    curl "http://localhost:3000/health?token=YOUR_MCP_SECRET"
    ```

    Expected response:

    ```json theme={null}
    { "status": "ok", "timestamp": "...", "dry_run": false }
    ```
  </Step>

  <Step title="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.

    <CodeGroup>
      ```text Caddy theme={null}
      # /etc/caddy/Caddyfile
      mcp.yourdomain.com {
        reverse_proxy localhost:3000
      }
      ```

      ```nginx Nginx theme={null}
      # /etc/nginx/sites-available/reacher
      server {
          listen 80;
          server_name mcp.yourdomain.com;

          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
          }
      }
      ```
    </CodeGroup>

    <Note>
      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.
    </Note>

    After setting up the proxy, confirm it's reachable from the public internet:

    ```bash theme={null}
    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}'
    ```
  </Step>

  <Step title="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](/reacher/connecting-to-claude) for full instructions and troubleshooting.
  </Step>
</Steps>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Connect to Claude" icon="plug" href="/reacher/connecting-to-claude">
    Step-by-step instructions for adding the connector in Claude.ai settings.
  </Card>

  <Card title="Tools reference" icon="wrench" href="/reacher/tools/overview">
    Learn what each tool does and how to use it effectively.
  </Card>

  <Card title="Configuration" icon="sliders" href="/reacher/configuration/environment-variables">
    Full reference for all environment variables and YAML config options.
  </Card>

  <Card title="Safety settings" icon="shield" href="/reacher/operations/safety">
    Configure blocked commands, directory restrictions, and audit logging.
  </Card>
</CardGroup>
