Agent integration
Give your coding agent complete, repeatable answers about unused code, duplication, complexity hotspots, and boundary violations, plus auto-fix. Works through the CLI or MCP in Claude Code, Cursor, and Windsurf.
Your coding agent writes code fast, but it cannot see the whole codebase at once. It cannot build a module graph, follow re-export chains, find duplication across thousands of files, or score complexity hotspots. Fallow does that analysis for the agent. The agent calls fallow through the CLI or MCP and gets the same complete result on every run.
Any agent that can run shell commands can use fallow. The CLI is the primary interface. MCP is an optional layer on top that adds typed tools.
Why agents need fallow
To analyze a codebase, a tool must build the full import graph and walk it. An agent that reads files into its context window cannot do this.
| What agents cannot do | What fallow does |
|---|---|
| Build a complete module graph across thousands of files | Builds and caches the full graph in one pass |
| Track re-export chains through barrel files | Resolves export * chains through unlimited levels |
| Know if an export is used somewhere outside their context window | Checks every import in the codebase |
| Detect code duplication across files they have not seen | Finds clones across all files with a suffix array algorithm |
Find which package.json dependencies are unused | Traces imports and script binaries to real usage |
| Guarantee completeness (no missed files, no false negatives) | Deterministic: the same input always gives the same output |
CLI: the primary agent interface
Every AI coding agent can run shell commands, so you do not need MCP:
# Full dead code analysis with JSON output
fallow dead-code --format json
# Only check changed files (great for agent PR workflows)
fallow dead-code --changed-since main --format json
# Find code duplication
fallow dupes --format json
# Preview what auto-fix would remove
fallow fix --dry-run --format json
# Apply fixes (agents should use --yes to skip confirmation)
fallow fix --yes --format json
# Detect feature flags and environment gates
fallow flags --format json
# List project info (plugins, entry points, file count)
fallow list --format json
When an agent runs fallow, always use --format json. The agent can parse JSON output without guessing. The human-readable format also works, but it is less exact to parse.
To read fallow JSON output in TypeScript, use import type { CheckOutput, HealthOutput, DupesOutput, AuditOutput } from "fallow/types". These types cover the full output contract. Fallow generates them from the same schema it uses internally. SchemaVersion is fixed to a literal when the types are generated, so a major schema change causes a compile error at your call sites. Your code cannot drift out of sync without notice.
MCP: structured tool calling
For agents that support MCP (Model Context Protocol), fallow-mcp gives each analysis as a structured tool. The agent gets typed inputs and outputs and does not parse CLI text.
The MCP server uses stdio transport and a hybrid adapter. It runs supported requests in process. For options that only the CLI command has, it starts a managed fallow CLI subprocess. Set FALLOW_BIN only when CLI-backed calls need a specific binary. The default is fallow in PATH.
The fallow npm package includes the fallow-mcp launcher. You can also install it with cargo install fallow-mcp, or download a binary from GitHub Releases.
Register the server with the Claude Code CLI. The CLI writes the configuration for you:
# Available in every project on this machine
claude mcp add fallow --scope user fallow-mcp
# This repository only, checked in for the whole team
claude mcp add fallow --scope project fallow-mcp--scope project creates .mcp.json in the repository root. fallow agent install writes the same entry for each harness it detects. It first checks which launcher works. You can also write the file by hand:
{
"mcpServers": {
"fallow": {
"command": "fallow-mcp"
}
}
}Run claude mcp list to confirm fallow is registered.
Claude Code does not read mcpServers from .claude/settings.json. It ignores a server there and shows no error. Use .mcp.json or claude mcp add.
Cursor reads MCP servers from mcp.json. To use fallow in one project, create .cursor/mcp.json in the project root. To use it in all projects, create ~/.cursor/mcp.json in your home directory:
{
"mcpServers": {
"fallow": {
"command": "fallow-mcp"
}
}
}Any MCP-compatible client can connect to fallow-mcp over stdio transport:
# Start the MCP server directly
fallow-mcp
# With a custom fallow binary path
FALLOW_BIN=/usr/local/bin/fallow fallow-mcpConfigure your client to launch fallow-mcp as a stdio subprocess.
Installed fallow as a project devDependency? "command": "fallow-mcp" expects the binary on your PATH, as with a global install. A binary in node_modules/.bin/ is not on PATH, so the server fails to start with ENOENT. Start it through the runner of your package manager, which finds the binary in node_modules/.bin/:
{
"mcpServers": {
"fallow": {
"command": "npx",
"args": ["--yes", "--package", "fallow", "fallow-mcp"]
}
}
}The npm and Bun examples explicitly select the fallow package that provides the launcher. Start the agent from the project root, so the runner finds the local install. To use a specific fallow binary, you can still set FALLOW_BIN.
Available MCP tools
| Tool | Description |
|---|---|
analyze | Full dead code analysis (fallow dead-code --format json). Finds unused files, exports, types, dependencies, and enum and class members. Also finds unresolved imports, unlisted dependencies, duplicate exports, circular dependencies, boundary violations, and stale suppressions. Other findings: re-export cycles (barrel files in a loop, which break re-exports with no error), package cycles (workspace packages that import each other in a loop), framework component findings (unused props, emits, inputs, outputs, Svelte events, unrendered components, and unprovided injects), and rule-pack policy violations (banned calls and banned imports from the rulePacks config key). For pnpm catalogs, it finds unused entries, empty groups, and unresolved references (a package.json names a catalog that does not declare the package, so pnpm install would fail). Private type leaks are opt-in: pass issue_types: ["private-type-leaks"]. Deprecated exports in use are opt-in too: pass issue_types: ["deprecated-exports-in-use"]. Each finding has a stable finding_id. An id that is absent from a scoped run, or from a run with other config, is unknown, not resolved. |
check_changed | Incremental analysis of changed files (fallow dead-code --changed-since). Each finding has the same finding_id as in a full analyze run. The run is scoped, so an id that is absent from it is unknown, not resolved. |
security_candidates | Unverified local security candidates, not confirmed vulnerabilities (fallow security --format json). Each entry in security_findings[] has category, CWE, severity, evidence, trace, optional reachability, blind-spot counters, and optional unresolved_callee_diagnostics samples for dynamic callee follow-up. severity is a review-priority tier, not a verified vulnerability verdict. Each finding also has a candidate object (source_kind, sink, boundary) that the agent can act on. A URL-category sink can have url_shape (fixed-origin-dynamic-path or dynamic-origin). A finding can also have an optional taint_flow source-to-sink triple and has a stable finding_id (equal to the SARIF fingerprint) to match findings across runs. There is no impact field: the agent decides exploitability. Set surface: true to add top-level attack_surface[] entries with defensive-boundary prompts for a verifier. Set gate to new for changed-line candidates, or to newly-reachable for candidates that became reachable from entry points. newly-reachable requires changed_since. reachability.untrusted_source_trace is module-level import context only and does not prove value flow. reachability.taint_confidence gives each reachable candidate a tier: arg-level (strong: the sink argument traces to a source read in the same module) or module-level (weak: only the module is import-reachable from a source). Use this field for the tier, not the evidence text. Verify trace, reachability context, severity, and evidence before you edit code. Supports root, config, workspace, paths, changed_since, changed_workspaces, surface, gate, no_cache, and threads; paths passes repeated fallow security --file filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. For the verifier packet and verdict recipe, see Security agent verification. The tool reads FALLOW_DIFF_FILE from the server environment to limit results to diff lines. For large repos, raise FALLOW_TIMEOUT_SECS. |
inspect_target | Collects all evidence for one file or exported symbol in one call. For a file, use target: { type: "file", file }. For a symbol, use target: { type: "symbol", file, export_name }. Returns kind: "inspect_target", the normalized target identity, trace_file, optional trace_export, and these items for the file: dead-code actions, duplication groups, complexity findings, and security candidates. Each evidence section has status and scope. For a symbol target, the tool warns when supporting evidence covers the whole file. Set the opt-in symbol_chain: true to add the symbol-level call chain (the same data as fallow trace) as a symbol_chain evidence section. It is off by default, applies only to a symbol target, and is best-effort and syntactic. It is not part of the ranking. Supports root, config, production, workspace, no_cache, and threads. production applies to trace, dead-code, and health evidence only. For large repos, raise FALLOW_TIMEOUT_SECS. |
code_execute | Bounded, read-only Code Mode: run several fallow analysis calls in one JavaScript snippet. The snippet gets { fallow, root } and returns JSON-serializable data. It can call read-only helpers such as fallow.projectInfo, fallow.audit, fallow.checkHealth, and fallow.run(tool, params) for the same allowlist. Fix tools that change files are not available. A snippet that passes save_baseline, save_regression_baseline, or save_snapshot gets an error. Call the standalone tool for the write. The sandbox has no filesystem, network, imports, eval, Function, process, require, Deno, Bun, or shell access. Params: code, optional root, timeout_ms (capped at 30000), and max_output_bytes (capped at 4000000). |
find_dupes | Code duplication detection (fallow dupes --format json). Each clone group has a stable fingerprint and a numeric spread. Near-miss groups also have similarity. Pass near: true to add function-scoped near-miss clones. To inspect a group, pass its fingerprint to trace_clone. By default, the tool ignores import declarations. Pass ignore_imports: false to count them. Pass ignore_symlinks: true to omit clone instances whose path is a symlink or lies under a symlinked directory. Without the parameter, the project config duplicates.ignoreSymlinks decides. stats.clone_groups_ignored counts groups that the config filters out. stats.near_candidates_skipped reports near-miss comparisons that fallow skipped because of the comparison limit. By default, clone instances do not include their verbatim source text. Pass include_fragments: true to include it. |
find_similar_code | Read-only search for semantically similar functions with the pinned local model (fallow similar-code --format json --quiet). Returns unverified candidates, with provider and model provenance, completion, limits, skips, and cache counts. Scope with files, workspace, changed_since, changed_workspaces, threshold, min_lines, or top. The score is not a probability or verdict. The tool never downloads a model. If setup is missing, ask the user to run fallow similar-code setup --local. |
inspect_similar_code | Reproduce one candidate_id and return bounded source, graph, ownership, churn, test, deterministic-duplication, and side-effect evidence. Read-only. Use it before you judge a candidate. When evidence is not available, do not judge. The tool does not write verdicts or edit source. |
fix_preview | Dry-run auto-fix preview (fallow fix --dry-run --format json) |
fix_apply | Apply auto-fixes (fallow fix --yes --format json) |
guard | Before you edit files, reports the architecture rules for them: boundary zone, allowed import zones, forbidden calls, and rule-pack policies. |
check_architecture | After you edit files, reports import cycles, boundary violations, and rule-pack policy violations (fallow architecture --format json). Returns kind: "architecture", with the same arrays and stable finding_id values as analyze. Set cycles, boundaries, or policy to select one kind. Code Mode exposes the tool as checkArchitecture. The analyze tool continues to report these findings in its kind: "dead-code" result. |
check_health | Complexity metrics, file health scores, hotspots, and refactoring targets (fallow health --format json). Set file_scores: true for maintainability index, hotspots: true for churn analysis, targets: true for ranked recommendations sorted by efficiency, trend: true for per-metric deltas against the most recent snapshot. Set complexity_breakdown: true to add a contributions[] array to each complexity finding. It lists each decision point (each else-if, nested if, boolean operator, loop, case, and so on) with its source line and cyclomatic and cognitive weight. Use it to explain why a function scored high and which lines to refactor. Set css: true to add a css_analytics section. It reports structural CSS problems that per-rule linters do not add up (specificity hotspots, !important density, deep nesting, design-token sprawl). It also reports cleanup candidates (unreferenced custom properties and @keyframes, dead Vue <style scoped> classes, unused @property and @layer, Tailwind arbitrary-value bypasses, Tailwind v4 @theme unused_theme_tokens, duplicate declaration blocks) and undefined-reference candidates. Each has a read-only verification step in its actions[]. The section also has token_consumers, a reverse index of where each design token is used: for each token, a consumer_count and a sample consumers[] list with locations. An agent can read the blast radius of a token before it changes the token. It covers Tailwind v4 @theme tokens (kind theme-var, css-var, utility, or apply) and CSS-in-JS token definitions: StyleX defineVars, vanilla-extract createTheme-family definitions, and PandaCSS defineTokens. For these, token is binding-qualified, and kind is js-member for member access or js-call for Panda token(...) calls. consumer_count is a static lower bound: fallow cannot see a computed class name like bg-${c}, dynamic import strings, unresolved aliases, generated package state, or computed token access. The field is context, not a finding, so it has no actions[]. css is opt-in because it reads project stylesheets and cross-checks source files. Fallow parses standard CSS and standard Vue, Svelte, and Astro <style> blocks structurally. It scans Sass and Less sources only where it can stay conservative without expanding preprocessor semantics. For a project with no git repository (Yandex Arc, Mercurial, Perforce), set churn_file to a fallow-churn/v1 JSON path. Hotspots, ownership, and targets then use that imported VCS history in place of git. The file defines the window, so since only labels the output. Set runtime_coverage to merge a V8 or Istanbul coverage dump. Tune it with min_invocations_hot (default 100), min_observation_volume (default 5000), and low_traffic_threshold (default 0.001). Set group_by to owner, directory, package, or section to split the results into groups. Each group gets its own vital_signs, health_score, and optional coverage_source_consistency, computed from the files of that group. Top-level metrics stay project-wide. SARIF results get properties.group, and CodeClimate issues get a top-level group field. Findings have a bucketed coverage_tier (none, partial, or high), and CRAP-triggered entries have coverage_source. summary.coverage_source_consistency tells you if those sources are uniform or mixed. The action is add-tests or increase-coverage when more coverage can still clear CRAP, or refactor-function when cyclomatic >= maxCrap. See "Structured actions" below. |
check_runtime_coverage | Merges runtime-coverage data into the health report. The required coverage param takes a V8 coverage directory, a single V8 coverage JSON, or an Istanbul coverage-final.json. A single local capture is free. Continuous or multi-capture monitoring (a V8 directory with more than one JSON file) needs a license. See fallow license. Tune it with min_invocations_hot (default 100), min_observation_volume (default 5000), low_traffic_threshold (default 0.001), max_crap (default 30.0), top, and group_by. Protocol-0.3+ sidecars add a summary.capture_quality block that flags short-window captures. Cloud runtime rows can have resolutionStatus and mappingQuality in function-list JSON, and resolution_status and mapping_quality in runtime-context JSON. Read the confidence table below before you act on file-level runtime signals. On large dumps, the call can take longer than the default 120s timeout. Raise FALLOW_TIMEOUT_SECS for that case. When you have a coverage dump, use this tool, not check_health. |
get_hot_paths | Runtime-context slice from the same local runtime coverage pipeline. Same input schema and license rules as check_runtime_coverage. runtime_coverage.hot_paths lists production hot paths, sorted by percentile and invocation count. Each hot path has an optimization_target block. To pick speed work, sort by optimization_target.cost_basis (inner_iterations first), then by cost_score. |
get_blast_radius | Runtime-context slice for blast-radius review. Same input schema and license rules as check_runtime_coverage. runtime_coverage.blast_radius has stable fallow:blast:<hash> IDs, caller counts, traffic-weighted caller reach, optional cloud deploy touch counts, and low, medium, or high risk bands. |
get_importance | Runtime-context slice for production-importance review. Same input schema and license rules as check_runtime_coverage. runtime_coverage.importance has stable fallow:importance:<hash> IDs, invocations, cyclomatic complexity, owner count, a 0-100 score, and a templated reason. |
get_cleanup_candidates | Runtime-context slice for cleanup review. Same input schema and license rules as check_runtime_coverage. runtime_coverage.findings has safe_to_delete, review_required, low_traffic, and coverage_unavailable verdicts. |
get_cloud_runtime_context | Runtime-context slice from Fallow Cloud, not from a local dump (fallow coverage analyze --cloud --format json). This is the only fallow MCP tool that makes a network call. Requires repo (owner/repo). project_id, period_days (1 through 90, default 30), environment, and commit_sha narrow the cloud selection. production, top, and min_invocations_hot work as on check_runtime_coverage. The key is FALLOW_API_KEY in the server environment, never a parameter. The key must belong to an organization on the Team tier. The tool does not use the local license. Without the key, the tool refuses the call before anything runs and returns code: "cloud_api_key_missing". Returns the same runtime_coverage block as the local tools, matched against the checkout at root. If root is on a different revision, findings is empty with no error, and the response has a cloud_functions_unmatched warning. Confirm that runtime_coverage.summary.data_source is cloud. Read the source-map confidence table below before you act on file-level signals. |
get_token_blast_radius | Design-token blast radius from static analysis (free, no runtime dump). Runs fallow health --css --format json. For each token, css_analytics.token_consumers gives its defining site, a consumer_count, and a capped consumers[] sample of {path,line,kind}. Covers Tailwind v4 @theme tokens (token is the ---prefixed custom property, and kind is theme-var, css-var, utility, or apply). Also covers CSS-in-JS token definitions: StyleX defineVars, vanilla-extract createTheme, createThemeContract, and createGlobalTheme, and PandaCSS defineTokens. StyleX and vanilla-extract consumers use binding-qualified dotted paths like vars.color.primary with kind js-member. PandaCSS consumers use token paths like tokens.colors.brand with kind js-call. With this tool, you do not need css=true on check_health. consumer_count is a static lower bound: fallow cannot see a computed class name like bg-${c}, dynamic import strings, unresolved aliases, generated package state, or computed token access. Use it to size the impact of an edit or a rename, not to decide on a deletion. For Tailwind, the dead-token verdict is unused_theme_tokens. CSS-in-JS tokens have no dead-token finding, so a CSS-in-JS consumer_count of 0 is weaker evidence. |
audit | Audit changed files for dead code, complexity, duplication, and styling (fallow audit --format json). Returns a verdict: pass, warn, or fail. Set base for the comparison ref and gate to new-only or all. Set include_entry_exports=true to also catch typos in entry-file exports (meatdata vs metadata). For accurate per-function CRAP scores in the health part, set coverage to an Istanbul coverage-final.json path. When the paths need rebasing for CI or Docker checkouts, also set an absolute coverage_root. Audit includes styling analytics by default, with styling_findings and css_analytics when the project has CSS or CSS-in-JS evidence. Set css_deep=false to skip project-wide styling reachability, or css_deep=true to turn it on when the config turns it off. Set runtime_coverage (V8 dir, V8 JSON, or Istanbul JSON) to add runtime-coverage findings to the same audit call. Tune it with min_invocations_hot (default 100). When the agent environment sets FALLOW_DIFF_FILE or FALLOW_CHANGED_SINCE, runtime_coverage.verdict ranks hot-path-touched above cold-code-detected for PR review. |
decision_surface | Lists the few structural decisions in a change that have real consequences (coupling, public API, dependency). Each one is a judgment question with the expert to ask. The list is ranked, capped, and anchored to signal_id values. The tool reads the same graph-derived review brief as fallow review (the brief path of fallow audit). The decision surface has exactly three categories: coupling-boundary (a new cross-zone dependency edge), public-api-contract (a new exported public API, or a changed contract that modules outside this diff use), and dependency (a new third-party dependency). Fallow derives each signal_id from the graph, the same way every run. Decisions are ranked by consequence (blast radius x reversibility), and each names the expert to ask. A decision can have previous_signal_id, the id of the anchor before a git mv, so a review tool can attach an earlier comment again after a rename. Each decision also has a tradeoff clause that states the structural cost as a fact, for example "Couples app to infra; N in-repo modules already depend on this anchor". It also has internal_consumer_count: the number of in-repo modules outside the diff that already depend on the anchor. Use this number to judge reversibility. It is separate from blast, which is only for ranking. Set base for the comparison ref and max_decisions to cap the number of decisions (default 4, clamped to 3 to 5). The brief always exits 0. It includes the verdict for information only and never gates. |
fallow_explain | Explain one issue type without running analysis (fallow explain <issue-type> --format json). Returns the rationale, an example, fix guidance, and the docs URL. |
project_info | Project metadata, including plugins, files, and entry points (fallow list --format json). The entry points are the ones that the analysis uses. To get only some sections, set entry_points, files, plugins, or boundaries to true. |
recommend | Proposes a fallow config for a project that has none yet (fallow recommend --format json). Read-only. It detects the framework, workspace, and tooling, and returns a loader-validated proposed_config that you can write as-is. It also returns a decisions[] list with a class for each setting: auto (decided from detection and applied without a prompt), default (a stated default with a rationale that you can override), or taste (a subjective choice, shown as an AskUserQuestion-shaped prompt with no preset answer). For a TypeScript project, it adds an informational typeAware.enabled decision with the opt-in command, the cost, and a guide link. It never writes this setting into proposed_config. Takes root only. Writing no config at all is a valid outcome. |
feature_flags | Detect feature flag patterns in the codebase (fallow flags --format json). Finds environment variable flags, SDK calls from common providers, and config object patterns. Set top to limit the results. |
list_suppressions | Lists active fallow-ignore suppression markers by file, with line, kind, level, reason, and a stale cross-reference (fallow suppressions --format json). Read-only. Use it to see what a clean verdict hides. It is not a gate and always exits 0, also when suppressions exist. Scope it with workspace, changed_since (the usual scope for a pull-request review), or repeated file entries. It runs a full analysis, so raise FALLOW_TIMEOUT_SECS on large repos. |
list_boundaries | Architecture boundary zones and access rules (fallow list --boundaries --format json). Returns zone definitions, access rules, file counts for each zone, and logical_groups[]. Each logical group is an autoDiscover parent before expansion, with: verbatim paths, discovered children, a status enum (ok, empty, or invalid_path), a summed file_count, optional authored_rule, optional fallback_zone cross-reference for the Bulletproof case, optional merged_from for duplicate parent declarations, optional original_zone_root echo for monorepo subtree scopes, and optional child_source_indices attribution for multi-path autoDiscover. If no boundaries are configured, returns {"configured": false}. |
trace_export | Trace why an export is used or unused (fallow dead-code --trace FILE:EXPORT_NAME --format json). Required file and export_name params. Returns file reachability, entry-point status, direct references, re-export chains, and a reason string. If export_name is a class, enum, or store MEMBER and not a top-level export, the tool returns a member trace (member_name, member_kind, owner_export, owner_is_used). The member trace gives the reachability and usage of the owning declaration and points to the matching --unused-<kind>-members command. Check which field is present (export_name or member_name) to tell the two apart. Use it before you delete an export that looks unused, or to debug an unused-class-member finding. |
trace_symbol | Trace exact TypeScript symbol references, namespaces, aliases, and re-export hops (fallow dead-code --type-aware --trace FILE:EXPORT --format json --quiet). Required file and export_name; optional type_aware_projects and type_aware_require (best-effort or complete). It adds semantic proof to the project-wide usage evidence from fallow. It does not return TypeScript compiler diagnostics or lint findings. |
symbol_impact | Return exact-symbol consumers, transitive affected files, and targeted tests for a TypeScript export (fallow dead-code --type-aware --symbol-impact FILE:EXPORT --format json --quiet). Required file and export_name; optional type_aware_projects and type_aware_require (best-effort or complete). Use it to plan a rename, deletion, or API change. It is change-impact evidence for planning. It does not replace tsc or Oxlint. |
trace_file | Trace all graph edges for a file (fallow dead-code --trace-file PATH --format json). Required file param. Returns reachability, entry-point status, exports, imports-from, imported-by, and re-exports. Use it to find out if a file is isolated, barrel-only, or imported by live entry points. |
trace_dependency | Trace where a dependency is imported (fallow dead-code --trace-dependency PACKAGE --format json). Required package_name param. Returns importing files (each file once), the files whose every import of the package is type-only, the number of importing files, used_in_scripts, and is_used. Optional usage, specifiers, sites, limit, cursor, and closure_depth params add the per-name usage object of fallow trace --dependency. used_in_scripts is true when package.json scripts or CI configs such as .github/workflows/*.yml or .gitlab-ci.yml call the package. is_used combines imports and scripts, the same way as the unused-deps detector. So a build tool like microbundle or vitest that only scripts call counts as used. Use it before you remove a dependency or move it between dependencies and devDependencies. |
trace_clone | Shows the details of one duplicate-code clone group (fallow dupes --trace <spec> --format json). Select the group with exactly one of: file and line (a source location), or fingerprint (a dup:<id> from an earlier find_dupes result). Returns the matched instance and every clone group that contains it. Each traced group has its fingerprint, an extract-function suggestion, and an optional suggested_name. Supports mode, near, min_tokens, min_lines, threshold, skip_local, cross_language, ignore_imports, and ignore_symlinks. To trace a near-miss group, use the same near value as the find_dupes call that found it. |
trace_import_path | Shortest import path between two modules (fallow trace --path FROM TO --format json). Required from and to file params. Returns reachable, hops, and a path array of import hops, each with type_only and import_line. An unreachable pair is a valid result: reachable is false and path is empty. When both params name the same module, the result is reachable: true. Both cases report hops: 0, so check reachable, not the count. Use it to see how one module comes to depend on another. |
trace_error | Maps the frames of a runtime stack trace to the definitions they name (fallow trace-error --format json). Paste the trace text in the required trace param, with an optional source label. The tool classifies each frame as in_project, node_modules, or out_of_corpus, then resolves the in-project frames. A frame that matches more than one definition reports ambiguous and lists them. A frame that matches nothing reports not_found. A frame that the tool did not try reports not_attempted. The tool reads no source maps, so it reports a frame in generated build output as generated output. See fallow trace-error. |
impact | Reads the local, opt-in Fallow Impact value report (fallow impact --format json). Runs no analysis. Reports the current finding counts, the trend since the last recorded run, pre-commit gate containment, and (on impact v1.5+) resolved and suppressed attribution. The history comes from a per-project file in the private config dir of the user, never inside the repo. Read-only, and takes root only (no config, no_cache, or threads). The enable, disable, and default commands that change state are not available. The report has enabled_source (project, user, or default). On a project where tracking was never enabled, it returns a full {"enabled": false, ...} report, never {}. An agent checks enabled and enabled_source, then record_count. It recommends fallow impact enable only when explicit_decision is false (the user was never asked). When it is true (the user turned tracking off here), the agent says nothing. This is a local developer signal. Fallow never records in CI, so in CI the tool returns an empty report. It is not a CI metric. |
impact_closure | Trace the impact closure for one file (fallow dead-code --impact-closure <path> --format json). Returns the transitive set of affected files that are not in the diff, and the coordination gaps for modules that use the file contract. Supports path, root, config, production, workspace, no_cache, and threads. Use it as evidence to plan a review. It does not prove that the affected files are wrong. |
impact_all | Combines all tracked projects on this machine into one cross-repo value report (fallow impact --all --format json). Runs no analysis. Reads the per-project histories in the user config dir and returns kind: "impact-cross-repo" with project_count, tracked_count, unreadable_count, a totals sum over all tracked projects (also repos that are now deleted from disk), and a projects[] array. Each row has a hashed project_key (always present, never a filesystem path), an optional label (the folder name of the repo), last_recorded, and the same per-project report shape as impact. Rows from older stores have no label, so use project_key for those. Use this tool for all projects together. Use impact (with root) for one project. sort sets the row order (recent (default), resolved, contained, or name). limit caps the rows, but totals still cover every project. The report counts enabled projects with no recorded history but does not list them. Read-only. The enable, disable, default, and reset commands that change state are not available. This is a local developer signal: it is empty in CI and is not a CI metric. |
Runtime source-map confidence
When the response capabilities array has function_identity_v2, cloud runtime tools can add source-map confidence metadata. Function list responses use resolutionStatus and mappingQuality. Runtime-context responses use resolution_status and mapping_quality. These fields tell you how much to trust the source map. They do not tell you if the function ran.
| Values | Meaning | Agent action |
|---|---|---|
resolved + high | The source map resolved the generated position to original source. | Trust the file path and line number, and refer to the original source. |
fallback + medium | A source map exists, but it does not cover this generated position. | Treat the file-level signal as approximate. Before a precise edit, ask the developer to rebuild with denser source maps. |
unresolved + low | No matching source map was uploaded for this bundle and commit. | Before you act on file-level coverage signals, ask the operator to upload the source map. |
null + null | The row does not include source-map confidence metadata. | Treat the row as missing confidence metadata. Do not downgrade it to low without other evidence. |
{
"tool": "analyze",
"arguments": {
"production": true,
"issue_types": ["unused-exports", "unused-files"]
}
}Tool hints
analyze, check_changed, find_dupes, and check_health can write a baseline or snapshot file. These four tools therefore declare readOnlyHint: false and destructiveHint: false. A host that approves read-only tools without a confirmation now asks before it runs them. code_execute stays read-only.
The save_baseline, save_regression_baseline, and save_snapshot parameters write only inside the project root, its Git work tree, the CI workspace (GITHUB_WORKSPACE, CI_PROJECT_DIR), RUNNER_TEMP, or the system temp directory. Another path returns a tool error. See Save destinations.
Review optional props absent from callers
To request absent component props, pass issue_types: ["absent-component-props"] to analyze:
{
"name": "analyze",
"arguments": {
"root": "/path/to/project",
"issue_types": ["absent-component-props"]
}
}
The rule is off by default. Selecting it opts in to findings about optional props that the component reads but no inspected reachable caller supplies. A finding includes its declaration and inspected callers. Review defaults and API intent before proposing an edit. Every action requires manual review; no action automatically removes a prop or conditional UI.
Notable tool parameters
Some tools take more parameters than the common root, config, no_cache, and threads:
| Tool | Parameter | Type | Description |
|---|---|---|---|
analyze | boundary_violations | bool | Short form of issue_types: ["boundary-violations"] |
analyze | finding_ids | string[] | Report only these finding_id values, like fallow dead-code --finding-id. The response adds finding_id_query. A missing id is resolved only when conclusive is true and analysis_fingerprint matches the stored value. A query always runs the full dead-code family. See finding_id_query. |
find_dupes | changed_since | string | Only report duplication in files changed since a git ref |
find_dupes / trace_clone | near | bool | Also report function-scoped near-miss clones with small structural edits. Default false. |
find_dupes / trace_clone | ignore_symlinks | bool | Omit clone instances whose path is a symlink or lies under a symlinked directory, like fallow dupes --ignore-symlinks. Without the parameter, the project config decides. Set false to report them also when the config sets ignoreSymlinks. In Code Mode, the combined tool takes the same value as dupes_ignore_symlinks. |
find_dupes | include_fragments | bool | Add the verbatim source text to each clone instance. Default false, because file and the line and column range point to the same code, and the text is most of the response. Set it to true only when you need the source without reading the files. |
find_dupes / trace_clone | min_occurrences | integer (≥ 2) | Minimum occurrences for a reported clone group. Default 2. Raise it to skip clones that occur only twice and find the widespread copies that are worth a refactor. When the filter hides a group, the JSON output has stats.clone_groups_below_min_occurrences. |
find_similar_code / inspect_similar_code | threshold | number (0 to 1) | Cutoff for the search. The value is specific to the model. It is not a probability or a quality gate. |
find_similar_code / inspect_similar_code | min_lines / top | integer | min_lines skips tiny functions. top caps the returned candidates. |
find_similar_code / inspect_similar_code | files | string[] | Keep only candidate pairs that touch one of these root-relative files. |
inspect_similar_code | candidate_id | string | Required immutable candidate ID from find_similar_code. |
security_candidates | gate | string | new gates changed-line candidates. newly-reachable gates candidates that became reachable from entry points, and requires changed_since. |
audit | gate | string | new-only gates only introduced findings. all gates every finding in changed files |
audit | css_deep | bool | Controls deep styling reachability in audit. Leave it out for the default project-wide styling pass. Set false to skip project-wide styling reachability. Set true to force --css-deep when the config turns it off. |
audit / check_health | coverage | string | Path to an Istanbul-format coverage-final.json for accurate per-function CRAP scores. Typed and CLI-backed routes use the same order: the tool parameter first, then FALLOW_COVERAGE, then health.coverage from config. |
audit / check_health | coverage_root | string | Absolute prefix that fallow removes from file paths in coverage data before it adds the project root. Use it when CI or Docker generated the coverage under a different checkout root. Typed and CLI-backed routes use the same order: the tool parameter first, then FALLOW_COVERAGE_ROOT, then health.coverageRoot from config. |
analyze / check_changed / audit | include_entry_exports | bool | Also report unused exports in entry files. This catches typos in framework-convention exports, for example meatdata vs metadata. The result is a logical OR with the includeEntryExports config value. |
analyze / check_changed / audit / fix_preview / fix_apply | show_cascade | bool | Also report the unused exports, types, class members, and enum members of unused files, like --show-cascade. Default false: the response hides them and counts them in cascade_hidden. For fix_preview and fix_apply, the fix also removes them. The Code Mode combined call takes the same parameter. See Findings in unused files. |
fallow_explain | issue_type | string | Issue type token or rule id to explain |
project_info | entry_points | bool | Request detected entry points |
project_info | files | bool | Request all discovered source files |
project_info | plugins | bool | Request active framework plugins |
project_info | boundaries | bool | Request architecture boundary zones and rules |
check_architecture | cycles / boundaries / policy | bool | Select one kind of architecture finding, like fallow architecture --cycles, --boundaries, and --policy. Without a selector, the tool reports all kinds. |
check_architecture | baseline | string | Path to a saved dead-code baseline. The tool reports only the new findings. |
check_architecture | file | string[] | Report only the findings in these project-relative files. |
check_architecture | group_by | string | Group the output by owner, directory, package, or section. The result then has kind: "architecture-grouped". |
analyze | group_by | string | Group output by owner (CODEOWNERS), directory (first path component), package (workspace), or section (GitLab CODEOWNERS [Section] headers, with owners metadata per group) |
Structured actions in tool responses
Every finding from every tool has a structured actions array. An agent can use it to apply fixes or suppressions in code:
- Dead code (
analyze,check_changed): a fix action (for exampleremove-export) and a suppress action. Theauto_fixableflag tells the agent whether to callfix_applyor to handle the suggestion by hand. For the action types, see the dead-code CLI reference. - Health (
check_health,audit): complexity findings and styling findings have anactionsarray. For complexity findings, a CRAP and coverage formula picks the primary action fromcoverage_tier,cyclomatic, andmax_crap_threshold. The action types includerefactor-function,add-tests,increase-coverage, andsuppress-line. Styling findings have read-only verification and suppression actions for styling. When you pass--baselineor--save-baseline, or sethealth.suggestInlineSuppression: false, fallow leaves outsuppress-line. It then adds a top-levelactions_meta: { suppression_hints_omitted: true, reason }field (underhealth.actions_metain combined-mode and audit output). Targets getapply-refactoringand a suppress action (when evidence exists). Hotspots getrefactor-fileandadd-tests. - Duplication (
find_dupes,audit):extract-sharedand suppress actions on clone families and groups. - Audit (
audit): the actions from all three parts (dead code, health, duplication).
Command-level next_steps[]
The analyze, check_health, find_dupes, and audit responses (and combined output) also have a top-level next_steps[] array. It is separate from the per-finding actions[]. It lists read-only follow-up commands, based on the findings of the run. Each entry is { id, command, reason }:
- You can run
commandas-is. It is never a fix or another command that changes files. Fallow gives the evidence. The agent decides on the change and applies it. - The stable kebab-case
id(setup,impact-report,trace-unused-export,trace-clone,complexity-breakdown,scope-workspaces,audit-changed) names a verification step to run before you act. Through MCP, use theidto call the matching tool (trace_export,trace_clone,check_healthwithcomplexity_breakdown: true,audit) or acode_executehost call. Do not run the CLIcommandstring in a shell. - A
setupstep (command:fallow schema) comes first, and only on projects with findings, no config, and no CI. By design, no tool runs it for you. Read the manifest, then offer the guided-setup commands (fallow init --agents,fallow hooks install --target agent) to the user. Do not run them without asking. The step goes away when a config exists or afterfallow init --decline. - An
impact-reportstep (command:fallow impact) shows at most once a week. It has the local value digest (commits contained at the gate, findings resolved) when impact tracking is enabled and the results are not zero. The impact store keeps the time of the last step, so the weekly limit is the same for all agents and sessions. The step can show even on a clean run. Tell the user the non-zero numbers in one line.
The array has no duplicates, is in priority order, and has at most three entries. When it is empty, fallow leaves it out. To turn it off, set FALLOW_SUGGESTIONS=off in the server env.
value_schema on add-to-config actions
Fallow emits add-to-config actions for findings that you resolve with a new entry in the fallow config, such as unused-dependency, type-only-dependency, test-only-dependency, and duplicate-export. These actions have an optional value_schema field next to value:
{
"type": "add-to-config",
"config_key": "ignoreDependencies",
"value": "autoprefixer",
"value_schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json#/properties/ignoreDependencies/items"
}
The value_schema URL is a JSON Pointer fragment into the published fallow schema.json. An agent can fetch the linked schema to validate value before it writes the value into the config of a user. For example, it can reject a malformed { file, exports } rule object on the ignoreExports action. The field only adds information. Actions without a schema still work, and agents that ignore the field work as before.
Run-level warnings[]
CLI-backed tool calls run fallow with --quiet, so the caller gets nothing that the CLI writes to stderr. In its place, the root warnings[] array has plain sentences about the run:
- a stale baseline
- each gate that reported
failorwarn - diagnostics that made the analysis less complete
- each request the run could not apply
A request that made the report wider and an output file that the run did not write each get their own sentence. Only a wider report means that the findings cover more than you asked for. A request that the run applied to an empty scope also gets its own sentence. An empty scope cannot have findings. An empty result there means there was nothing to analyze. It does not mean the code is clean.
When the command cannot read a baseline file as its own format, that gets its own sentence, before the staleness sentence. Read baseline_staleness.unrecognised_format first, because such a file suppresses nothing. When the file names a writer that fallow knows, baseline_staleness.saved_by and the warning name the command that saved it. Pass another path, and do not save the baseline again. The sentence also says that the run would fail the --fail-on-stale-baseline gate. Every tool says this except audit, because the audit stale-baseline gate does not apply to the baselines it loads.
The typed in-process route reads the same fields, so a tool gives the same answer on both routes. The warnings do not move or remove other fields. gate_outcomes, baseline_staleness, workspace_diagnostics and request_outcomes stay where they are in the response. A run with nothing to report adds no warnings. A warning never makes the call an error.
Resources
The server also gives read-only reference material as MCP resources. A resource read starts no subprocess and runs no analysis, and your client can cache it by URI. The client reads resources with its own resource tool, for example ReadMcpResourceTool in Claude Code. Each payload is plain JSON. The server version is in _meta.fallow_version of each content item, so a cached copy shows its own version, and the schema resources stay valid strict JSON Schema. The catalogue is fixed at build time, so there are no subscriptions and no list-changed notifications.
| Resource | Content |
|---|---|
fallow://tools | The tool manifest: name, one-line description, nearest CLI fallback, key parameters, license, and read-only flag for every tool |
fallow://issue-types | Every issue type with its command, category, config key, zero-config default severity, opt-in and fixable flags, docs URL, and explain URI |
fallow://explain | Index of every explainable issue type with a one-line summary and its fallow://explain/{issue_type} URI |
fallow://explain/{issue_type} | The explain document for one issue type, the same payload as fallow explain <issue-type> --format json |
fallow://tools/{name} | The long-form guide for one tool, with the detail that does not fit in its tools/list description. fallow://tools/analyze explains the boundary_violations short form, the group_by modes and how to run the next_steps[] follow-up commands. analyze, check_health and get_cloud_runtime_context have a guide. A registered tool without a guide returns code: "no_tool_guide", and a name that is not a fallow tool returns code: "unknown_tool". |
fallow://task-matrix | The task-to-command matrix that also drives AGENTS.md and fallow --help: which read-only command to run before deleting, refactoring, committing, or scoping work |
fallow://schema/config, fallow://schema/plugin, fallow://schema/rule-pack | The JSON Schemas printed by fallow config-schema, fallow plugin-schema, and fallow rule-pack-schema |
{issue_type} takes the bare id (unused-export), the namespaced id (fallow/unused-export), or the CLI filter spelling. An unknown URI returns a structured error, and its data lists the known URIs. For an unknown issue type, data lists the nearest matches. The numeric code is -32002 for clients on protocol versions before 2026-07-28 and -32602 from that version on. So check data, not the code. fallow schema also publishes the same catalogue as mcp_resources.
Combined output from bare fallow
To get a full picture of the codebase in one call, run fallow with no subcommand. It runs all analyses in one pass and returns one JSON object with dead_code, duplication, and health sections:
fallow --format json
The combined output has all dead-code issue types, the duplication findings, and the health metrics.
Environment variables
| Variable | Description |
|---|---|
FALLOW_BIN | Path to the fallow CLI binary for CLI-backed calls. The MCP server checks these in order: this env var, a binary next to fallow-mcp, then fallow in PATH. It has no effect on typed in-process calls. |
FALLOW_TIMEOUT_SECS | Timeout in seconds (default: 120) for typed and CLI-backed calls. A typed call that times out returns FALLOW_MCP_API_TIMEOUT. A CLI call that times out kills the process tree and returns FALLOW_MCP_SUBPROCESS_TIMEOUT. Raise it for very large codebases. |
FALLOW_DIFF_FILE | Path to a unified diff. The analysis tools report only the findings on changed lines. See Diff and ref from the environment. |
FALLOW_CHANGED_SINCE | Git ref. The analysis tools report only the files changed since this ref. See Diff and ref from the environment. |
FALLOW_MCP_WARM_SESSION | Set to 0, false, off, or no to turn off the in-memory store of parsed files (on by default). With the store on, a typed call on files that did not change since an earlier call does no parse work. A changed, added, or removed file makes the call parse the changed files again. The store holds at most 4 file lists and 256 MiB of source. It does not change any answer, and it does not apply to CLI-backed calls. |
Diff and ref from the environment
FALLOW_DIFF_FILE and FALLOW_CHANGED_SINCE come from the server environment, not from the caller. When the server cannot use one of them, the call does not fail. It stands down to the full project, the same as the CLI:
- A
FALLOW_DIFF_FILEthat cannot be read, or that is too large, gives a report at full scope. - A
FALLOW_CHANGED_SINCEref that does not resolve gives a report at full scope.
The response then has a not-applied entry in request_outcomes, with the reason token and the sentence of the CLI, and a sentence in warnings[]. An applied request gives an applied entry with scope_size. The typed route and the CLI route publish the same object, on the dead-code, dupes, health, flags, and combined responses.
A since or changed_since argument that the caller passes, and that does not resolve, still fails the call with FALLOW_CHANGED_FILES_FAILED, because the caller can fix it.
Some tools keep the hard error for a FALLOW_CHANGED_SINCE ref that does not resolve:
auditanddecision_surfaceread the variable as their base ref, not as a request to narrow the report.project_info,list_boundaries, and the trace tools have norequest_outcomesin their response. A ref that stands down would make their result wider, and nothing in the response would say so.
Error handling
The MCP server returns structured JSON errors from both routes:
- Typed API timeout: returns the
FALLOW_MCP_API_TIMEOUTcode. The response comes back in time, but the in-process work can still finish in the background. - CLI exit code 1: the server treats it as success (issues found, not an error) and returns the full JSON output.
- CLI exit code 2+: the server passes through the structured JSON error that the CLI writes to stdout, when there is one. If there is no JSON, the server builds
{"error": true, "message": "...", "exit_code": N}from stderr. - CLI timeout: kills the process tree and returns the
FALLOW_MCP_SUBPROCESS_TIMEOUTcode.