Skip to main content

Rover CLI

rover runs as an MCP server over stdio, or as a one-shot CLI that searches the web for URLs and fetches them as clean Markdown on stdout. The remaining subcommands inspect or maintain what those produce: the cache, background tasks, config, and local models.

Synopsis:

rover [--config <path>] <subcommand> [args]

Global flags:

FlagDescription
--config <path>Load this TOML file for the invocation. The file must exist.

Every subcommand — search, fetch, mcp, cache, task, batch, doctor, meta, and config show / set — resolves the same config file. With --config <path>, Rover loads that file and errors if it is missing. Without it: if ROVER_CONFIG is set to a non-empty value, Rover treats it exactly like --config — that file must exist and parse, or Rover fails loudly (an explicit redirect never silently falls back). With ROVER_CONFIG unset or empty, Rover searches the platform config file (~/.config/rover/rover.toml on Linux/macOS) then a project-local ./rover.toml, loading the first that exists; if none exists, built-in defaults apply. A file written by rover config set is therefore picked up by rover fetch and rover mcp without passing --config.

rover config set ssrf.level loopback # writes ~/.config/rover/rover.toml
rover fetch http://127.0.0.1:8080/ # honors it — no --config needed

See Configuration for the full key reference.

Subcommands:

  • search <query> finds candidate URLs. Discovery only — it never fetches them.
  • fetch <url> runs a one-shot fetch and prints Markdown plus frontmatter to stdout.
  • mcp starts the MCP server over stdio.
  • cache <list|get|purge|stats> runs cache operations.
  • task <id> inspects or monitors a long-running task.
  • batch <id> inspects or monitors a batch_fetch task (alias for task with a kind check).
  • doctor runs environment diagnostics.
  • config <show|set> inspects or updates the config file.
  • meta <use|hook> wires Rover into an agent harness, or runs the hook handler that wiring installs.
  • model <download|list|remove|verify> manages the local model cache (feature-gated).

Exit code is 0 on success and 1 on any failure: config parse error, fetch error, doctor check failure, and so on. doctor is the one subcommand whose exit code is a verdict; see its section below.

rover search <query> [-n|--count <N>] [--offset <N>]
[--country <CC>] [--language <LANG>] [--ui-language <LANG>]
[--safe-search <off|moderate|strict>]
[--freshness <day|week|month|year|YYYY-MM-DD..YYYY-MM-DD>]
[--extra-snippets] [--no-spellcheck] [--fetch-metadata] [--enrichment]
[--site <DOMAIN>]... [--exclude-site <DOMAIN>]... [--goggle <URL_OR_DEF>]...
[--format <human|json>]

Finds candidate URLs and prints them. It does not fetch them — pick the ones worth reading and pass those to rover fetch. Needs the web-search Cargo feature (in every prebuilt binary) and an API key; see Web search.

FlagDefaultDescription
-n, --count <N>[search] count (10)Results, 1–20.
--offset <N>0Page index, 0–9. Each page is a separate billable request.
--country <CC>[search] countryTwo-letter code results are drawn from, or ALL.
--language <LANG>[search] languageContent language, e.g. en, pt-br.
--ui-language <LANG>[search] ui_languageLanguage of provider-generated metadata.
--safe-search[search] safe_searchoff, moderate, or strict.
--freshnessnoneday, week, month, year, or 2024-01-01..2024-06-30.
--extra-snippetsoffUp to 5 additional excerpts per result.
--no-spellcheckoffSearch the query verbatim.
--fetch-metadataoffInclude the provider's crawl timestamps.
--enrichmentoffInclude the provider's structured extras verbatim. Verbose.
--site <DOMAIN>—Restrict to a domain. Repeatable; composed as site:.
--exclude-site <DOMAIN>—Drop a domain. Repeatable; composed as NOT site:.
--goggle <URL_OR_DEF>[search] gogglesCustom ranking rule. Repeatable, max 3.
--formathumanhuman prints a ranked list; json prints the MCP envelope.

Search operators work inside the query itself: "exact phrase", -excluded, site:, filetype:, intitle:, inbody:, lang:, loc:, and uppercase AND/OR/NOT.

