LeanToken exposes the same retrieval services through its CLI and MCP server. All paths are relative to the configured repository root, and all source responses are bounded.
--root <PATH> Repository root (default: current directory)
--allow-broad-root Allow a filesystem root, home directory, or parent of home
--include-generated Include known generated and package-cache directories
--index-include <PATTERN> Include only matching repository-relative paths (repeatable)
--index-exclude <PATTERN> Exclude matching repository-relative paths (repeatable)
--max-walk-entries <COUNT> Walker entries per discovery (default: 500000)
--max-files <COUNT> Admitted source files (default: 150000)
--max-total-source-bytes <BYTES> Aggregate source bytes (default: 2147483648)
--max-depth <DEPTH> Repository-relative depth (default: 64)
--max-file-bytes <BYTES> Bytes admitted from one file (default: 2097152)
--max-prepare-batch-files <COUNT> Files per preparation batch (default: 256)
--max-prepare-batch-bytes <BYTES> Bytes per preparation batch (default: 67108864)
--max-index-workers <COUNT> Parallel file-preparation workers
--database <PATH> Override the per-repository SQLite cache path
--tokenizer <ENCODING> Source and protocol accounting tokenizer
--json Emit JSON from CLI commands
leantoken index [--rebuild]
leantoken status
leantoken coverage
leantoken savings
leantoken doctor [--client CLIENT] [--ready-timeout-seconds SECONDS]
leantoken files <tree|find|glob> [options] [--consistency <mode>]
leantoken search <query> [options] [--consistency <mode>]
leantoken outline <path>... [--consistency <mode>]
leantoken read <path> [--lines START:END] [--symbol NAME] [--consistency <mode>]
leantoken history <operation> ... [options]
leantoken json <path> [options]
leantoken context --task <text> --budget <tokens> [--consistency <mode>]
leantoken update [--check] [--yes]
leantoken upgrade [--check] [--yes]
leantoken mcp [--result-mode dual|text|structured]
leantoken setup [CLIENT...] [--all] [--refresh] [--private-runtime] [--yes]
[--dry-run] [--allow-outdated] [--force-unmanaged]
leantoken remove [CLIENT...] [--all] [--yes] [--dry-run] [--force-unmanaged]
leantoken runtime list
leantoken runtime prune [--keep-latest COUNT] [--dry-run] [--yes]
leantoken cache list [--summary] [--state STATE] [--repository-root PATH]
[--compatibility CLASS] [--index-content-version VERSION]
[--incompatible-with-current] [--limit COUNT] [--cursor CURSOR]
leantoken cache prune [--older-than DAYS] [--max-total-bytes BYTES]
[--remove-missing-roots] [--incompatible-with-current]
[--dry-run] [--yes]
leantoken episode audit --adapter ADAPTER --input PATH
Use leantoken <command> --help for the complete argument list.
episode audit normalizes one existing, already-redacted analyzer report into
a stable local report. It is repository-free: repository, database, indexing,
and tokenizer options are rejected. The default output is deterministic
Markdown; pass global --json for deterministic compact JSON:
leantoken episode audit \
--adapter multi-agent-suite-v1 \
--input benchmarks/reports/multi-agent-context-suite-v1-codex-0.144.1.json
leantoken --json episode audit \
--adapter mcp-wire-report-v2 \
--input benchmarks/reports/wire-trace-synthetic-v2.jsonAdapters are explicit and versioned:
multi-agent-suite-v1importscodex_multi_agent_suiteaggregates;model-ab-trajectory-v1importsmodel_ab_trajectoryclassifications;mcp-wire-report-v2imports currentmcp_wire_analyzereports;codex-host-receipt-v1imports publishablecodex_host_receiptreports;context-utilization-v1importscontext_utilizationclassifications.
An adapter/schema mismatch fails instead of guessing. The suite adapter
recomputes counts, provider-request means, provider-input comparisons, and
contract violations from complete redacted run samples, then checks the
published aggregates. It therefore reproduces the 60-run v1 mean of 8.2 child
provider requests and 50.9% input regression, and identifies the v2
one-context-plus-optional-search contract without reading private rollouts.
Wire and host adapters share the same normalized output while keeping absent
provider accounting null.
The auditor reads at most 64 MiB and accepts at most 10,000 episodes, 100,000 tool calls, 100,000 events, 100,000 evidence ranges, and 4,096 distinct artifact bindings. JSON input is parsed once and output cardinality is bounded by those limits. Reports contain only normalized counts, fixed classifier descriptions, and BLAKE3/Git bindings. Input paths, task names, prompts, raw source, commands, tool arguments, and tool outputs are not copied. A host receipt whose privacy declaration retains private material is rejected.
Coverage accompanies every nullable count. complete means complete within
the imported analyzer's declared boundary, reported_subset names a
conservative child/tool subset, and unavailable serializes a null value.
Local tokenizer counts never populate provider-native input. Downstream-use
signals remain explicit proxies; absence of a signal is not labeled unused
evidence. The report separately lists all eight v1 avoidable-event classifiers
as exact, proxy, or unavailable, so an unevaluable classifier cannot be
mistaken for an observed zero.
leantoken status reports readiness separately from reconciliation activity.
index_state is uninitialized until the first generation commits and ready
afterward. freshness is current while idle and reconciling while an index
operation is active, so a cold idle repository reports
uninitialized/current. Status is deliberately read-only and reports
working_tree_checked: false; current describes reconciliation activity, not
a filesystem scan. Before the first generation, direct CLI retrieval exits with
guidance to run leantoken index; use leantoken doctor to verify the complete
MCP startup and first-retrieval flow. Status also reports SQLite main/WAL/SHM
bytes, indexed source bytes, their amplification ratio, and current process RSS
when the platform exposes it. RSS is per process, not a claim about all clients
sharing the repository cache. index_content_version identifies the managed
cache compatibility lane used by the current binary; different values use
separate managed cache paths.
leantoken coverage explicitly scans indexed file metadata from one pinned
generation and reports structural parser coverage. It separates recognized
complete, recognized incomplete, and unrecognized files, includes exact source
byte totals, and returns bounded language and safe extension-family groups.
Pass --json for compact machine-readable output. The report does not walk the
working tree, reparse source, or expose repository paths.
By default, LeanToken indexes the complete repository visible through Git,
.ignore, and .leantokenignore rules, except for .git metadata and the
conservative generated-directory policy. An explicit indexing scope reduces
the repository membership built into SQLite:
leantoken --root . \
--index-include 'src/**' \
--index-include 'tests/**' \
--index-exclude 'src/generated/**' \
indexIncludes are optional; with only excludes, every other ignore-visible path is
eligible. Excludes always win. Literal paths select their complete subtree,
while patterns use the same slash-normalized, case-sensitive glob behavior as
retrieval path filters. Patterns must be repository-relative. LeanToken
normalizes separators and redundant . components, sorts and deduplicates the
result, and accepts at most 64 include-plus-exclude patterns, 1,024 bytes per
pattern, and 16 KiB in total.
Scope is an indexing and negative-evidence boundary, not a query-time
convenience filter. Discovery prunes excluded subtrees before traversal limits
and preparation; status reports the normalized patterns and a compact opaque
scope digest. Every retrieval response reports meta.index_scope as full or
scoped and includes the digest for scoped caches. An empty result from a
scoped cache therefore proves absence only inside that configured scope.
Normalized scope participates in the automatically managed cache identity, so
full and scoped indexes for one repository can coexist. Reuse the same scope
arguments on every command that must address that cache. An explicit
--database is bound to both repository and full scope identity and fails
with index_scope_mismatch if reused with another scope.
For a dependency-heavy TileLang checkout, for example, first-party work can exclude the recorded dependency submodules:
leantoken --root . --index-exclude '3rdparty/**' index
leantoken --root . --index-exclude '3rdparty/**' mcpWorkspace-specific MCP registrations can place the repeatable scope flags
before the mcp subcommand in their command arguments. The global setup
flow intentionally remains repository-agnostic and does not infer a scope.
Changing scope selects another managed cache; it never mutates the membership
meaning of an existing cache silently.
After the first generation, the one-shot files, search, outline, read,
and context commands default to --consistency reconcile_working_tree. Each
command completes a non-rebuild reconciliation before opening one committed
snapshot, so edits completed before the command are visible atomically. Use
--consistency indexed_generation when a lower-latency query of the latest
completed snapshot is intentional. Changes written concurrently may require a
later request.
leantoken savings reports persistent repository-local observed token
accounting for complete serialized responses. Search, outline, and context
compare emitted source with whole-file reads of the unique represented files.
Read compares the emitted range with the requested live range before
truncation or suppression.
The nested response_accounting object is the machine-readable retrieval-
compression section and additionally covers files,
context_plan, json, and history. It separates source,
path/metadata, protocol, and total compact-response tokens. JSON uses the
complete input file or files as its represented-source baseline. Operations
without a defensible source baseline still contribute their full response cost,
so their signed estimated_net_tokens_saved value is negative rather than
silently disappearing. Counts are stored separately per configured tokenizer.
The default terminal view separates Retrieval compression from Observed
task savings before the per-operation table. The represented-source ratio is
labeled retrieval-only and is never presented as a task-savings percentage.
Color is used when stdout is a terminal. NO_COLOR or CLICOLOR=0 disables
color, while CLICOLOR_FORCE=1 enables it for compatible redirected output.
Pass --json for the compact JSON representation used by scripts. The existing
response_accounting object owns successful-response retrieval compression;
the additive observed_task_savings object reports task attribution
separately. Without a host task/outcome identity its status is unavailable,
its task delta, rate,
retry, superseded, relevance, and failure-response-token fields remain null,
and the successful responses with unknown relevance and observed failed calls
remain visible as counts. The observations object reports persisted
successful and failed service records, exact expected_hash not-modified
responses, their suppressed represented-source tokens, and a fixed-order
failure breakdown with non-sensitive categories. Its legacy useful
classification means a complete supported protocol response, not relevant or
useful evidence; the human view labels it complete-supported.
Full-response counts include the compact structured response but not tool
discovery, JSON-RPC transport envelopes, provider billing/cache behavior,
native-tool costs, or task/evidence success. Instrumented service failures are
counted separately and never assigned a fabricated token cost. Calls cannot be
grouped into retry chains or tasks because LeanToken receives no host
task/outcome identifier. It also cannot know whether evidence was unused,
irrelevant, or superseded, nor whether a task completed successfully; these
limits are returned explicitly in observations.unobserved.
Accounting is best effort: a busy repository writer skips telemetry rather
than delaying or failing retrieval, so persisted counts are lower bounds.
Whole-file baselines are also unavailable when the selected tokenizer does not
match the indexed tokenizer until reconciliation completes. Source-only
counters from older caches remain visible, but cannot be reconstructed as
historical full-response costs and are therefore excluded from
response_accounting. This is not an audit ledger.
Current index responses retain the aggregate files_skipped count and explain
it with the bounded skip_reasons object: binary,
oversized_during_read, and failed. These counts cover files admitted for
preparation and always sum to files_skipped. Older serialized responses can
omit the object because their breakdown is unknown. No per-file skip list is
returned; bounded failure warnings may still identify files that could not be
read. files_seen counts admitted files plus deletions directly observed from
requested targeted paths. Paths merely omitted by full or visibility discovery
because they are absent or excluded are not part of files_seen,
files_skipped, or the reason counts. An already-indexed omitted path can still
increment files_removed when its stale entry is deleted.
Setup writes only the leantoken entry in each selected global client config.
It manages a concise discovery skill only for the selected host family: Claude
Code uses ~/.claude/skills/leantoken/SKILL.md; Cursor, OpenCode, Codex,
Gemini CLI, and Antigravity use ~/.agents/skills/leantoken/SKILL.md. Hosts
preload only its name and routing description, then load the instructions on
selection; the nine MCP schemas remain deferred. Repeated setup updates only
marker-owned skill copies, removal preserves an unowned file, and partial
client removal retains a shared skill while another managed registration needs
it. JSON setup reports the exact cl100k_base size of one discovery skill as
telemetry; it is not a pass/fail cap on the routing guidance.
New MCP launchers carry a hidden setup ownership marker. Exact-version npx
launchers created by earlier releases and executables below LeanToken's private
runtime root are recognized for migration. A same-name manual registration is
otherwise left untouched and setup fails with its exact path. Review that file,
then pass --force-unmanaged --dry-run to preview the replacement before
applying it explicitly.
When setup runs through npx, the stored command pins
leantoken@<exact current version> and retains --yes so background MCP
startup cannot block on an install prompt. The launcher may contact npm to
resolve or download that exact package, but it cannot switch to a newer version
between restarts.
For regular use, the private native runtime is recommended to avoid retaining npm and Node wrapper processes for every MCP session. It remains explicit so a zero-install setup does not silently add an application-data write:
npx --yes leantoken@0.1.10 setup --codex --private-runtime --dry-run
npx --yes leantoken@0.1.10 setup --codex --private-runtime --yesDry-run reports the exact versioned application-data path and BLAKE3 digest.
Setup copies the native executable already verified by the running package,
activates it with an atomic no-clobber rename, then updates all selected client
registrations as one rollback-capable transaction. A process lock serializes
setup, and a durable journal restores pre-transaction contents on the next
setup invocation after an interruption; recovery refuses to overwrite a file
changed independently after the interruption. The registered command launches
that native executable directly. Removal deletes registrations but retains
versioned runtimes for explicit rollback; it never selects latest. Private
installation rejects a runtime root that is a symlink or is not a directory.
JSON reports a transaction-wide apply_error even when an orphan discovery
skill was the only planned mutation and no client result rows exist.
runtime list reports installed versions, executable bytes, active state, and
client references. runtime prune is a dry-run unless --yes is present; it
always retains referenced and active runtimes, keeps the newest two
unreferenced versions by default, and refuses directories containing anything
other than the expected native executable. Applied pruning rechecks every
supported client configuration immediately before deletion and removes through
snapshot-matched open directory handles, so a concurrent registration or path
swap fails closed. Change the bounded retention window with --keep-latest (0
through 64):
leantoken runtime list
leantoken runtime prune --keep-latest 2 --dry-run
leantoken runtime prune --keep-latest 2 --yesChoose upgrades and rollbacks explicitly by running the desired version, then refresh only entries that already exist:
npx --yes leantoken@latest setup --refresh --yes
npx --yes leantoken@0.1.8 setup --refresh --yessetup --refresh --dry-run audits the same plan without writing. Refresh does
not infer consent from installed clients and does not create new entries. If an
exact package is neither cached nor reachable while offline, startup fails; it
does not fall forward to @latest.
Global setup does not bind the repository where setup was run. OpenCode's
entry uses workspace-relative cwd: ".". Claude Code, Cursor, Codex, Gemini
CLI, and Antigravity use the working directory their host assigns to the MCP
process, which must be the active workspace. Broad home and filesystem roots
still fail closed before cache creation or indexing. --root remains available
for deliberate manual or project-scoped configurations.
cache list inspects every recognized per-repository, per-index-content-version
cache in the platform ProjectDirs cache directory and reports exact aggregate
counts and bytes. Per-cache entries are returned in stable identifier order,
20 at a time by default and at most 100 at a time. Pass --cursor with the same
filters to continue. --summary omits entries, repeatable --state values are
OR filters, and --repository-root matches one exact recorded root.
--compatibility independently filters compatible-current,
obsolete-older, legacy-unversioned, newer-unsupported, or unknown;
--index-content-version is an exact repeatable filter, while
--incompatible-with-current keeps only older and legacy-unversioned content.
At most five compatibility and 32 exact-version filters are accepted. All
filters are bound into the versioned cursor and cannot be changed between
pages.
Entry pages retain the existing metadata state and separately expose content
compatibility, full/scoped index identity, recorded root, schema, last
access, direct SQLite/sidecar bytes, and active lease status. Summaries report
entries/bytes by compatibility plus inactive incompatible bytes that are
actually safe to reclaim. Legacy repository-only identities remain visible.
Listing does not open repository services and therefore works from any
directory. JSON output contains Unix timestamps and
returned/matched/total counts for automation.
The versioned identity applies to automatically managed caches. An explicit
--database path remains unchanged and must not be shared by incompatible
index-content versions or normalized index scopes.
cache prune requires at least one explicit selection policy:
--older-than DAYSselects caches whose last repository bind is at least that old;--max-total-bytes BYTESselects least-recently-used caches until the managed total reaches the requested bound;--remove-missing-rootsexplicitly selects a cache when its recorded root is currently absent;--incompatible-with-currenttargets recognizableobsolete-olderandlegacy-unversionedcaches; active entries are reported but skipped. Without--yes, this criterion automatically performs a dry run.
Use --dry-run to inspect every keep/delete/skip decision. Actual deletion
requires --yes. Missing roots are not an implicit deletion criterion because
offline mounts and removable volumes can return later. Older-schema and
recognizable incomplete caches remain eligible for explicit age or size
policies. Corrupt/unknown inspection results, newer content, newer schema,
mismatched root identity, and unexpected directory content always fail closed.
After acquiring the exclusive lease, prune re-inspects compatibility and safety
before deleting to prevent a stale plan from crossing a state change.
Every Services instance holds a shared lease from before SQLite initialization
until its final clone drops. Prune must acquire the exclusive form and therefore
skips active MCP leaders, followers, and CLI services. It deletes the database,
WAL, SHM, journal, and coordination sidecars but retains the zero-byte lease
identity so a returning repository cannot race a new process through a replaced
lock file. Explicit --database files outside the managed directory are never
enumerated. Stop older LeanToken versions that predate cache leases before
pruning during a mixed-version rollout.
leantoken doctor launches the current executable as a real MCP subprocess and
verifies its initialization identity and agent instructions, exact nine-tool
catalog, and first leantoken.context retrieval. On a cold repository it
allows up to 120 seconds by default for the first retrieval, then follows
structured retry_after_ms guidance while the index warms. Set
--ready-timeout-seconds from 1 through 600 for a different bounded window.
If the window expires during normal indexing, the diagnostic says that the
index is still building and tells you to rerun after it completes. Use --json
for a machine-readable readiness report, including the current executable,
configured host registrations and their inferred versions, and the executable's
index_content_version. This doctor launches the current executable and
compares it with configured host entries; it does not claim to identify other
unregistered processes that share an explicit database.
Pass --client codex (or another supported client) to read that host's stored
registration and launch its exact command and arguments instead. This verifies
the configuration users actually restart into, including pinned npx and private
runtime launchers. When the registration exposes an exact pinned release, the
handshake and tool catalog are validated against that configured release rather
than the version of the doctor process that launched it. Aggregate doctor output
reports an exact but disabled OpenCode entry as disabled and recommends a
managed refresh instead of calling it current. The configured-child MCP contract
does not expose its index schema, so this mode omits index_content_version
instead of reporting the launching doctor's compile-time value.
Failures use the doctor_failure category and identify the registration,
launch, handshake, catalog, or first_retrieval stage so repair tooling
does not need to parse prose.
The MCP request contract is generated from the same Rust types used by the
RMCP Parameters<T> adapters. Clients should use the published schemas rather
than reconstructing a second schema or protocol-version handshake. files,
search, json, and history requests select one tagged operation under
operation; controls that apply only to that operation are nested in the
selected variant. For example, search uses
{"operation":{"kind":"regex","query":"unsafe\\s+fn"}} and file discovery
uses {"operation":{"kind":"glob","pattern":"src/**/*.rs"}}.
The breaking shape change also makes symbol identity explicit. Outline and
history results use { "name": "method", "parent": "Type" } where a parent
exists, and read/history inputs pass the same object rather than asking the
client to rebuild a parent.name string. Repository-relative paths and
non-empty selectors are normalized at the request boundary. Optional numeric
limits treat omission and explicit null identically and use the documented
default. Relationships that JSON Schema cannot express, such as
minimum_query_matches <= queries.length, remain runtime validation errors.
Ordinary context responses may include bounded provenance with commit,
branch, working-tree state, repository generation, freshness, and an explicit
availability status. Provenance is best effort: an unavailable Git probe does
not invalidate otherwise valid context. The +contract.<fingerprint> suffix
is only LeanToken's application-capability diagnostic digest over the generated
tool catalog and LeanToken resource metadata; RMCP owns MCP protocol
negotiation.
leantoken mcp starts the stdio protocol before opening the repository cache so
the initialize handshake is never blocked by indexing. After the client's
initialized notification, one process becomes indexing leader and followers
reuse its committed SQLite generations. A retrieval call made while the first
generation is being built waits internally for up to 30 seconds so a short cold
index does not require another model turn. This same absolute bound applies when
an explicit reconcile_working_tree call is waiting behind the initial
generation's operation lock. If the requested consistency boundary cannot
finish within the bound, its waiter is removed and the call returns successful
structured index_building retry guidance with retry_after_ms; it never falls
back to a stale result. Later calls report whether they use a current or
reconciling index generation.
One MCP server accepts at most 16 active tool calls. Its cloned request
handlers share that bound, while a separately started MCP process—including an
agent attached to another workspace—has independent capacity. Initialization
and tools/list remain available when tool calls are saturated. All tools,
including savings, use admission; a request for the wrong repository is
rejected before it consumes a slot.
Admitted retrieval work uses a separate Services executor with eight running
blocking closures and room for eight queued operations. A seventeenth active
operation returns retryable reason retrieval_capacity_exhausted immediately.
Queued work that cannot start within 500 ms returns
retrieval_queue_timeout. Cancelling queued work prevents its closure from
starting. Cancelling or aborting a caller whose closure is already running does
not make that capacity available early; it returns only when the closure exits.
These are per-server safety bounds, not a shared machine-wide quota.
Each retrieval snapshot holds one pooled SQLite connection and a DEFERRED WAL read transaction. It does not copy the database or leave a snapshot file. Snapshot memory is bounded by the eight running readers; up to eight additional admitted requests may retain their bounded inputs while queued. Long-running readers can temporarily delay WAL page reuse, so concurrency changes should be evaluated with WAL and RSS measurements rather than by raising the connection count alone.
LeanToken refuses to index a filesystem root, the current user's home directory,
or a parent of that home directory by default. This prevents an MCP host launched
from a broad working directory from recursively watching and indexing unrelated
projects and package caches. Select the workspace with --root; use
--allow-broad-root only for a deliberate broad index.
Repository discovery also fails closed when any configured walk-entry, file, aggregate-byte, or depth limit is crossed. LeanToken returns a typed error and keeps the previously committed generation intact; it never publishes a truncated repository. Every numeric limit must be positive, and the preparation batch byte limit must be at least the per-file byte limit. Limit failures stop automatic MCP indexing until the process is restarted with a narrower root or adjusted limits, preventing a fixed tree from being rescanned every 500 ms.
Discovery keeps useful hidden repository content, including .github,
.devcontainer, root dotfiles, and .cargo/config.toml. It skips known
generated and cache trees such as node_modules, target, .venv, venv,
.tox, .cache, package-manager caches, Python caches, .gradle, and
.rustup, and always prunes .git metadata before descending. Use
--include-generated only when generated trees are intentional source inputs;
it never admits .git.
Place .leantokenignore files at the repository root or in nested directories
to add gitignore-style rules. They have higher precedence than .gitignore and
.ignore; negation rules can therefore restore paths hidden by those files.
Built-in generated-tree exclusions run before ignore matching, so restoring
those requires --include-generated. Changes to any ignore control file cause
one bounded visibility reconciliation.
The indexing leader registers its watcher before the initial scan so changes during startup are not lost. Watcher queues and retained path state are bounded; bursts collapse to one pending reconciliation. Automatic reconciliation waits for a quiet period, and repeated full rescans or transient failures use capped backoff. Terminal root, discovery-limit, configuration, and cache-binding errors stop the indexing runtime and require a corrected configuration or restart.
Logs go to stderr. Stdout is reserved for MCP protocol messages. Semantic
failures after a valid tools/call are native MCP tool-error results with
isError: true; their structured content carries a fixed, allowlisted message,
status: "error", and stable category for model-visible recovery. Malformed
or unroutable protocol requests and internal failures remain JSON-RPC errors.
Repository, database, and external canonical paths, plus underlying I/O and
SQLite details, remain in stderr diagnostics rather than protocol responses.
The default structured mode returns the typed result without duplicating its
JSON as text. Explicit dual and text modes remain troubleshooting
overrides. leantoken doctor reports the effective static mode directly.
The catalog publishes documented input schemas but omits
optional output schemas; repeating full response DTOs in every tools/list
result costs model context without changing tool behavior.
Search, outline, read, and context responses return an opaque
meta.receipt_id. Pass that ID to a later retrieval to suppress exact content
and overlapping source ranges already returned. Receipt metadata persists in
the repository cache, so the same ID works across MCP processes and
programmatic service restarts while that cache, generation, and the sliding
24-hour lifetime remain valid. Near-duplicate evidence remains visible and increments
meta.receipt_near_duplicates.
Receipts are bounded, server-managed, and tied to one repository generation.
An old-generation receipt returns stale_receipt; malformed, expired,
capacity-evicted, cross-cache, and unknown IDs return unknown_receipt.
Deleting/pruning the repository cache also deletes its receipts. Receipts store
only repository identity, generation, paths, line ranges, content hashes, and
semantic signatures—not task/query text or raw source. Context
fragment_hashes and known_hashes remain available for stateless
compatibility.
Use leantoken.receipt_rebase only when carrying exact evidence across a
completed generation is useful. It is explicit and never changes ordinary
stale-receipt behavior:
{
"receipt_id": "r...",
"consistency": "reconcile_working_tree",
"max_samples_per_outcome": 4,
"max_response_tokens": 2000
}The source receipt must belong to the same repository cache and indexing scope
and to an earlier generation. Evidence is carried only when current path,
inclusive line coordinates, and content hash are all identical. The operation
does not guess line shifts, renames, moved symbols, duplicate bodies, overlap,
near-duplicates, or fuzzy matches. It returns complete carried, changed,
missing, and unmapped counts, a BLAKE3 commitment to the ordered full
classification, and up to 16 source-free examples per outcome. The new receipt
is meta.receipt_id; the source remains unchanged and stale.
Carried rows are exact-only: they suppress only a later candidate with the same emitted-content hash. They do not participate in range-overlap or near-duplicate suppression, so an unchanged outline signature cannot hide a changed body in the same range.
Validation is bounded to the source receipt's 2,048 evidence rows, 64 MiB of
live source in total, one configured-size file buffer at a time, and 64
exact-coordinate structural candidates per evidence item. Anything that cannot
be proven inside those bounds is unmapped and is not carried. Generation or
source-receipt races fail without creating a partial receipt. This Phase 1
contract intentionally excludes automatic rebase and all relocation heuristics.
Prefer LeanToken over shell discovery and whole-file reads. For a broad coding,
debugging, review, or architecture task, start with leantoken.context. Use the
narrow tools directly when the target is already known:
broad task -> context
known identifier/text -> search -> read
known file, unknown range -> outline -> read
unknown path -> files
Index-backed MCP retrieval tools, including receipt_rebase, accept an
optional consistency input:
indexed_generation(default) queries the latest completed index generation without scanning or waiting for filesystem changes. It does not mean Git HEAD and may include files indexed from an earlier working-tree state;reconcile_working_treefirst reconciles the current working tree, then queries the resulting completed generation.
Use reconcile_working_tree when edits, generated files, branch changes, or
external commits must be visible to the current call. Reconciliation uses the
same ignore rules and cross-process operation lock as automatic indexing, and
the request remains cancellable. Concurrent requests on one server share a
reconciliation wave when no scan has started yet. Requests arriving after a
scan starts wait for the next wave so they cannot inherit an older freshness
boundary. Cancelling one caller does not cancel shared work already running.
If that work fails, every coalesced caller receives the shared typed cause;
LeanToken does not automatically rerun the same failed scan for each caller.
Writes that begin concurrently with the call may require another
reconcile_working_tree request. CLI users can run leantoken index
immediately before retrieval when they need to reconcile first.
Numeric retrieval limits are inclusive and validated uniformly by the CLI,
MCP, and direct service APIs. max_results must be in 1..=100; the active
repository cap is config.max_results and may be lower. Omitted values use
config.default_results.
max_tokens and token_budget must be in 1..=32,000; context_lines may be
zero and must not exceed 20. Omitted optional values use their documented
defaults. Values outside these ranges are rejected rather than silently
clamped. Disallowed zero values are invalid input; values above a maximum
produce a model-visible MCP tool error with the public field name, requested
value, and active maximum.
LeanToken's stdio transport admits at most 16 decoded tool calls into the MCP
SDK at once and holds that capacity through response delivery; each server also
admits at most 16 active tool handlers. Excess calls fail fast with
status: "retryable" rather than creating unbounded SDK tasks. Initialization
and tools/list remain available while tool capacity is saturated. These
bounds are process-local: another MCP process serving another workspace has
independent capacity.
This explicit evidence-lifecycle operation is described in the
MCP receipt section. It returns no source and performs no
retrieval ranking. Call it only after a stale receipt is worth preserving; use
the returned current-generation meta.receipt_id on later search, outline,
read, or context requests.
Returns repository-local token accounting and an opaque snapshot. With no
input, window is lifetime. Supply a prior
{"snapshot":"lts1..."} to receive only aggregate counter changes since that
snapshot; the response returns a replacement snapshot. Snapshots are
caller-carried, repository- and tokenizer-bound, checksummed, and limited to
32 KiB. They do not create a per-request event table.
response_accounting retains every successful response and reports comparable baseline counts,
source/path-metadata/protocol/total response tokens, signed response deltas,
receipt-suppression counts, and fixed rows for all nine retrieval operations.
The additive observations object reports best-effort persisted successes,
failures by operation and stable error category, and exact expected_hash
matches. expected_hash_suppressed_source_tokens measures the requested source
omitted by those matches. This is distinct from
response_accounting.receipt_suppressed_exact and
receipt_suppressed_overlap, which describe receipt-based duplicate
suppression. request_classification divides new requests into useful,
incomplete, unsupported, hash_suppressed, and failed.
Typed unsupported_language failures belong to unsupported; the broader
failed_service_requests observation still reports every returned error.
Incomplete or unsupported retrievals—including a zero-symbol LaTeX
outline—remain visible as full response cost but do not inflate effective
source compression.
The report is a represented-source comparison over successful recorded
responses plus separately observed failure counts. It does not infer retry
chains, whether returned evidence was used, superseded calls, provider framing,
or task success. Those limits are returned in observations.unobserved. Treat
the signed response delta as local response accounting, not as a
correctness-adjusted claim about an agent's full workflow.
This is a read-only observation: calling leantoken.savings does not update
the tracker. Accounting writes never delay retrieval: local writer contention
skips the record, so every count is a persisted lower bound.
Discovers repository structure without returning source bodies.
Operations:
{"kind":"tree","path":"src","depth":2}: compact hierarchy;{"kind":"find","query":"mcp"}: fuzzy path and basename matching;{"kind":"glob","pattern":"src/**/*.rs"}: indexed path matching.
Pass one of those tagged objects as operation. Operation-specific fields
cannot be mixed. max_results, max_response_tokens, cursor, and (for
tree) depth belong to the selected operation. Output contains bounded
file/directory entries with language and size metadata when available.
Fuzzy score remains the primary ordering key. Equal-score results prefer root
and conventional production-source paths, then shallower paths; explicit
benchmark, bench, fixture, and testdata directories sort after ordinary paths.
The final path tie-break remains deterministic and continuation-safe.
Set projection="paths" for the opt-in path-only response. It returns the same
ordered page as full in a paths array plus the complete meta freshness,
repository, token-accounting, and continuation contract. Kind, language, byte
size, and fuzzy score are omitted. The default remains full; use it when those
fields affect the next routing decision.
Returns ranked source excerpts. Modes are auto, text, regex,
identifier, symbol, and reference.
Each request selects a tagged operation such as
{"kind":"regex","query":"unsafe\\s+fn","max_results":20}. Its options
include path filters, focus paths, result and token limits, context-line count,
case sensitivity, and a generation-bound cursor. Defaults are 20 results,
8,000 source tokens, and two context lines. Each hit includes its
path, one-based returned line range, excerpt, primary match_kind, all merged
match_kinds, score reasons, content hash, raw score, and a normalized_score
from 0 to 1 relative to the strongest candidate in the query. Structural fields
appear only when syntax supports them.
auto and identifier searches merge lexical and structural hits that resolve
to the same indexed definition coordinates. Set prefer_structural=true to
retain the structural definition excerpt as the primary hit when channels are
merged; merged channel and score-reason diagnostics are preserved either way.
The response coverage reports total, current-page returned, and
truncated counts separately for definitions, references, and text/regex
matches. One merged hit can represent more than one channel.
Structural symbol queries accept either a bare name or the canonical
parent.name identity exposed by outline and search metadata. Qualified
queries use the leaf name to obtain trigram candidates and then verify the
combined owner/name identity, so they do not require a larger FTS index.
Unqualified search remains a bounded candidate operation and may return more
than one definition; combine each hit's enclosing_symbol and symbol to pass
the qualified identity to outline, read, or history.
Set projection="grouped" for broad symbol/reference discovery that does not
need every repeated excerpt or score. Grouping runs after the normal ranked
page, exact-hit deduplication, lexical/structural definition merge, and receipt
filtering. Each group retains an explicit definition when available, otherwise
one representative path/range/excerpt/content hash; references are summarized
by file with their count, covered line span, and roles. coverage,
occurrences_total, repository freshness, exact token accounting, and the
normal continuation cursor remain available. Non-exhaustive searches still
default to full. Use leantoken.read with a returned path/range to expand
source, or repeat a narrowed full search when individual reference excerpts
are needed.
Set all_occurrences in text or regex mode for every non-overlapping match,
including repeated alternatives on one line. MCP defaults these requests to
projection="occurrences": one path/range/excerpt/content hash per unique
excerpt plus an array of every exact {line, start_column, end_column} span.
Columns are zero-based UTF-8 byte columns; a multi-line regular expression also
reports end_line. Set coordinates_only=true to group only by path and omit
source and hashes, or explicitly request projection="full" for the older
per-occurrence ranked hits and global byte offsets.
Both shapes report occurrences_returned for the current page and an exact
occurrences_total across the filtered index. Grouped excerpt token charging
counts each unique excerpt once; coordinates-only calls charge no source
tokens. Exhaustive pagination still applies max_results without changing the
total; follow next_cursor until absent.
For a repeated exact exhaustive query, MCP callers can explicitly request a persistent coverage receipt:
{
"operation": {
"kind": "regex",
"query": "unsafe\\s+fn",
"all_occurrences": true,
"coordinates_only": true,
"query_receipt": {"kind": "record"}
}
}record persists a receipt only when every occurrence fits the returned page
and the final response budget succeeds. Pagination or token omission returns
status: "not_recorded_incomplete_response" without an ID. Invalid regex,
internal exhaustive limits, cancellation before the receipt write, and any
other error persist nothing. Ranked auto, identifier, symbol, and
reference modes, focus boosts, cursors, the full/grouped projections, and
evidence receipt_id cannot be combined with query receipts.
Pass the returned ID back with the same normalized predicate to avoid repeating the lexical scan:
{
"operation": {
"kind": "regex",
"query": "unsafe\\s+fn",
"all_occurrences": true,
"coordinates_only": true,
"query_receipt": {
"kind": "reuse",
"receipt_id": "q..."
}
}
}A successful reuse returns status: "already_covered", the exact match count
and result commitment, and no occurrence groups. Path separators, duplicate
patterns, and pattern order are normalized. An exact zero-match proof may also
cover a conservatively provable narrower include/exclude scope; a nonzero
superset cannot derive a subset count and fails with
query_receipt_mismatch. A later repository generation is reusable only when
the index configuration and a streaming digest of every relevant indexed
path/content hash are unchanged. Otherwise the call fails with
stale_query_receipt.
This reuse still requires a tool call, so it can avoid the server-side text/regex scan and result payload but cannot claim that the host skipped a model turn. A future handoff or capsule integration would need separate host evidence before claiming an avoided call.
The MCP initialize response appends +contract.<fingerprint> to the runtime
version. The fingerprint covers the canonical generated tool catalog and
LeanToken resource capability/template metadata, so clients can distinguish a
stale server binary or cached application contract from the feature set that
accepted the request. RMCP negotiates the MCP protocol version separately.
Each page examines at most max_results ranked candidates. max_tokens may
filter some or all of those candidates, so a page can contain fewer hits or be
empty while still returning next_cursor. Follow the cursor to examine later
candidates. When next_cursor is absent, every candidate was examined; increase
max_tokens and restart the search if omitted excerpts must become eligible.
Lexical matches remain eligible when structural extraction is unavailable or incomplete.
Repository-wide lexical scans have explicit file, per-file chunk, occurrence,
and compiled-program safety limits. Exhaustive text and regex modes remove the
candidate-chunk cap, but retain the other limits. If a limit would make the
answer incomplete, the tool returns a typed limit error instead of reporting a
partial exhaustive result. A regex_chunks_per_file error includes the
repository-relative blocking_path so the caller can narrow or exclude it.
Returns definitions, imports, signatures, parent relationships, and one-based line ranges for one or more files. Name and kind filters narrow the output. Bodies are not returned by default.
The name filter accepts the same bare or canonical parent.name identity used
by symbol search, read, and history. A bare name can intentionally return
multiple definitions; use the returned parent, kind, and line coordinates
to choose a qualified target.
path_results returns one ordered outcome for every input with its zero-based
request_index, normalized repository-relative path, and typed status.
Indexed paths continue to produce bounded entries in files when another path
is absent. Paths that are missing, ignored, or otherwise outside the current
index snapshot report not_indexed; LeanToken does not probe the live
filesystem to guess which cause applies. Invalid or unsafe paths remain
request-level errors.
parse_complete reports whether every requested path was indexed and parsed
completely; each file reports the same state independently. Parse completeness
does not imply result completeness.
result_complete is true only when every path was indexed and the response
contains every filtered symbol and import. Exact total_symbols,
returned_symbols, total_imports, returned_imports, and
symbol_counts_by_kind cover the indexed subset and make its coverage auditable.
truncated_by_max_results provides meta.next_cursor for another page, while
truncated_by_max_tokens means the query must be repeated with a larger token
budget to recover omitted entries. Outline cursors are bound to the repository
generation, normalized path order, symbol filters, and projection. Each page
repeats the same ordered path_results; the cursor's global entry offset maps
deterministically through the indexed subset in that same request snapshot.
Set projection="signatures" to exclude imports before result/token selection
and omit symbol byte offsets. The response retains path, language,
parse-completeness state, symbol name/kind/parent/signature, one-based line
ranges, exact symbol coverage, freshness, and continuation. Each file includes
a content_hash over the serialized ordered signatures array so the compact
representation can be checked and reused without paying one hash per symbol.
result_complete then describes the filtered symbol set, not imports. Signature
cursors are projection-bound; switching between full and signatures with a
cursor fails stale instead of applying incompatible offsets.
Supported languages report whether parsing was structurally complete.
JavaScript, TypeScript, and TSX outlines include top-level const, let, and
var bindings, exported data bindings, class fields, and object/array default
exports. Function-local variables remain lexical search evidence rather than
outline symbols.
C# outlines include namespace and type declarations plus methods, local
functions, constructors, properties, fields, events, enum members, indexers,
and operators. using directives are imports, method-like ranges include
complete bodies, and type and call references report their enclosing member.
CSS outlines include complete selector rules, custom properties, media/supports/
container conditions, and keyframes. Selector atoms are available to reference
search. HTML outlines include sectioning elements, IDs, forms and controls,
dialogs, buttons and links, data-* actions, hash anchors, and script/style
resources. HTML resource paths are also reported as imports.
Markdown outlines include ATX and Setext headings as markdown_heading
symbols. Their ranges cover the complete section through the line before the
next heading of equal or higher level, parent fields preserve the heading tree,
and headings inside fenced code blocks are excluded.
Unsupported text files remain searchable and are marked incomplete rather than
being presented as precise.
Reads an exact source range.
pathis required.target: {"kind":"lines","start":40,"end":90}selects an inclusive one-based range.target: {"kind":"symbol","identity":{"name":"LeanTokenMcp"}}selects one indexed symbol definition. Nested definitions use{"name":"wait_for_initial_index_cancellable","parent":"Services"}. An unqualified or qualified identity that matches multiple definitions returns typedsymbol_ambiguousinstead of selecting the first definition.target: {"kind":"heading","name":"Installation"}selects one indexed Markdown section. The exact outline signature form, such as"name":"## Installation", is also accepted. Add"occurrence":2to select the second duplicate heading; occurrences are one-based and follow source order.target: {"kind":"continuation","cursor":"..."}continues a truncated response. The cursor preserves byte-exact progress even when the preceding page ended in the middle of a line.max_tokensdefaults to 8,000 and accepts values through 32,000.expected_hashreturnsnot_modifiedwithout source when it matches the hash from the same prior target.delta: truerecords a complete, non-truncated target as a bounded future base. Whenexpected_hashis absent, a follow-up automatically selects the newest base for the same repository and exact target. Unchanged content returnsstatus: "not_modified"; changed content usesstatus: "delta"and thedeltafield only when the complete unified diff costs fewer source tokens than full current content. Passexpected_hashto require one explicit prior hash instead.
content_hash identifies the returned range. indexed_hash identifies the
whole indexed file. index_stale is true when the live file differs from the
indexed version (for example after an edit that has not been reindexed yet).
target_start_line and target_end_line describe the complete resolved target;
returned_start_line and returned_end_line describe the current page.
status: "truncated", truncated: true, next_start_line, and
continuation_cursor fail loudly whenever source remains. Continuation cursors
are bound to the repository generation, path, and live full-file hash, so a
stale cursor cannot combine pages from different file versions.
truncation_guidance reports the complete target and remaining source-token
cost, estimated additional pages at the current budget, a bounded recommended
budget for the next continuation, and the minimum pages allowed by the server's
source-token ceiling. basis: "verified_live" means full-file verification
proved the pinned indexed target matches live source;
basis: "indexed_generation_estimate" keeps bounded reads cheap and makes the
snapshot-based uncertainty explicit.
delta_receipt reports the stable target key, selected base and head hashes and
generations, full and delta token counts, avoided tokens, and any explicit
fallback reason. Missing bases, changed target coordinates, truncated or
oversized content, and uneconomic diffs return full content. Delta state is
in-memory and repository-local, expires after 30 minutes, and is bounded to
128 entries, 512 KiB per entry, and 8 MiB of retained content. Latest-base
selection scans only those 128 insertion-order keys in reverse and never
creates an additional unbounded index. It never applies
to ranked context fragments or continuation cursors.
status: "not_modified" means either expected_hash or the automatically
selected delta base matched. The distinct status: "receipt_suppressed" means
a server-managed evidence receipt already contained the exact current content.
Changed content in an overlapping range is returned and added to the receipt
rather than being hidden as unchanged.
meta.repository_generation is the committed index generation used for path
and symbol lookup; meta.freshness is reconciling while an index operation
is active on this cache.
When the index has never completed a generation, retrieval tools wait for up to
30 seconds before returning a successful retry result such as
{"status":"retryable","reason":"index_building","retry_after_ms":500, "index_progress":{"detail_available":true,"active":true, "current_generation":0,"attempt_id":"...","phase":"preparation",...}}.
Detailed progress has a fixed shape with aggregate counters only. Its phases
distinguish discovery, hash/planning, preparation, relational staging, each FTS
build, final commit/checkpoint, and terminal completion/failure/cancellation.
update_sequence, last_progress_unix_ms, and aggregate counters let callers
detect forward progress without exposing paths or source. files_staged is not
queryable until the atomic generation commit completes.
A follower process that cannot observe the leader's memory instead returns
"detail_available":false; unavailable optional fields are omitted rather
than reported as zero. The same index_progress object appears in read-only
status while the committed generation remains zero and is omitted once an
index is ready. Retry the same call after retry_after_ms. Caller cancellation
interrupts the internal wait. After local edits, set consistency to
reconcile_working_tree on the next MCP retrieval. An indexed_generation
read may still use index_stale and expected_hash to detect or suppress live
ranges.
Reads exact repository-relative JSON files without requiring them to be indexed, including ignored artifact paths. Operations are:
query: select the root, an RFC 6901 JSON Pointer, or a standard JMESPath expression, then return avalue,collapsed,keys, orschemaprojection.numeric_summary: collect numeric leaves below the selection and return exact count, min, median, nearest-rank p95, max, and ignored non-numeric count.diff_fields: evaluate up to 100 selectors against two files and report presence, projected before/after values, and whether each field changed.
The JSON request has exactly these three operation kinds. collapsed, keys,
and schema are projections of query, not operation kinds. For example:
{
"operation": {
"kind": "query",
"path": "benchmarks/reports/graph-signal-ablation-v1.json",
"projection": "keys"
}
}JMESPath selectors are evaluated against the selected JSON document root. To
summarize the graph benchmark's per-corpus cold-index values, select the actual
field beneath graph_index.corpora:
{
"operation": {
"kind": "numeric_summary",
"path": "benchmarks/reports/graph-signal-ablation-v1.json",
"selector": {
"kind": "jmespath",
"expression": "graph_index.corpora[].cold_index_ms"
}
}
}consistency belongs to applicable repository retrieval tools; it is not a
field in this live JSON request. A numeric summary with count: 0 means the
selected path contained no numeric leaves, not necessarily that the JSON file
was malformed. keys(@) only works when its selection context is an object;
if a caller evaluates it against the wrong or null context, it fails. Use the
query operation with projection: "keys" for bounded key traversal.
collapsed replaces arrays with their total count and a bounded sample.
max_items defaults to 1,000 (maximum 10,000), array_sample_size defaults to
3 (maximum 20), and max_tokens defaults to 8,000. Exact source hashes bind
responses to the complete live files. Raw values that exceed a cap fail loud.
keys is a deterministic flat projection and supports pagination under both
item and token limits. MCP orders broad keys by (depth, JSON pointer) so root
and top-level shape precede deep subtrees. Optional depth is relative to the
selected root: root is zero, depth: 1 includes immediate children, object
segments use RFC 6901 escaping, and arrays share one /* shape segment. Every
keys response reports exact total_items, returned_items, and
remaining_items. An incomplete page identifies max_items or max_tokens in
incomplete_reason and returns meta.next_cursor; repeat the identical path,
selector, projection, and depth with cursor to continue. Version-two cursors
bind the traversal order and depth in addition to those query inputs and the
live source hash. Legacy cursors, changed files, and changed query shapes fail
with stale_cursor instead of mixing incompatible pages.
Incomplete schema, collapsed, and projected diff results report the same
exact counts and incomplete_reason. Schema construction retains breadth-first
siblings until max_items or max_tokens is reached and adds an
x-leantoken-incomplete object containing the exact omitted-frontier count and
up to 32 deterministic JSON pointers. Their nested shapes are not split into
ambiguous pages, so they do not return a cursor; increase the relevant limit or
use a narrower selector. A complete schema retains the prior schema shape and
does not contain the extension. Strict JSON parse failures include the syntax
category, one-based line and column, and zero-based byte offset. JMESPath
compile and runtime failures include their stage, typed reason, expression
offset, line, and column.
Reads symbol-aware evidence from immutable Git revisions without changing the working tree or index:
operation: {"kind":"read_symbol","path":"src/lib.rs","symbol":{"name":"Services"}, "revision":"main~1"}parses the historical blob and returns that symbol.operation: {"kind":"diff_symbol",...}parses the symbol independently atbase_revisionandhead_revision, then returns a bounded unified diff.operation: {"kind":"diff_symbols","targets":[...],...}compares an ordered symbol set over one shared revision range and returns cursor-paged outcomes.operation: {"kind":"symbol_log",...}starts atrevision(defaultHEAD) and uses Git line-history traversal for the resolved symbol range.
Historical paths are repository-relative, revisions are resolved before object
lookup, and blobs remain subject to the configured per-file byte limit.
Nested symbols use the same {name,parent} identity accepted by exact live
reads. A unique bare name also resolves, while multiple matches return typed
symbol_ambiguous instead of selecting by parser order. In diff_symbols, an
ambiguous endpoint remains a per-target unavailable result with
ambiguous_base_symbol or ambiguous_head_symbol, so other targets still
succeed. diff_symbol permits the file or symbol to be absent at one endpoint:
before or after is omitted, the unified diff contains the complete bounded
addition or deletion, and semantic_change.kind is added or removed. A
symbol absent at both endpoints remains a typed symbol_not_found error.
max_tokens defaults to 8,000 and applies to historical source or unified diff;
truncation is explicit through result_complete, HistoricalSymbol.truncated,
or diff_truncated. max_results defaults to 20 and is capped at 100 for
symbol_log. Symbol metadata includes the resolved 12-character revision,
complete line range, kind, parent, and full-content hash. returned_end_line
exists only when content is returned; metadata-only diff endpoints and symbol
logs omit that range instead of reporting line zero. Symbol diffs normalize only
their private comparison buffers, so parser slices do not manufacture a
whole-file no-newline marker and the returned content and hash remain unchanged.
This tool deliberately has no index consistency mode because Git objects are
immutable.
diff_symbols accepts 1–64 targets and returns at most 32 per page. Each target
contains path and symbol; supply head_path and head_symbol together to
classify an explicit move or rename. Results preserve request order and include
the original request_index plus one of unchanged, added, removed,
renamed, modified, not_found, or unavailable. Base/head commit metadata
is shared by the page. A continuation cursor binds the resolved revisions,
ordered normalized target pairings, and next offset, so changing any of them
returns stale_cursor.
The batch operation parses each distinct file once per endpoint and performs at
most seven Git subprocesses regardless of target count. It accepts at most 32
distinct paths per endpoint, 1 MiB per blob, 8 MiB of blobs per endpoint, 1,024
parsed symbols per endpoint, and 1 MiB of retained unified diff per page.
max_tokens is shared across all diffs. If max_response_tokens requires
fitting, LeanToken first preserves the largest ordered prefix of symbol status
records, then fills their diffs in request order; incomplete_reason identifies
the source-token, diff-byte, or final-response bound. The public Rust service
exposes this as Services::history_diff_symbols; the existing HistoryOperation
enum remains source-compatible.
diff_symbol also returns semantic_change when the matched symbol content
differs. The receipt classifies the change as modified, distinguishes
signature_changed from body_only, and marks public_contract_changed only
when an explicitly pub, public, or export signature changes. Added and
removed exact symbols use the same public-contract rule. The unified diff
remains the source of truth. Renames are classified by immutable review context,
where both changed paths and symbol names can vary.
For context restricted to immutable history, pass BASE..HEAD as
leantoken.context.base_revision with strict_changed_paths: true. A single
commit uses COMMIT^..COMMIT; the resolved diff scope and coverage receipt make
the hard boundary explicit.
Turns a task into a ranked set of source evidence. task is the only required
input; token_budget defaults to 3,000 and accepts values through 32,000.
For autonomous broad triage, make one materialized call with plan_only=false
and use the evidence directly. Make at most one focused follow-up only when the
returned coverage identifies a concrete missing implementation or
regression-test owner. This is a host usage contract, not service session state
or a restriction on implementation agents. The
repeated multi-agent context suite
records the four-task, 60-run evidence and its limits.
Optional inputs focus or exclude paths and symbols, provide hashes already held
by the caller, and identify a prior repository generation. include_paths is a
hard boundary: every returned source fragment must match at least one supplied
pattern, while focus_paths remains a ranking boost unless
strict_focus_paths=true. minimum_fragments_per_focus_path reserves the
requested number of fragments for every focus pattern before ordinary ranking.
Context accepts at most 32 focus patterns and a minimum of at most eight
fragments per pattern. Required focus coverage receives bounded file-local
candidates before global top-N truncation; broad globs that exceed the
per-pattern file inspection bound report that limitation in warnings.
Explain-profile plans and materialized responses report a bounded allocation
diagnostic under each coverage.focus_path_coverage[].diagnostics. Balanced and
compact responses omit it without changing selection or making a metadata-only
plan larger than the corresponding materialized response. The diagnostic
separates eligible indexed paths, generated ranges and symbol ranges, enforced
reservations, exact selected source tokens, and non-zero suppression counts for
path policy, caller-held hashes, deduplication, source budget, fragment
capacity, per-file diversity, or soft global ranking. An unsatisfied path also
reports one capacity_blocker; it is an observed selection boundary, not a
task-success or relevance-confidence estimate. Counts are per pattern and are
not additive when focus globs overlap.
strict_changed_paths=true restricts fragments to the resolved explicit paths,
an immutable BASE..HEAD range, a base-revision-to-working-tree diff, or current
Git working-tree changes when neither diff input is supplied. Include, strict
focus, strict changed, and exclude constraints are intersected; no constraint
silently broadens another. must_include_paths and
must_include_symbols generate and select required indexed evidence before
focus minimums and ordinary ranking. A required path guarantees path
representation, not task relevance. Its candidate is the highest task-matching
bounded chunk in the selected file; when no task query matches, context returns
an explicit required_path_fallback excerpt from the start of the file.
max_fragments defaults to 8 and accepts values through 100.
Use required_evidence when path presence is insufficient. Each entry supplies
a path, one to sixteen literal queries, and an optional
minimum_query_matches (default one). Context selects matching excerpts before
ordinary ranking and reports each contract under coverage.required_evidence.
evidence_scope_satisfied is true only when every contract matches an indexed
path and selected or already-held evidence covers the requested number of
distinct queries. Up to 32 contracts and 64 KiB of query text are accepted.
Matches are case-insensitive literals and are materialized as bounded 40-line
excerpts centered on the evidence line. MCP accepts these objects directly;
the CLI accepts the same object as repeatable JSON, for example
--required-evidence '{"path":"paper/**","queries":["claim boundary"]}'.
workflow_evidence is an opt-in object for facts the caller directly observed
while executing the workflow. Its four arrays are failure_traces, symbols,
repository-relative paths, and test_intents. Each class accepts at most
eight items, each item accepts at most 8 KiB, and the combined payload accepts
at most 32 KiB. Evidence shares the existing 12-query context fan-out instead
of starting an unbounded second search. Do not populate it from benchmark gold
labels or guesses:
{
"task": "fix the failing default-value regression",
"workflow_evidence": {
"failure_traces": ["error: default_values_if is missing"],
"symbols": ["default_values_if"],
"paths": ["tests/builder/default_vals.rs"],
"test_intents": ["default values regression"]
}
}Required symbols share the request token budget. A definition that fits its
share is returned completely. When it does not fit, the fragment and plan
candidate report truncated: true together with the complete
target_start_line and target_end_line; coverage reports the name under
partial_must_include_symbols instead of claiming complete coverage.
For human review or control-plane inspection before expensive or high-risk
materialization, set plan_only=true to run the same hard scopes, ranking,
must-cover selection, token budget, and fragment limit without returning
source. The response has an empty fragments array and no server-managed
receipt mutation; plan contains bounded paths and ranges, final scores and
reasons, exact source-token estimates, focus coverage, completeness, and a
generated-artifact warning. receipt_id is rejected in plan mode; use
known_hashes for stateless suppression that must apply to both preview and
materialization. After approval, repeat the same request with
plan_only=false to materialize those candidates against the selected index
consistency boundary.
Context ranking excludes known generated report trees by default:
artifacts/runtime_reports/**, artifacts/viability_audit/**,
artifacts/replay_reports/**, notes/runs/**, and node_modules/**. Exact
files, search, and read operations are unaffected. A matching
include_paths pattern explicitly admits an indexed artifact to context; an
explicit exclude_paths or strict scope still wins.
Repositories can append context-only exclusions in .leantoken.toml:
[context]
exclude_paths = ["generated/**", "reports/audit/**"]These patterns do not remove files from the index. Known cache trees that are
not indexed by default, including node_modules, still require the global
--include-generated indexing override before exact lookup or explicit context
inclusion can find them. Repository configuration is resolved when LeanToken
opens the repository.
An MCP server can also serve a bounded set of additional repositories when they are explicitly approved in the primary repository configuration:
[repository_contexts.docs]
root = "../docs-repository"Context names are request-only identifiers; retrieval calls never accept a
filesystem root. The primary workspace is selected when repository_context
is omitted, while an approved name selects the corresponding repository. A
maximum of eight additional contexts is accepted. Each context has its own
index, generation, cache, and admission state; unknown names fail closed, and
receipts remain bound to the selected repository identity and generation.
The coverage receipt distinguishes unmatched focus/include constraints,
covered requirements, indexed requirements blocked by path or budget limits,
and requirements absent from the index. Every focus path returns indexed and
selected fragment counts with an implicit minimum of one; strict or explicit
minimum requests contribute to path_scope_satisfied.
The field reports path coverage only; it does not claim task relevance. Explicit
required_evidence contracts instead contribute to
evidence_scope_satisfied and report matched and unmatched queries per path.
Strict changed-path requests return resolved and selected changed-path counts.
An empty strict scope therefore returns an explicit coverage failure rather
than unrelated evidence. Already-held matching hashes satisfy a must-cover or
evidence requirement without resending source.
omission_summary distinguishes path filtering, known hashes, and budget or
result limits with aggregate counts in both compact and balanced responses.
Choose the MCP response_profile field or CLI --response-profile flag:
compactpreserves fragments, receipts, hard-constraint coverage, warnings, aggregate omission counts, and retry routing, but removes individual omissions, verbose facets, and optional diff evidence.balancedis the default and preserves the historical non-verbose response.explainadds bounded individual omissions, path, language or file-type, reason, score-band, focus and changed-path facets, plus available diff evidence.
Every response reports effective_response_profile. Profiles do not change
candidate generation, ranking, fragment membership or order, source-token
budgets, hard constraints, or receipt suppression. They only change the
serialized presentation cost. The --verbose-diagnostics CLI option maps to
explain; combining it with an
explicit compact or balanced profile is rejected. Facet lists are
deterministic and bounded to 12 values; longer path or file-type tails are
combined into [other]. Candidates rejected before scoring use the not scored
band. The selector merges overlapping candidates, suppresses duplicate or known
content, preserves file diversity, and returns short reasons for each chosen
fragment.
A compact immutable review journey can first preview and then materialize the same request:
{
"task": "review the change for correctness regressions",
"workflow": "review",
"base_revision": "origin/main..HEAD",
"strict_changed_paths": true,
"response_profile": "compact",
"plan_only": true
}Check coverage.changed_path_coverage and path_scope_satisfied for the hard
range boundary, and inspect workflow_receipt.owner_test_candidates plus
missing_families for bounded owner-test evidence and gaps. Then repeat it with
plan_only=false. Use response_profile="explain" only when the omitted or
focus-allocation or semantic diff diagnostics are needed.
workflow accepts auto, implementation, contribution, review, or
investigation. Contribution and review modes add bounded repository guidance,
issue/PR templates, validation configuration, and tests whose names match
changed or focused source paths. auto selects a specialized mode only from
high-confidence task language; ordinary tasks retain implementation ranking.
The resolved mode is returned as workflow. Specialized responses include a
workflow_receipt with candidate counts and explicit missing evidence families;
missing guidance or owner tests is not represented as proof that none exists.
Materialized review requests over an immutable BASE..HEAD range also place
a semantic_change receipt under diff_scope.evidence. It deterministically
classifies parsed definitions as added, removed, renamed, or modified;
splits matched modifications into signature and body-only changes; identifies
explicit public-contract changes; reports recognized JSON configuration changes
as RFC 6901 key paths without values; and labels likely owner tests as found,
missing, or unknown. Rename requires one unique normalized body fingerprint
on each side. Ambiguity, incomplete parsing, byte or result limits, unsupported
Git entries, and truncated test scans are emitted as gaps rather than guessed.
Semantic classification shares the diff-evidence cap of 64 changed paths and
separately caps symbol and configuration changes at 64 each, with an 8 MiB
aggregate historical-content limit. It is absent from plan-only and non-review
responses. Review requests
against the working tree or a single base revision retain owner-test coverage
but report semantic_change_requires_immutable_range; no risk score is
produced.
Diff-scoped requests spanning at least 32 changed paths across three or more
deterministic path groups also return a bounded routing receipt. It reports
candidate, changed, selected, and group counts and suggests up to three narrower
include_paths scopes. The receipt records the originating consistency
boundary, base revision, and held fragment hashes once; callers overlay a
suggested scope while reusing the original diff inputs. It is decomposition
guidance, not a completeness claim.
The context evidence receipt retains a compact hash list aligned by index with
the returned fragments. For normal same-session reuse, pass
meta.receipt_id instead of copying those hashes. The server then suppresses
exact duplicates and overlapping ranges across context, search, outline, and
read. fragment_hashes plus known_hashes remains the stateless fallback for
clients that cannot retain a server receipt.
Set the optional handoff object on a materialized request when a host is about
to compact a broad context or transfer work to another executor. The response
then includes handoff_manifest: a source-free task summary, repository and
generation identity, Git commit and working-tree state, diff identities,
selected path/line/hash coordinates captured before receipt suppression, held
hashes, focus inputs, changed/related/test paths, and caller-supplied
validations, assumptions, questions, negative evidence, and avoid rules.
Validations are transported as caller reports; LeanToken does not execute them.
plan_only and handoff are rejected together because a plan has not
materialized grounded evidence.
The manifest is a host-triggered transfer artifact, not persistent memory.
receipt_id remains useful across processes while the same repository cache,
generation, and TTL survive; after a new generation, only the explicit
exact-only receipt_rebase operation can create a current receipt. Coordinates
and hashes remain the verification boundary. If Git identity or working-tree
state cannot be established, the corresponding field is absent or unknown
and gaps explains the missing provenance. Receipt suppression can leave
fragments empty while the manifest still records the selected pre-suppression
coordinates.
Host state is capped at 16 validations and 16 entries in each note list. Summaries and notes accept 512 UTF-8 bytes per item; validation commands accept 1,024. Output retains at most 100 evidence coordinates, 64 held hashes, 32 focus paths, 32 focus symbols, 64 changed paths, 64 related paths, 32 test paths, and 64 explicit gaps. Deterministic truncation adds a gap rather than claiming completeness. Because the manifest itself has protocol cost, request it for genuine multi-fragment handoffs rather than routine small context calls; the repository benchmark reports the zero-, one-, and all-reread crossover.
Every retrieval response also includes an opaque meta.repository_id. MCP
callers can pass it back as expected_repository_id; a server bound to another
repository or linked worktree rejects the call with
repository_identity_mismatch instead of returning misleading empty evidence.
CLI equivalents make the reuse contract explicit:
leantoken --json read src/lib.rs --lines 40:90 --expected-hash HASH
leantoken --json context --task "finish the validated fix" --budget 1200 \
--known-hash HASH_FROM_RECEIPT --prior-generation 7
leantoken --json context --task "fix the observed parser failure" \
--failure-trace "error: unexpected token" --evidence-symbol Parser::parse \
--evidence-path src/parser.rs --test-intent "parser regression"
leantoken --json context --task "transfer the grounded implementation state" \
--handoff --handoff-summary "Continue the validated parser fix"search, outline, read, and context bound returned source text. The
default read limit is 8,000 tokens and the hard source-output ceiling is 32,000
tokens. Assembled context has a separate 3,000-token default. Programmatic
configurations may lower these defaults and ceilings; omitted MCP fields use
the active service defaults rather than the static tool-schema examples.
Every retrieval response separates budgeted evidence from model-facing response overhead:
source_tokenscounts the selected evidence text. Path-onlyfilesresponses therefore report zero source tokens.protocol_tokenscounts the compact JSON response envelope with scalar values neutralized and result arrays emptied.path_and_metadata_tokenscounts the remaining non-source response cost, including paths, metadata values, and repeated result structure.total_response_tokenscounts the final compact JSON service response for direct/CLI calls. For MCP it counts the complete server-producedCallToolResultin the configured result mode, including the nonzero accounting fields themselves.tokenizeridentifies the tokenizer used for every count.
Accounting is filled repeatedly to a deterministic fixed point. Therefore
source_tokens + protocol_tokens + path_and_metadata_tokens equals
total_response_tokens, and tokenizing the final compact DTO produces that
exact total. Source limits remain independent and do not by themselves impose a
hard ceiling on the final serialization. All retrieval callers can opt into
that second boundary with ServiceCallOptions, MCP max_response_tokens, or
CLI --max-response-tokens; a mandatory correctness skeleton that cannot fit
returns a typed ResponseBudgetExceeded error. CLI JSON and MCP structured
tool-error content include provided_max_response_tokens,
minimum_required_response_tokens,
retry_with_at_least, and a bounded aggregate breakdown; the established
requested and limit fields remain available. Retrying with the reported
minimum is exact, while one token less remains insufficient. files, read,
history text/commit results,
context, and JSON keys pages use deterministic operation-aware fitting. Other
shapes fail loudly instead of dropping evidence without a valid continuation.
MCP counts include text/structured-content duplication in dual mode. Tool
schemas, provider framing, and JSON-RPC envelopes remain outside the per-call
boundary; use the wire-cost harness for those connection-level costs.
The default tokenizer is cl100k_base. Exact built-in modes are cl100k_base,
o200k_base, o200k_harmony, p50k_base, p50k_edit, r50k_base, and
gpt2.
estimate is an inexact heuristic for providers whose tokenizer is not
available locally. It does not guarantee that a provider will accept a payload
at the reported budget; responses mark this with token_count_exact: false.
savings uses the same tokenizer and marks whether its local counts are exact.
The response_accounting.estimated_net_tokens_saved retrieval-compression
value subtracts every
recorded complete response from the represented-source baseline, so metadata,
protocol, plan-only, discovery, and history costs can reduce the net result.
It does not establish task-level savings. Only successful persisted responses
contribute token deltas. observed_task_savings therefore reports no task-level
percentage until host-linked outcomes and the declared cost categories are
available. It prominently separates observed failed calls and successful
responses with unknown relevance; failure-response tokens, retry chains,
superseded calls, and task outcomes remain explicitly unknown rather than being
treated as zero. observations retains the lower-level persisted failure and
exact expected-hash counters.
Source limits do not include JSON keys, paths, scores, hashes, receipts, tool
schemas, or JSON-RPC envelopes. total_response_tokens captures the applicable
DTO or MCP tool-result boundary; benchmark utilities continue to measure
complete transport costs when they include schemas and JSON-RPC envelopes.
Every source range has a 128-bit BLAKE3 fingerprint for local identity and duplicate suppression. Direct search/read responses carry it with the range; context places hashes once in the aligned receipt table. Receipts transfer grounded context without creating a LeanToken session, transcript, or model state.
Failed CLI commands emit a human-readable Error: ... line by default. With
--json, they emit one compact JSON object on stderr and retain the existing
top-level error string as an established error-wire field. The additive category
field is the stable machine-readable discriminator. Request errors may also
include the public field, requested, and active limit; clients should
branch on these fields instead of parsing error text.
Argument parsing failures use invalid_input and retain clap's exit status 2.
Help and version output are not failures: they remain on stdout with status 0,
even when --json is present. Errors after successful parsing retain status 1.
JSON mode suppresses tracing on stderr so each failure remains one complete JSON
document.
Current category values are:
| Category | Condition |
|---|---|
invalid_input |
Invalid values, missing arguments, or typed input validation |
input_too_long |
A public input crossed its byte limit |
invalid_request |
An audited caller-usage conflict |
request_limit_exceeded |
A result, token, context, or collection limit |
not_indexed |
A requested path or symbol is absent from the index |
index_not_ready |
No repository generation has committed yet |
stale_cursor |
A cursor is malformed or belongs to another request/generation |
request_cancelled |
Cooperative cancellation stopped the request |
path_outside_root |
A path escaped the repository boundary |
unsupported_language |
Structured parsing is unavailable for the language |
invalid_regex, invalid_glob |
Invalid pattern syntax |
repository_configuration |
Invalid root, cache binding, or configuration |
repository_index_limit |
Repository discovery crossed a hard bound |
runtime_unavailable |
A required runtime capability is unavailable |
retryable_conflict |
Concurrent repository state requires a retry |
serialization_failure |
A response or persisted value could not be serialized |
response_accounting_invariant |
Response token accounting failed to converge |
cache_prune_failure |
Cache maintenance could not prune an artifact |
setup_failure |
Setup or installation state violated an invariant |
operation_failure |
A product operation reached an unexpected state |
internal_error |
An implementation, storage, I/O, or other unexpected failure |
The structured fields are an allowlist. I/O and SQLite failures use
category: "internal_error"; the typed failure categories above identify
other audited boundaries without exposing additional implementation details.
Future releases may add categories or optional fields, so consumers should
ignore keys and category values they do not know.
Oversized inputs, invalid regular expressions or globs, stale cursors,
unsupported structured reads, and unsafe paths return model-visible tool errors
without terminating the server. Their structured category values are stable
enough for client branching, while messages never echo caller-supplied or
resolved paths.
Internal repository configuration, storage, and I/O failures are logged without
including source bodies and are returned as generic MCP internal errors.
Default limits include:
- 2 MiB maximum indexed file size;
- 20 default and 100 maximum results per request;
- 80 lines or 32 KiB per search chunk;
- up to four indexing workers by default; override with
--max-index-workers; - 64 KiB query input and 4 KiB path/pattern input.