Skip to main content
Reacher is configured primarily through environment variables in a .env file at the project root. Copy .env.example to .env and fill in your values before starting the server.
cp .env.example .env
MCP_SECRET, TAILSCALE_API_KEY, and GITHUB_TOKEN are validated at startup. The server will exit immediately if any of these are missing.

Authentication

MCP_SECRET
string
required
Shared secret that Claude.ai sends with every request as a URL query parameter (?token=...). All requests without the correct token are rejected with 401 Unauthorized.Generate a secure value with:
openssl rand -hex 32
Never reuse a token across environments. Treat this like a password.

Tailscale

TAILSCALE_API_KEY
string
required
API key for the Tailscale control plane. Used by tailscale_status to query device list, IP addresses, and online/offline status.Required scope: Devices (read)Create one at login.tailscale.com/admin/settings/keys.

GitHub

GITHUB_TOKEN
string
required
Personal access token for GitHub API calls. Used by:
  • gist_kb — read and write private Gists
  • github_search — search pull requests and commits
  • fetch_external — inject auth on requests to api.github.com (when configured in FETCH_EXTERNAL_TOKEN_MAP)
Required scope: gist (read + write). Add repo if you need to search private repositories.Create one at github.com/settings/tokens.

HTTP proxy

PROXY_ALLOWED_DOMAINS
string
required
Comma-separated list of hostnames that fetch_external is permitted to call. Requests to any domain not in this list are rejected before the HTTP call is made.
PROXY_ALLOWED_DOMAINS=api.github.com,api.linear.app,api.notion.com
Matching is done against the exact hostname parsed from the request URL. See Domain allowlist for a full explanation.
FETCH_EXTERNAL_TOKEN_MAP
string
default:"{}"
JSON object that maps hostnames to environment variable names. When fetch_external makes a request to a matching domain, it reads the named environment variable and injects it as an Authorization: Bearer <token> header.
FETCH_EXTERNAL_TOKEN_MAP={"api.github.com":"GITHUB_TOKEN","api.linear.app":"LINEAR_API_TOKEN"}
The token value never leaves the server — Claude only sees the result of the API call. See Domain allowlist for examples.

SSH safety

SSH_BLOCKED_COMMANDS
string
default:"rm -rf /,shutdown,reboot,mkfs,dd,format"
Comma-separated list of command substrings to block from ssh_exec. Matching is case-insensitive substring matching — if any blocked string appears anywhere in the command, the command is rejected.
SSH_BLOCKED_COMMANDS=rm -rf /,shutdown,reboot,mkfs,dd,format
This variable overrides ssh.blocked_commands in reacher.config.yaml. The same setting can also be managed in the YAML file as a list, which supports comments and easier editing.
SSH_ALLOWED_DIRS
string
default:"(empty — no restriction)"
Comma-separated list of directory prefixes. When set, ssh_exec will only allow commands that operate on paths starting with one of the listed directories.
SSH_ALLOWED_DIRS=/home/deploy,/tmp,/var/log
When empty (the default), there are no directory restrictions. This variable overrides ssh.allowed_dirs in reacher.config.yaml.

Audit logging

AUDIT_ENABLED
string
default:"true"
Set to false to disable audit logging entirely. Any other value (or the absence of this variable) leaves auditing enabled.
AUDIT_ENABLED=false
When enabled, every tool call is written as a JSON line to AUDIT_LOG_PATH. Sensitive keys (anything containing token, password, secret, or key) are stripped from the log entry automatically.This variable overrides audit.enabled in reacher.config.yaml.
AUDIT_LOG_PATH
string
default:"./reacher-audit.log"
Path to the audit log file. Accepts both relative paths (resolved from the project root) and absolute paths.
AUDIT_LOG_PATH=/var/log/reacher/audit.log
The file is created automatically if it does not exist. Entries are appended, so the file grows continuously — rotate it with a tool like logrotate in production.This variable overrides audit.log_path in reacher.config.yaml.

Server

PORT
number
default:"3000"
TCP port that the Express HTTP server listens on.
PORT=8080
When deploying behind a reverse proxy (Caddy, Nginx, Traefik), this is the internal port the proxy forwards to. Claude.ai connects to the public HTTPS URL, not this port directly.
DRY_RUN
string
default:"false"
Set to true to put ssh_exec into dry-run mode. In this mode, ssh_exec logs the command it would have run but does not actually execute it.
DRY_RUN=true
Useful for testing prompts and validating what Claude would do before granting live SSH access. The /health endpoint reports the current dry-run state.This variable overrides dry_run in reacher.config.yaml.

Browser

BROWSER_CDP_HOST
string
default:"127.0.0.1"
Hostname or IP address of the Chrome DevTools Protocol (CDP) compatible browser that the browser tool connects to.
BROWSER_CDP_HOST=127.0.0.1
Requires a running CDP-compatible browser (such as Lightpanda or Chrome with --remote-debugging-port) and the agent-browser CLI installed globally.
BROWSER_CDP_PORT
number
default:"9222"
Port of the CDP-compatible browser.
BROWSER_CDP_PORT=9222

Precedence rules

When the same setting exists in both .env and reacher.config.yaml, the environment variable always wins. This makes it safe to commit a base reacher.config.yaml to version control and override specific values per deployment via environment variables. See reacher.config.yaml reference for full details on the YAML config file.