rover search "rust async trait"
rover search "async trait" --site docs.rs -n 5
rover search "rust release notes" --freshness week
rover search "python asyncio filetype:pdf"

human output leads with the trust banner — titles and snippets are third-party content and are guarded exactly as an MCP response is — then lists rank, title, URL, snippet, and a facts line (age · host · language · schema types), and closes with the result count, whether more pages exist, related queries, and the rover fetch handoff.

--format json emits the same envelope the MCP search tool returns, so a pipeline and an agent read one contract:

rover search "tokio runtime" --format json | jq -r '.results[].url'
rover search "tokio runtime" --format json | jq -r '.results[0].url' | xargs rover fetch

Exit code is 0 for a successful search including one with no results, and 1 for a missing credential, a rejected argument, or a provider failure.

rover fetch​

rover fetch <url> [--force-refresh] [--ignore-robots]
[--user-agent <UA>] [--timeout-secs <N>]
[--rate-limit-rpm <N>] [--per-host-concurrency <N>]
[--global-concurrency <N>] [--max-retries <N>]
[--max-tokens <N>] [--summarize <JSON>]

Fetches <url> through the cache-aware orchestrator (fetch_with_cache), runs the extraction pipeline, and prints a frontmatter-wrapped Markdown document to stdout.

FlagTypeDefaultDescription
--force-refreshbooloffBypass the cache and re-fetch from origin.
--ignore-robotsbooloffSkip the robots.txt gate for this fetch. CLI-only escape hatch.
--user-agent <UA>stringfrom [fetch]Override user_agent for this request (UA header, redirects, image sub-fetches, robots matching).
--timeout-secs <N>u64from [fetch]Override timeout_secs (per-request timeout). Must be > 0.
--rate-limit-rpm <N>u32from [rate_limit]Override requests_per_minute_per_domain.
--per-host-concurrency <N>u32from [rate_limit]Override per_domain_concurrency. Clamped to >= 1.
--global-concurrency <N>u32from [rate_limit]Override global_concurrency. Clamped to >= 1.
--max-retries <N>u8from [rate_limit]Override max_retries.
--max-tokens <N>usizeunsetToken budget. Auto-summarises the extracted body toward N when it runs over.
--summarize <JSON>stringunsetExplicit summarise blob, applied before --max-tokens.

--max-tokens is a target, not a ceiling. When the extracted body runs over N, Rover summarises it toward the budget through the configured [summarization] backend, which defaults to the offline extractive one. The result is best-effort. The extractive backend budgets by summing per-sentence token counts, so the joined summary can land a few tokens over N. It emits the summary anyway and does not error. The MCP fetch tool's max_tokens behaves differently: it's a single-shot hard ceiling that can return max_tokens_exceeded. See Managing token budgets.

--summarize runs first. Pass a JSON blob with the same shape as the MCP summarize tool's args minus url, for example:

rover fetch https://example.com/long-report \
--summarize '{"mode":"abstractive","target_tokens":500}'

The body is replaced with the summary, then --max-tokens applies on top if set. Both flags run the configured [summarization] backend. See Summarising pages.

rover mcp​

rover mcp [--ignore-robots]
[--rate-limit-rpm <N>] [--per-host-concurrency <N>]
[--global-concurrency <N>] [--max-retries <N>]
[--http [--bind <ADDR>]]

Starts the MCP server over stdio. Takes the same --rate-limit-* and --ignore-robots overrides as fetch, applied for the lifetime of the server rather than a single request. Loads the resolved default config (or --config <path>) at startup, like every other subcommand. See MCP tools for the tool surface it exposes.

rover mcp --http​

Serve MCP over Streamable HTTP instead of stdio, so multiple agents can share one running instance and its cache.

FlagDefaultDescription
--httpoffSelects the HTTP transport. Not feature-gated — every build has it.
--bind <ADDR>[http] bindRequires --http. Overrides ROVER_HTTP_BIND and [http] bind.

Bind resolution: --bind → ROVER_HTTP_BIND → [http] bind → 127.0.0.1:7683.

