Get going.
Three commands to get recon wired into your editor and serving symbol-aware tools to your AI agent. The rest of this page is a complete reference for every CLI command — pick what you need.
01Quickstart
One-time setup per machine, then per project.
# 1. install (Linux / macOS) curl -fsSL https://mcprecon.pages.dev/install.sh | bash # 2. authenticate (caches license globally at ~/.config/recon/) recon login sk-recon-your-key # 3. in each project: index + write the IDE's MCP config cd your-project recon init --mcp cc # Claude Code
Restart your IDE. The 20 code_* tools are now available. recon serve is auto-spawned over stdio — you never run it by hand.
Run recon doctor. If everything's green, you're done. If anything's red, the Troubleshooting section maps every failure to a fix.
02Install
recon ships as a single small Rust binary. The installer verifies a SHA-256 manifest and (when cosign is on the machine) the manifest's sigstore signature against our GitHub Actions release workflow.
# Linux · macOS curl -fsSL https://mcprecon.pages.dev/install.sh | bash # Windows (PowerShell) iwr https://mcprecon.pages.dev/install.ps1 | iex # pin a specific version VERSION=v0.5.7 curl -fsSL https://mcprecon.pages.dev/install.sh | bash # upgrade in place (lands the latest published release) recon update
03IDE setup
recon init --mcp <ide> indexes the repo, writes the IDE's MCP config, and drops a strict-policy agent rules file so the agent uses recon's code_* tools instead of Read/Grep/Glob.
| IDE | Flag | MCP config written to | Agent rules written to |
|---|---|---|---|
| Claude Code | --mcp cc | ./.mcp.json | ./CLAUDE.md (appended) |
| OpenCode | --mcp oc | ./opencode.jsonc | ./AGENTS.md (created or appended) |
| Cursor | --mcp cursor | ./.cursor/mcp.json | ./.cursor/rules/recon.mdc |
| Windsurf | --mcp windsurf | ~/.codeium/windsurf/mcp_config.json | ./.windsurf/rules/recon.md |
| Kiro | --mcp kiro | ./.kiro/settings/mcp.json | ./AGENTS.md (created or appended) |
| Zed | --mcp zed | ./.zed/mcp.json | ./.zed/rules/recon.md |
| VS Code | --mcp vscode | ./.vscode/mcp.json | ./.vscode/rules/recon.md |
| Cline | --mcp cline | ./.cline/mcp_settings.json | ./.cline/rules/recon.md |
| Roo Code | --mcp roo | ./.roo/mcp.json | ./.roo/rules/recon.md |
| OpenAI Codex | --mcp codex | ~/.codex/config.toml | ./AGENTS.md (created or appended) |
The agent rules file is the difference between "agent uses recon when it remembers" and "agent uses recon by default." It's a strict policy: Read/Grep/Glob on source files require explicit user permission and an explanation.
Reverse the wiring
# the symmetric inverse of `recon init --mcp cc`
recon purge --mcp cc
Removes the local index (.recon/), the IDE's MCP entry (preserves any other servers in the config), the agent rules block, and frees the server-side repo slot. recon purge with no flag deletes only the index.
Global flags. Two flags are available on every subcommand:
--repo <path>— repository root (default:.). A no-op on account-only commands likelogin,logout,license,update,version; meaningful for everything that touches a project (init,purge,doctor,serve,index,reindex,stats, and the direct-query commands).--json— emit a JSON envelope instead of formatted text. Pipe intojqor a script.recon doctoralso accepts a sub-flag--jsonfor the same purpose; either form works.
04CLI · Account
Commands that talk to your recon account on the worker. All of them need recon login to have run at least once on this machine.
recon login <key>
Validate an API key against the recon worker, persist a signed license cache, and store the raw key for future calls (chmod 0600 on Unix).
$ recon login sk-recon-XXXXXXXXXXXXXX ✓ Authenticated — Pro tier (5 repos, 10000 files, 1000K LOC) License cached at /home/me/.config/recon/license.json (TTL 24h) Credentials saved to /home/me/.config/recon/credentials.json (mode 0600)
Behind a corporate proxy? Set RECON_API_URL=<your-staging-worker> to override the default endpoint.
recon logout
Delete the cached license and the credentials file. recon serve will refuse to start until you log in again.
$ recon logout Removed /home/me/.config/recon/license.json Removed /home/me/.config/recon/credentials.json
recon license
Show the currently cached license — tier, limits, expiry, source (remote vs cached).
$ recon license Tier: Pro Max repos: 5 Max files: 10000 Max LOC: 1000000 Expires: 2026-12-31 Source: cache (refreshed 11h ago)
recon repos list
List the repos this account has registered with the worker. As of v0.2.0, max_repos is enforced server-side: each recon init POSTs the repo's canonical-path SHA-256 fingerprint to the worker, and registrations beyond your tier limit are rejected.
$ recon repos list 3 repo(s) registered (3/5 on Pro) a3f1c8b2e6d4710f first_seen=2026-04-22T10:14:33Z last_seen=2026-04-25T07:11:09Z e7d2a9c44e10bb22 first_seen=2026-04-23T09:01:18Z last_seen=2026-04-24T19:42:00Z 19f0c7884c2b8e6f first_seen=2026-04-25T07:14:41Z last_seen=2026-04-25T07:14:41Z
recon repos remove <target>
Free a slot. target may be either a path (the CLI canonicalizes and hashes it) or a 64-char fingerprint pasted from recon repos list. Paths to deleted directories work — fingerprinting falls back to the verbatim string when canonicalize fails.
$ recon repos remove /home/me/old-project Removed a3f1c8b2e6d4710f from your account $ recon repos remove a3f1c8b2e6d4710f9c2e6d4710fc8b2e6d4710fc8b2e6d4710fc8b2e6d4710f # also accepts the full fingerprint
Moving or renaming a repo directory burns a slot — the fingerprint is path-based, so the new location produces a new fingerprint. Run recon repos remove <old-path> first, then recon init at the new location.
recon savings push v0.3.2+ · Pro/Team
Push today's local token-savings rollup to the dashboard at mcprecon.pages.dev/dashboard. Aggregates the per-tool counters in .recon/recon.db (populated by every MCP tool call) into one row keyed on (user_id, today_UTC) and POSTs to /v1/account/savings. Repeat runs on the same day are idempotent — the worker MAX-merges, so a stale snapshot cannot regress an already-stored counter and double-counting is impossible.
Free tier returns a clean upgrade message (HTTP 402) rather than an error. The CLI surfaces it as plain text — no stack trace.
$ recon savings push
Pushed savings for 2026-04-29 (Pro tier · recorded status)
calls=312 · response_tokens=47000 · baseline=2850000 · saved=2803000
# Free tier
$ recon savings push
Error: Savings dashboard is a Pro/Team feature. Upgrade at
https://mcprecon.pages.dev/pricing
recon savings show v0.3.2+
Print local counters as TSV, no network. Reads telemetry straight from .recon/index.db — no MCP server needed. The dashboard surfaces the same numbers as daily rollups.
$ recon savings show # tool calls response_tokens baseline tokens_saved avg_latency_ms code_outline 317 63400 951000 887600 4.20 code_repo_map 42 84000 840000 756000 21.30 code_path 18 2200 90000 87800 3.80 ... # total 923 251600 4730000 4478400 -
05CLI · Project
Commands that operate on a single repository. They take an optional --repo <path> (default: the current directory).
recon init [--mcp <ide>]
The setup command. Builds the index, registers the repo with your account, and (with --mcp) writes the IDE's MCP config and agent rules. Idempotent — re-running on the same project refreshes the index without burning a slot.
Flags. --mcp <cc|oc|cursor|windsurf|kiro|zed|vscode|cline|roo|codex> picks the IDE wiring; omit to index only.
What it does, in order. Validate the cached license → register the repo fingerprint with the worker (rejects if you're at max_repos) → walk the tree and build the SQLite + Tantivy index → on --mcp, write the IDE config + strict-policy rules and smoke-test recon serve (4-second window; stderr surfaced if the child exits early) → append .recon/ to .gitignore.
$ recon init --mcp cc Registered repo with recon (new/5 on Pro) Indexed 1834 files, 87421 symbols (0 errors) Wrote /home/me/proj/.mcp.json Wrote recon rules block to /home/me/proj/CLAUDE.md Added .recon/ to .gitignore Done. Restart your IDE to activate recon tools.
If you've hit your tier's max_repos, recon init aborts before indexing and tells you what to do:
$ recon init --mcp cc Pro plan allows 5 repo(s) — limit reached. Your Pro plan allows 5 repos. Free a slot with `recon repos remove <path>` or upgrade your plan. Run `recon repos list` to see what's registered, `recon repos remove <path>` to free a slot, or upgrade at https://mcprecon.pages.dev/pricing
recon purge [--mcp <ide>]
Reverse recon init. Without flags it removes only the local .recon/ directory. With --mcp <ide> it also strips the IDE's MCP entry (preserving sibling servers), removes the agent rules block, and frees the server-side repo slot.
$ recon purge --mcp cc Purged /home/me/proj/.recon Removed recon entry from /home/me/proj/.mcp.json Removed recon rules block from /home/me/proj/CLAUDE.md Released local repo cache entry Released server-side repo slot
recon doctor [--json]
Run a battery of probes covering every surface a stuck install can fail on. Exit code is 1 if anything's broken — wire it into CI to catch regressions.
$ recon doctor recon doctor — 0.5.7 (x86_64-unknown-linux-gnu) repo: /home/me/proj [OK] binary recon 0.5.7 (x86_64-unknown-linux-gnu) [OK] repo dir /home/me/proj writable [OK] global config /home/me/.config/recon writable [OK] license Pro (expires unix=1798761600) [OK] credentials present, mode 0600 [OK] worker /v1/health reachable [OK] worker repo list 3/5 on Pro [OK] index 1834 files, 87421 symbols [OK] cc mcp /home/me/proj/.mcp.json → recon entry present [WARN] oc mcp /home/me/proj/opencode.jsonc not present [WARN] cursor mcp /home/me/proj/.cursor/mcp.json not present [OK] CLAUDE.md rules /home/me/proj/CLAUDE.md → recon block present [WARN] AGENTS.md rules /home/me/proj/AGENTS.md not present Summary: 9 ok, 3 warn, 0 fail
This sample is abridged; recon doctor checks the supported MCP wiring targets and agent-rule files, so unconfigured targets show up as WARN rather than FAIL.
--json emits the same data as a structured envelope — feed it to jq or pipe it into a monitoring script. The probes that aren't your IDE turning into WARN is expected; only FAIL blocks your install.
recon update [--check] [--force]
Self-upgrade to the latest published release. Verifies the SHA-256 manifest and (with cosign present) the sigstore signature before swapping the binary.
$ recon update --check Latest: v0.5.7 (you're on v0.5.6) $ recon update Downloading recon-x86_64-unknown-linux-gnu.tar.gz... Verifying signature... OK Replacing /usr/local/bin/recon... OK Now on v0.5.7.
After the binary swap, restart any running MCP/IDE session so it loads the new executable. If the release changes indexing behavior, run recon reindex in existing repos.
Passive update notices. Every CLI command (except serve, update, and doctor --json) checks for a newer release in the background and prints a one-line stderr hint when one is available. The check result is cached for 24 hours in ~/.config/recon/update-check.json; no network request is made while the cache is fresh. The notice is suppressed when --json output is active.
recon version
Print the binary's semver tag. Identical to recon --version; provided for shell scripts that want a stable subcommand.
06CLI · Server
recon serve [-p <port>] [--host <addr>] [--log <level>]
Start the MCP server. With no flags it speaks JSON-RPC over stdio — the form your IDE auto-spawns. --port/-p switches to Streamable HTTP for browser-based MCP clients or remote use.
Flags.
-p, --port <n>— bind a TCP port for the Streamable HTTP transport. Omit for stdio.--host <addr>— bind address for HTTP transport (default127.0.0.1). The HTTP transport has no built-in authentication and ships with an allowed-hosts filter that accepts onlylocalhost/127.0.0.1/::1in the request'sHostheader — i.e. the server is reachable from the same machine even if you bind0.0.0.0.--log <level>—error,warn,info(default),debug,trace.
# stdio (your IDE auto-spawns this — you never run it manually) recon serve # Streamable HTTP on localhost:7777, debug logging recon serve --port 7777 --log debug
All logging goes to stderr. A stray println! anywhere in the server breaks the stdio JSON-RPC channel silently. CI guards this with a subprocess test.
recon index
Index the repo at --repo (default .) without starting the server and without touching IDE configs. Same path recon init takes for the indexer step, just isolated. Useful for:
- seeding the index from a CI step before the IDE first boots,
- warming the index on a fresh clone so the agent's first query isn't a cold-start,
- scripted batch indexing (loop over a directory of repos before opening any of them).
Incremental — second runs use the Merkle snapshot at .recon/snapshot and only re-parse changed files.
recon reindex
Force a full re-index, discarding the Merkle snapshot. Use when the index is corrupt, after a major upgrade that bumps the schema, or when symbols seem stale despite the live watcher.
Incremental reindex detects changes to ignore_patterns in .recon/config.toml. Paths that are newly ignored are cascade-deleted from the index (symbols, references, and Tantivy docs), preventing stale entries from appearing in search results or the repo map.
$ recon reindex Cleared snapshot Indexed 1834 files, 87421 symbols (0 errors)
recon stats
Print an index health report: repo root, file count, total symbols, Tantivy doc count, plus a global count of all repos this machine has indexed. Pair with --json for monitoring scripts (the JSON envelope additionally exposes schema_version).
$ recon stats Index Health repo /home/me/proj files 1834 symbols 87421 tantivy 87421 Indexed repos (global): 3 $ recon stats --json | jq '.files_indexed, .schema_version' 1834 "4"
07CLI · Direct query
Each MCP tool has a one-to-one CLI mirror. Use these to debug recon, paste output into a chat without the agent in the loop, or wire recon into shell pipelines. All of them respect the global --repo and --json flags.
recon find <name> [-k <kind>] [-l <lang>]
Find symbols by name. Mirrors code_find_symbol. Three-tier matcher: exact SQLite hit → Tantivy BM25 → FTS5 trigram + nucleo fuzzy.
name(positional, required) — symbol name to search. Fuzzy by default.-k, --kind <kind>— restrict to one kind:fn,struct,class,trait,enum,method, etc.-l, --lang <lang>— restrict by language:rs,py,ts,go, …
$ recon find Server --kind struct --lang rust --json | jq '.[] | .qualified_name' "recon_server::server::ReconServer" "recon_server::router::RepoRouter"
recon search <query> [-m <mode>] [-f <filter>]
Search for text patterns in source files. Mirrors code_search.
query(positional, required) — the pattern to search for.-m, --mode <mode>—exact(default),regex, orhybrid(BM25 + text fused via reciprocal rank fusion).-f, --filter <dsl>— narrow with the filter DSL:"*.rs","type:rust","status:modified","!test"(negate), space-separated for AND.
$ recon search "TODO" --filter "*.rs status:modified" | head crates/recon-cli/src/main.rs:1428: // TODO: streaming HTTP transport progress crates/recon-search/src/text.rs:97: // TODO: pluggable scorer $ recon search "fn handle_.*request" --mode regex --filter "!test"
recon outline <path>
Tree-style outline of a file: one line per symbol, kind + name + line number. Mirrors code_outline. ~300–500 tokens for a 500-line file vs. ~5000+ for a full cat.
path(positional, required) — file path relative to the repo root.
$ recon outline crates/recon-cli/src/main.rs crates/recon-cli/src/main.rs struct Cli :23 enum Ide :38 enum Command :152 function validate_license_or_die :347 function smoke_test_serve :374 function main :976
recon skeleton <path> [-d <depth>]
Signatures and docstrings, bodies elided as .... Roughly 10× compression vs. reading the file. Mirrors code_skeleton.
path(positional, required) — file path relative to the repo root.-d, --depth <n>— nesting depth (default2). Bump for deeply nested modules; lower for a tighter view.
$ recon skeleton crates/recon-search/src/text.rs --depth 1
recon symbol <path> <name>
Full source of one symbol with its parent chain and direct caller/callee references. Mirrors code_read_symbol. Use this — not recon outline + manual extraction — when you need the body of a single function or type.
path(positional, required) — file path relative to the repo root.name(positional, required) — symbol name or a line number (recon picks the enclosing symbol).
$ recon symbol crates/recon-cli/src/main.rs smoke_test_serve
$ recon symbol crates/recon-cli/src/main.rs 374
# same — line 374 is inside smoke_test_serve
recon refs <symbol>
Find references (call sites, type uses, imports) of a symbol. Mirrors code_find_refs.
symbol(positional, required) — bare name (ReconServer) or fully-qualified (recon_server::server::ReconServer). Qualified is unambiguous when names collide.
$ recon refs ReconServer $ recon refs recon_server::server::ReconServer --json
recon ls [-g <glob>] [-l <lang>] [-f <filter>]
List indexed source files with language, line count, and top symbols. Mirrors code_list. Replaces find/fd walks for source-file inventory.
-g, --glob <pattern>— glob filter (e.g."crates/recon-cli/**").-l, --lang <lang>— language filter.-f, --filter <dsl>— full filter DSL (see section 09).
$ recon ls --lang rust --filter "status:modified" $ recon ls --glob "crates/recon-server/**" --json | jq '.[] | .path'
recon map [-b <budget>] [-f <path>...]
PageRank-ranked overview of the most important symbols, fitted to a token budget. Mirrors code_repo_map. The first command to run when you (or your agent) lands in an unfamiliar repo.
-b, --budget <n>— max output tokens (default2000).-f, --focus <path>— boost ranking around this file. Repeatable: pass--focusmultiple times to focus on several files.
$ recon map --budget 800 --focus crates/recon-server/src/server.rs $ recon map -b 1500 -f crates/recon-cli/src/main.rs -f crates/recon-server/src/server.rs
recon strings <pattern> [-k <kind>] [-f <filter>]
Search inside string literals and comments only — skips identifiers and keywords, so a hit is always a real natural-language or string match. Mirrors code_find_strings.
pattern(positional, required) — text to search for.-k, --kind <kind>—literal,comment, orboth(default).-f, --filter <dsl>— filter DSL.
$ recon strings "deprecated" --kind comment --filter "*.rs" $ recon strings "https://" --kind literal
recon multi <p1> <p2> ...
Search several patterns in a single index pass. Mirrors code_multi_find. Cheaper than N separate recon search calls when the agent (or you) has a fan-out plan in hand.
patterns(positional, repeated) — two or more patterns, space-separated.
$ recon multi unwrap expect panic!
# single pass; results grouped per pattern
recon query <tool> [<json-args>]
Raw MCP tool call. Useful for testing tools that don't have a dedicated CLI subcommand, or for exercising arg shapes before wiring them into automation.
tool(positional, required) — tool name (e.g.code_find_symbol,code_repo_map).json-args(positional, optional) — tool arguments as a JSON object. Default"{}".
$ recon query code_find_symbol '{"name":"Server","kind":"struct"}'
$ recon query code_repo_map '{"token_budget":500}'
$ recon query code_context '{"symbol":"render_map","token_budget":2000}'
08MCP tools
The 20 tools your AI agent calls when recon is wired up — symbol-and-text primitives, graph-aware traversals, plus multi-repo activation. Each is documented in the agent's tool listing; these are summarised here so you can audit what the agent has access to. Operator-only telemetry (recon stats, recon savings show) lives in the CLI, not on the MCP surface.
…. Replaces Read.The measured badge marks tools whose token-savings are computed per-call against the in-process Read/grep equivalent. Tools without the badge use conservative static estimates (graph/ranking, multi-repo, and reindex have no clean Read/Grep/Glob alternative, so a measured baseline would be synthetic). Both categories feed the same daily rollups on recon savings push and the dashboard.
Tool parameter reference
Every tool accepts a JSON object. Required parameters are marked ✓.
code_outline
| Param | Type | Req | Description |
|---|---|---|---|
path | string | ✓ | File path relative to repo root |
code_skeleton
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
path | string | ✓ | — | File path relative to repo root |
depth | u32 | 2 | Nesting depth to show |
code_read_symbol
| Param | Type | Req | Description |
|---|---|---|---|
path | string | ✓ | File path relative to repo root |
symbol_or_line | string | ✓ | Symbol name or line number |
code_find_symbol
| Param | Type | Req | Description |
|---|---|---|---|
name | string | ✓ | Symbol name (fuzzy matching) |
kind | string | Filter by kind: fn, struct, class, trait, enum, method | |
lang | string | Filter by language |
code_find_refs
| Param | Type | Req | Description |
|---|---|---|---|
symbol | string | ✓ | Symbol name or qualified name |
code_search
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
query | string | ✓ | — | Search query |
mode | string | exact | exact, regex, hybrid, or semantic | |
filter | string | — | Filter DSL expression |
code_list
| Param | Type | Req | Description |
|---|---|---|---|
glob | string | Glob pattern to filter files | |
lang | string | Language filter | |
filter | string | Filter DSL expression |
code_repo_map
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
focus_files | string[] | — | Files to boost in the ranking | |
token_budget | usize | 2000 | Max tokens for the output |
code_find_strings
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
pattern | string | ✓ | — | Pattern to search in string literals/comments |
kind | string | both | literal, comment, or both | |
filter | string | — | Filter DSL expression |
code_multi_find
| Param | Type | Req | Description |
|---|---|---|---|
patterns | string[] | ✓ | Multiple patterns to search simultaneously |
filter | string | Filter DSL expression |
code_reindex
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
force | bool | false | Force full re-index even if hashes match |
code_path
Shortest call-graph path from src to dst. Bidirectional BFS over the cached reference graph; total-visit cap 50,000 nodes. Replaces chained code_find_refs calls.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
src | string | ✓ | — | Source symbol (bare or fully qualified) |
dst | string | ✓ | — | Destination symbol (bare or fully qualified) |
max_hops | u32 | 8 | Maximum hops to explore (hard cap 16) |
code_callers
Transitive callers up to depth rings. Cycle-safe; per-tier fan-out capped at 50; total-visit cap 50,000. Each symbol emitted at its minimum depth only.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
symbol | string | ✓ | — | Symbol name (bare or fully qualified) |
depth | u32 | 1 | Depth rings to explore (hard cap 6) |
code_callees
Transitive callees — what does this symbol call? Mirror of code_callers. Same caps and cycle safety.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
symbol | string | ✓ | — | Symbol name (bare or fully qualified) |
depth | u32 | 1 | Depth rings to explore (hard cap 6) |
code_context
One-shot bundle: signature + body + callers + callees + types + tests. Replaces the canonical 4-call understand-X loop. Drops sections under token pressure in order: tests → callees → types → callers.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
symbol | string | ✓ | — | Symbol name — bare, qualified, or fuzzy |
token_budget | usize | 2000 | Token budget for the bundled response |
code_impact
Blast radius — transitive callers plus tests that exercise the symbol. Use to answer "what might break if I change X?"
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
symbol | string | ✓ | — | Symbol name (bare or fully qualified) |
depth | u32 | 4 | Maximum tier depth (hard cap 6) |
code_subsystems
List the natural subsystems of the repo — weakly-connected components of the reference graph. Sorted by symbol count descending.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
limit | usize | 50 | Maximum subsystems to return |
code_subsystem
Drill into one subsystem (by id from code_subsystems). Returns a skeleton of all symbols in the component.
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
id | u32 | ✓ | — | Subsystem id from code_subsystems |
token_budget | usize | 1500 | Token budget for the subsystem skeleton |
code_activate_repo
Switch the active repository for subsequent tool calls. Tier-limit aware; persists across restarts. Use in multi-repo setups.
| Param | Type | Req | Description |
|---|---|---|---|
path | string | ✓ | Absolute path to the repo to activate |
code_list_repos
List loaded repos with file count, symbol count, and active flag. Pairs with code_activate_repo.
| Param | Type | Req | Description |
|---|---|---|---|
| No parameters | |||
09Filter DSL
Search-shaped tools (code_search, code_find_strings, code_list) and their CLI mirrors accept an optional --filter / filter: argument. The DSL is small on purpose — these are the supported terms:
| Term | Matches | Example |
|---|---|---|
*.rs / *.{ts,tsx} | file extension glob | code_search "retry" filter:"*.rs" |
type:rust | language by name | type:python |
status:modified | git status flag | status:modified or status:staged |
!test | negative path-substring | !test — excludes paths containing "test" |
/src/ | positive path-segment | /crates/recon-server/ |
size:>1mb / size:<10kb | file size comparison | size:>100kb — files larger than 100 KB |
mtime:<2d / mtime:>1w | modification time recency | mtime:<1d — modified in the last day |
Terms compose with whitespace (logical AND):
recon search "TODO" --filter "*.rs status:modified !test" # → TODOs in modified Rust files outside test/ paths recon ls --filter "size:>50kb mtime:<7d type:rust" # → large Rust files modified in the last week
Size units: b, kb, mb, gb (case-insensitive). Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
If the filter string fails to parse, tools return zero results rather than ignoring the filter. This prevents accidental data leakage from a typo in the filter expression.
10Configuration
Per-repo settings live in .recon/config.toml at your project root. The file is local — commit it if you want shared defaults, never your license. Every setting has a conservative default; override only what you need.
# .recon/config.toml # Additional ignore patterns (on top of .gitignore) ignore_patterns = ["*.generated.*", "dist/"] # Max file size to index (default 1 MB) max_file_size = 1048576 # Max file size for embedding pipeline (default 100 KB) max_embed_size = 102400 # File watcher debounce interval in ms (default 250) watcher_debounce_ms = 250 # Tantivy writer heap size (default 50 MB) tantivy_heap_bytes = 50000000 # Max search results per tool call (default 30) max_search_results = 30 # Token budget for code_repo_map (default 2000) default_map_budget = 2000 # Secret redaction — keep on unless you really mean it redact_secrets = true allow_sensitive = false
All options
| Key | Type | Default | Purpose |
|---|---|---|---|
ignore_patterns | string[] | [] | Additional path patterns to ignore (on top of .gitignore) |
max_file_size | u64 | 1048576 | Maximum file size to index (bytes) |
max_embed_size | u64 | 102400 | Maximum file size for embedding pipeline (bytes) |
watcher_debounce_ms | u64 | 250 | File watcher debounce interval (ms) |
tantivy_heap_bytes | usize | 50000000 | Tantivy writer heap size (bytes) |
max_search_results | usize | 30 | Maximum results per search tool call |
default_map_budget | usize | 2000 | Default token budget for code_repo_map |
redact_secrets | bool | true | Scan tool responses for secrets and redact them |
allow_sensitive | bool | false | Allow access to sensitive files (.env, .pem, etc.) |
Product tiers and config are separate concerns. Tiers control account limits (max repos, files, LOC). Config controls local indexing behavior (file-size caps, secret redaction, sensitive-file access). You cannot raise tier limits via config.
Environment variables
Two supported user-facing knobs. Other RECON_* variables exist in the source as internal escape hatches for tests and operations — they're not part of the public API and may be renamed or removed without a deprecation cycle.
| Variable | What it controls |
|---|---|
RECON_LOG_FORMAT | Set to json to emit structured logs on stderr (one JSON object per event). Default is human-readable text. Useful when piping into a log aggregator. |
RECON_NO_EMBED | Set to 1 (or true/yes/on) to disable the embed service entirely. Semantic search (code_search mode=semantic) is skipped and falls back to lexical-only; everything else is unaffected. Use when you don't want any chunk text to leave your machine and don't need a local-only rebuild via --features local-embed. v0.4.2+ |
RECON_API_URL | Override the default worker endpoint. Useful behind a corporate proxy or for staging environments. v0.2.0+ |
RECON_REQUEST_TIMEOUT_SECS | Per-tool request timeout in seconds. Default 30. Increase for very large repos where graph tools may need more time. v0.4.0+ |
11Account & repos
Every recon account has a tier-defined max_repos — the number of distinct repositories one license can index concurrently. As of v0.2.0 the limit is enforced by the worker: each recon init registers the repo's canonical-path SHA-256 fingerprint, and a 403 there blocks indexing rather than a local file the agent could overwrite.
How registration works
- On
recon initthe CLI canonicalises the repo path, SHA-256s the result to a 64-char hex fingerprint, and POSTs it to/v1/account/repos. - The worker accepts atomically — even at limit-1, two concurrent
inits with different fingerprints can't both succeed. - Repeat POSTs (same fingerprint) refresh
last_seen_atand return200 status: refreshedrather than burning a slot. - The worker only sees the digest. Your filesystem layout never leaves the machine.
Managing slots
$ recon repos list 3 repo(s) registered (3/5 on Pro) a3f1c8b2e6d4710f first_seen=2026-04-22T10:14:33Z last_seen=2026-04-25T07:11:09Z ... $ recon repos remove /home/me/old-project Removed a3f1c8b2e6d4710f from your account
Pricing & limits
Tier limits are summarised on the pricing page. recon license shows what your current key entitles you to.
12Troubleshooting
Start with recon doctor. It runs a battery of probes across every surface a stuck install can fail on and returns exit-code 1 when anything's broken. Match the failing probe to the row below:
| Symptom | Cause | Fix |
|---|---|---|
API key rejected: server response signature invalid |
Worker's LICENSE_HMAC_SECRET doesn't match the binary's embedded RECON_LICENSE_HMAC_KEY. Hit during the v0.1.0 release, fixed in v0.1.1. |
Upgrade: recon update |
credentials not found at … — run `recon login` |
v0.2.0 needs the raw API key (not just the cached signed license) so it can call /v1/account/repos. |
recon login <sk-recon-...> once per machine. |
Pro plan allows 5 repo(s) — limit reached |
You're at max_repos. The current repo isn't registered; the worker won't add it. |
recon repos list → recon repos remove <path>, then re-run recon init. Or upgrade your plan. |
No valid license. Run 'recon login <key>' |
License cache missing or older than the 24 h TTL with no network. | Re-validate: recon login <key>. Behind a firewall? Whitelist the worker host. |
could not reach recon worker |
DNS / TCP / TLS to the worker failing. The license cache covers you for 24 h; after that, recon serve hard-fails. |
recon doctor → look at worker /v1/health. Check corporate proxy / firewall. |
schema unreadable — try `recon reindex` |
Index DB schema mismatch (rare; usually means an upgrade interrupted mid-migration). | recon purge then recon init. Index rebuild on a 500K-LOC repo takes ≈ 60 seconds. |
| MCP tools not appearing in your IDE | IDE caches the server list at startup. | Restart the IDE. recon doctor verifies the MCP wiring file points at the current binary. |
| Agent still uses Read/Grep/Glob despite the rules file | Some agents need the rules block to be loaded explicitly. | Confirm recon doctor shows recon block present on the agent's rules file. Restart the agent session so it re-reads the rules. |
tantivy lock errors during indexing |
Two recon processes wrote to the same .recon/tantivy directory. |
Kill stray processes, then rm .recon/tantivy/.tantivy-meta.lock and re-index. Single-writer is enforced; you should never see this in steady state. |
13Security
- Source files stay local. Files are indexed on your machine; recon never uploads source. The license check (key in, tier out) and the embed forwarder (chunk text in, vectors out, see below) are the only network calls.
- Embeddings: hosted by default, opt-out and air-gap supported.
Semantic search uses a Cloudflare worker that forwards chunk text (tens to a few hundred tokens per chunk) to a Modal-hosted Jina v2-base-code embedding model. The worker caches vectors by
sha256(chunk_text)so repeats hit Modal once. We don't log chunk text; we don't persist it. Source files never make this trip — only the chunks the indexer extracts. Two escape hatches:RECON_NO_EMBED=1— disables embedding entirely. Semantic search degrades to lexical-only; everything else is unaffected.cargo build --features local-embed -p recon-cli— rebuilds with offline ONNX inference (fastembed + Jina v2-base-code, ~80 MB binary). Nothing leaves the machine.
- Credentials chmod 0600. The raw API key is stored at
~/.config/recon/credentials.jsonwith mode 0600 on Unix.recon doctorverifies the mode. - Secret redaction. Every tool response that includes file bytes is scanned for AWS keys, OpenAI keys, PEM blocks, and 10+ other patterns before being returned. Override only with explicit
allow_sensitive = true. - Path traversal guard. Every tool call canonicalizes its path argument and prefix-checks against the repo root. Escape attempts return
PathTraversal (-32007). - Sensitive file gate.
.env*,*.pem,*.key,id_rsa*are skipped by default — they don't get indexed or embedded. - Symlink alias protection. v0.5.6+ Paths are canonicalized before the sensitive-file check. A symlink like
safe.rs → .envis resolved to its target and blocked — the raw path name cannot bypass the policy. - Filter parse errors fail closed. v0.5.6+ If a filter DSL string fails to parse, tools return zero results instead of ignoring the filter and returning unfiltered data. This prevents accidental data leakage from typos.
- Stdout hygiene. All logging goes to stderr; a CI test spawns the server and rejects any stray
println!. - Signed binaries. Every release ships a SHA-256 manifest signed via cosign keyless against our GitHub Actions release workflow.
- Worker rate limits fail closed. If the rate-limit binding is missing in production, requests are rejected (fail-closed). In local/test environments, requests pass through (fail-open) for development convenience.
14Performance
Benchmarked on real codebases. Release build, warm cache, mimalloc, fat LTO. Numbers from the bench-real harness in the repo.
Query latency (p50)
| Tool | Zed (80K symbols) | Rust compiler (320K symbols) |
|---|---|---|
code_context | 10 ms | 16 ms |
code_find_symbol | 8 ms | 8 ms |
code_search | 11 ms | 33 ms |
code_outline | 14 ms | 13 ms |
code_skeleton | 11 ms | 11 ms |
code_find_refs | 8 ms | 12 ms |
code_repo_map (cached) | 8 ms | 19 ms |
code_repo_map (cold) | 405 ms | 2.0 s |
| cold index | 19 s | 53 s |
Token reduction (per operation)
| Scenario | Read/Grep/Glob | recon | Reduction |
|---|---|---|---|
| Read one function | ~23,838 tok | ~111 tok | 215× |
| Find a symbol | ~17,500 tok | ~226 tok | 77× |
| Repo orientation | ~52,500 tok | ~2,170 tok | 24× |
| Find references | ~15,000 tok | ~638 tok | 24× |
| Outline a file | ~23,838 tok | ~1,350 tok | 18× |
A typical "find and fix a bug" task: ~3.2K tokens with recon vs ~100K+ with Read/Grep/Glob.
15License
recon is proprietary software. © 2026. All rights reserved. Use is governed by the recon Terms of Service.