EndpointBehaviour
POST /mcpThe JSON-RPC endpoint. Stateless, JSON responses only — Rover sends no server-initiated messages, so SSE framing has nothing to carry.
GET /healthzLiveness. 200 with the running Rover version.
GET /readyzReadiness. Opens the cache database; 200 if it answers, 503 if it doesn't.

Neither health endpoint sits behind ROVER_HTTP_TOKEN. See [http] for the config block and Deployment for the container setup, request-size limits, and the security posture of running this exposed on a network.

POST /mcp requires Content-Type: application/json and an Accept header listing both application/json and text/event-stream — send Accept: application/json, text/event-stream even though only JSON ever comes back; rmcp 406s a request that advertises only one.

Status codes, beyond the 200/503 above, are all transport-level — a tool-call failure is a JSON-RPC error inside a 200, never one of these:

StatusCause
401Missing or wrong bearer token.
403Host or Origin failed validation.
405GET or DELETE /mcp — only POST is served.
406Accept doesn't list both application/json and text/event-stream.
413Body over 16 MiB with a Content-Length header present. See Deployment for the no-Content-Length case, which isn't 413.
415Content-Type isn't application/json, or the body fails to deserialise.

With ROVER_HTTP_TOKEN set, every unrouted path — including /healthz/ or /readyz/ with a trailing slash, which are different, unrouted paths, not the same endpoint with looser matching — returns 401, not 404: the auth layer wraps the fallback too. Without a token, unrouted paths 404 normally.

rover cache​

rover cache list [--limit <N>] [--offset <N>]
rover cache get <url>
rover cache purge <pattern> [--all]
rover cache stats

Operations against the cache database. See Caching & freshness for TTL and revalidation behaviour.

SubcommandDescription
listList cached URLs, most recent first. --limit defaults to 20, --offset to 0.
get <url>Print the cached Markdown body for <url>.
purge <pattern>Delete cache entries whose URL matches the glob (*, ?). The pattern * requires --all as a safety interlock.
statsPrint cache size, entry count, and expired-entry count.

A bare rover cache purge '*' would wipe the whole cache, so it refuses to run without --all. Get the glob wrong without that interlock and you pay to fetch every page again.

rover task​

rover task <id> [--monitor] [--cancel]
[--format human|ndjson] [--from-event <N>]

Reads tasks and task_events from the cache database. Read-only except for --cancel.

FlagDefaultDescription
--monitoroffStream task events as they're appended. Combine with --from-event to resume.
--canceloffSet the task's cancellation_requested flag, a single UPDATE.
--format <fmt>humanhuman prints one line per event; ndjson emits one JSON object per line.
--from-event <N>unsetStart streaming after this event id; use with --monitor.

rover batch​

rover batch <id> [--monitor] [--cancel]
[--format human|ndjson] [--from-event <N>]

Same flags and semantics as rover task, with one guard: the loaded task's kind must be batch_fetch. Point it at a non-batch id and it errors rather than guessing. See Batch & background tasks.

rover doctor​

rover doctor [--format human|ndjson]

Runs the diagnostic battery sequentially, cheap checks first. The always-run checks:

  1. sqlite_open: cache database opens cleanly.
  2. sqlite_wal_mode: WAL journal mode active.
  3. sqlite_schema_version: schema version matches the binary.
  4. output_dir_writable: output.dir (or its default) is writable.
  5. network_reachable: HEAD https://example.com succeeds.
  6. extractive_synthesis: the extractive backend produces output on a fixed input.
  7. backends_authenticate: every cloud [backends.*] block authenticates.
  8. captioners_authenticate: every configured image captioner authenticates.
  9. web_search: whether web search is compiled in and credentialed.

web_search is a status report, not a health check. It reports skip both when the web-search feature is absent and when it is present but no API key is configured — neither makes an otherwise healthy install unhealthy, because Rover fetches perfectly well without search. It deliberately makes no request to the search provider: doctor is run casually and repeatedly, and a connectivity probe that quietly costs money on every invocation is the wrong default. To verify a key end to end, spend one request on purpose with rover search "rover mcp" -n 1. See Web search.

Feature-gated checks appear only when the matching feature is compiled in: the headless browser launch check (headless), and the local model cache and integrity checks (local-inference / injection-model). See Optional features for the feature matrix.

FlagDefaultDescription
--format <fmt>humanhuman: one line per check with ✓ / ✗ / - markers and a summary footer (all checks ok / one or more checks failed). ndjson: one {check, status, detail?} JSON object per line.

The exit code is the verdict. It's 0 iff no check failed, and 1 otherwise. A skip is non-failing, so a feature you didn't compile in won't fail the run. That makes rover doctor safe to drop straight into CI.

rover config show​

rover config show

Prints the effective configuration as TOML, every leaf annotated with its source: defaults, file, or env. Each comment includes the full dotted key, so grep ssrf.level against the output matches the right line.

Example output:

# rover effective configuration
# defaults | file (~/.config/rover/rover.toml) | env

[cache]
default_ttl = "1h" # from: defaults (cache.default_ttl)
min_ttl = "5m" # from: defaults (cache.min_ttl)
...

rover config set​

rover config set <dotted.key> <value>

Edits the config file in place, creating the parent directory and the file itself if missing. For keys that already exist, it preserves comments and key ordering; new keys are appended at the bottom of the matching [section]. Prints ✓ <key> = <value> (wrote <path>) on success.

Settable keys:

  • fetch.user_agent, fetch.timeout_secs
  • cache.default_ttl, cache.min_ttl, cache.max_ttl, cache.override_no_store, cache.store_raw_html
  • ssrf.level, ssrf.project_root
  • rate_limit.requests_per_minute_per_domain, rate_limit.per_domain_concurrency, rate_limit.global_concurrency, rate_limit.max_retries
  • robots.respect, robots.default_ttl, robots.failure_ttl
  • summarization.default_backend, summarization.default_mode, summarization.default_style, summarization.fallback_to_extractive
  • summarization.tables.target_tokens, summarization.tables.focus
  • tokenizer.default
  • output.dir
  • mcp.heartbeat_interval, mcp.reap_threshold
  • debug.log_level, debug.har_path, debug.har_body_cap
  • headless.max_concurrent, headless.chrome_executable
  • image_captions.default, image_captions.max_tokens, image_captions.max_per_page, image_captions.min_width, image_captions.min_height, image_captions.max_bytes
  • image_captions.cache.enabled, image_captions.cache.ttl, image_captions.cache.restrict_to, image_captions.cache.store_raw_image
  • http.bind, http.allow_server_paths
  • search.api_key_env, search.base_url, search.count, search.country, search.language, search.ui_language, search.safe_search
  • search.extra_snippets, search.spellcheck, search.include_fetch_metadata, search.enrichment
  • search.timeout_secs, search.max_retries, search.requests_per_minute, search.retry_after_ceiling

There is deliberately no settable key holding an API token. search.api_key_env names the environment variable Rover reads the Brave key from, so rover config set cannot write a credential to disk. (search.goggles is a list, which config set does not handle — edit the file directly.)

Examples:

rover config set fetch.timeout_secs 30
rover config set ssrf.level project
rover config set ssrf.project_root /Users/me/code
rover config set cache.store_raw_html true
rover config set image_captions.default cloud
rover config set headless.max_concurrent 8
rover config set search.count 5
rover config set search.country GB
rover config set search.safe_search strict

rover meta​

rover meta use <claude|general> [-s|--scope <local|user|project>]
rover meta hook <claude|general>

Wires Rover into an agent harness, or runs the hook handler that wiring installs.

meta reads the config file, because the steering it generates is capability-aware: it only teaches agents the search tool when this build has the web-search feature and an API key is present. Otherwise it writes the previous guidance — use your harness's built-in web search to find URLs, then read them with Rover — and prints a note saying why, and what would change it. The installed Claude Code hooks re-evaluate on every session, so exporting a key later is enough; only the on-disk CLAUDE.md / AGENTS.md block is frozen at install time and needs a re-run. See Web search.

rover meta use​

rover meta use <harness> [-s|--scope <local|user|project>]

Registers Rover with a harness in one step: MCP server, steering hooks (where supported), and a rules-file block. It is idempotent and validate-then-apply: it parses every file it would touch first, and aborts without writing anything if a prerequisite is missing or a target file is malformed JSON. A per-file summary prints to stderr on success.

ArgumentValuesDefaultDescription
<harness>claude, general—Target harness.
-s, --scope <scope>local, user, projectlocalWhere config is written. general ignores it and always uses the project root.

claude delegates MCP registration to the claude CLI (claude mcp add rover -s <scope> -- rover mcp, skipped if already registered), installs a SessionStart hook (matched to startup|clear|compact, so it re-runs on every session entry) and a WebFetch-matched PreToolUse hook that both run rover meta hook claude, and writes a rules block into CLAUDE.md. It needs the claude binary on PATH. Scope decides the files:

ScopeHooks fileRules block
local.claude/settings.local.jsonnone (SessionStart hook carries it)
project.claude/settings.json./CLAUDE.md
user~/.claude/settings.json~/.claude/CLAUDE.md

general writes a project-root mcp.json (adding a rover server, preserving any others) and a rules block in AGENTS.md. It installs no hooks. Both files are updated in place on re-run.

See Quickstart for the full walkthrough, the per-scope file mapping, and the exact content each piece writes.

rover meta hook​

rover meta hook <harness>

The runtime hook handler that the claude wiring points at. It reads a hook payload on stdin, branches on hook_event_name, and prints response JSON for SessionStart (an <EXTREMELY_IMPORTANT_TOOL_UPDATE>-wrapped briefing with concrete tool-call examples) and PreToolUse (a non-blocking reminder, with no permissionDecision); any other event prints nothing. rover meta hook general errors, since general installs no hooks. You don't normally run this by hand; Claude Code invokes it per the settings.json entries that rover meta use claude writes.

rover model​

rover model download <repo_id>
rover model list
rover model remove <repo_id>
rover model verify [<repo_id>]

Download, list, remove, and verify cached local models from HuggingFace Hub. Gated by the local-inference feature at compile time; without it, the subcommand is absent.

Models live under $HF_HOME/hub/ (default ~/.cache/huggingface/hub/).

rover model download​

rover model download <repo_id>

Downloads a model ahead of time. Shows per-file progress to stderr and finishes with a confirmation line.

Example output:

downloading Qwen/Qwen3.5-0.8B from HuggingFace…
config.json 4 KB / 4 KB
tokenizer.json 11 MB / 11 MB
model.safetensors 1.6 GB / 1.6 GB
✓ cached at ~/.cache/huggingface/hub/models--Qwen--Qwen3.5-0.8B

rover model list​

rover model list

Lists all cached models with their disk sizes.

Example output:

~/.cache/huggingface/hub
Qwen/Qwen3.5-0.8B 1.6 GB
Qwen/Qwen3-4B 8.1 GB

rover model remove​

rover model remove <repo_id>

Removes a cached model and frees the disk it held. Returns a confirmation with the freed size.

Example output:

removed ~/.cache/huggingface/hub/models--Qwen--Qwen3.5-0.8B (1.6 GB freed)

rover model verify​

rover model verify [<repo_id>]

Re-hashes cached model files and compares them against the integrity manifest (.rover-integrity.toml) recorded at download time. Pass a <repo_id> to verify that one model; omit it to verify every cached model. Exits non-zero if any file has been modified or is missing. See Security & threat model §"Local model files" for the full integrity model.

Example output:

OK Qwen/Qwen3.5-0.8B (4 files, revision a1b2c3d)
FAIL Qwen/Qwen3-4B (revision e4f5a6b)
model.safetensors: modified (expected sha256:…, got sha256:…)

The same check runs automatically before any local model is loaded for inference, and the local_model_integrity check in rover doctor reports the same status. To bypass it, use --unsafe-disable-model-integrity-check (or ROVER_UNSAFE_DISABLE_MODEL_INTEGRITY_CHECK=1), a security-sensitive escape hatch that logs a warning at startup.

note

Gated by local-inference. When it is not compiled in, rover model --help returns an unrecognized-subcommand error.