Skip to content

Add headless disktree scan --json and a read-only MCP server - #79

Open
rsonnad wants to merge 1 commit into
tobi:mainfrom
rsonnad:headless-scan-mcp
Open

rsonnad wants to merge 1 commit into
tobi:mainfrom
rsonnad:headless-scan-mcp

Conversation

@rsonnad

@rsonnad rsonnad commented Oct 10, 2026

Copy link
Copy Markdown

What this adds

AI agents and scripts can ask disktree where the space went, without a window and without any way to change anything.

  • disktree scan PATH [--json] prints a stable, documented report: the total plus the largest directories and files, each with path, bytes, human size, type, modified, depth, category and reclaim. Options: --top, --depth (a listing depth over a full scan, so totals stay exact), --apparent-size, --exclude (repeatable), -x/-X, --only dirs|files. Filters: --min-size 1G, --ext iso,dmg, --older-than 6mo|2025-01-31, --name PATTERN, --category cache, --reclaim any|build-output|… (category and reclaim list only the outermost matching folder, so --category cache gives you ~/.cache, not every folder inside it). Without --json it prints a short table.
  • disktree summary PATH [--json]: capacity, used, free and available space for the volume. Nothing is scanned.
  • disktree mcp: a stdio MCP server with scan_path, largest_files, largest_dirs, list_files and disk_summary. They return the same JSON as the CLI, as structuredContent and as text.
    • A scan is cached for five minutes per path and scan options, and dropped when it expires even if the server sits idle. refresh: true forces a new walk.
    • Progress notifications go out when the client sends a progress token.
    • notifications/cancelled stops the walk.
    • Bad arguments come back as isError tool results.

The JSON is documented field by field in docs/agent-json.md and versioned by schema_version. The README has a new "For scripts and AI agents" section with Claude Desktop, Cursor and Claude Code configs.

Read-only, and how that is enforced

  • Everything lives in a new crate, crates/disktree-agent, which depends on disktree-core for scanning only. It never names removal or export.
  • tests/read_only.rs reads the crate's own source and fails on removal, export::, trash::, remove_file, remove_dir, rename(, fs::write, File::create, OpenOptions, Command::new and similar.
  • Every MCP tool is annotated readOnlyHint: true, destructiveHint: false, and a test checks that no tool name suggests otherwise.
  • The subcommands are handed over at the top of main, before Power Efficiency settings are loaded or saved, and before the cmux re-spawn, which would otherwise point stdout at /dev/null.

Changes outside the new crate

  • disktree-core: ScanOptions::exclude, a compiled pattern set (exclude.rs). It is checked in the walk's classify, so excluded trees are never entered, and costs nothing when empty.
    • Patterns without / match names; with / they match paths. * stays inside one path component, ** crosses components, and ? matches one character.
    • The matcher is a bit-set automaton, so a pattern full of stars can't blow up.
    • The MFT reader falls back to the walk when patterns are set, as it already does for follow_links.
  • disktree-app/src/main.rs: the dispatch above, plus three usage lines. Nothing else in the window changed. A directory literally named scan still opens as disktree ./scan.
  • Dependencies: serde_json, dunce and chrono, all already in the lockfile. Nothing new is downloaded.

Testing

  • cargo xtask lint (fmt check, then clippy with -D warnings) and cargo xtask test pass on macOS (Apple silicon). That includes 25 new agent tests, 5 new core tests and the existing 71 window-harness tests.
  • cargo clippy -p disktree-core -p disktree-agent --all-targets -- -D warnings is clean for x86_64-pc-windows-msvc and x86_64-unknown-linux-gnu. disktree-app can't be cross-checked for Windows from a Mac (GPUI's build script needs the Windows SDK), so CI is the first Windows build of main.rs.
  • disktree scan ~ --json on a real home directory: 6.3M files and 718k directories (416 GiB) in 48 s, headless, exit 0.
  • Live MCP session over stdio against the built binary:
    • initialize, tools/list and scan_path on a 3.8M-file tree, with progress notifications;
    • a cached list_files call answered in 0.2 s;
    • largest_dirs with category=cache;
    • disk_summary.
  • The window still opens normally with a path.

🤖 Generated with Claude Code

AI agents and scripts can now ask disktree where the space went without
opening a window:

- `disktree scan PATH [--json]` prints a stable, documented report: the
  total, the largest directories and files, each with path, bytes, human
  size, type, modified time, depth, category and reclaim reason. Options
  for top-N, listing depth, apparent vs on-disk size, exclude patterns and
  staying on one filesystem; filters for minimum size, extension,
  older-than, name pattern, category and reclaim reason.
- `disktree summary PATH [--json]`: the volume's capacity and free space,
  without scanning.
- `disktree mcp`: a stdio MCP server with scan_path, largest_files,
  largest_dirs, list_files and disk_summary, returning the same JSON. Scans
  are cached for five minutes, report progress, and stop when cancelled.

Everything lives in a new `disktree-agent` crate that reuses
`disktree_core::scan` and never reaches `removal` or `export`; a test fails
the build if its source ever names them or any file-writing call, and every
tool is annotated read-only. The core gains `ScanOptions::exclude`, applied
as entries are listed so excluded trees are never walked (the Windows MFT
reader falls back to the walk when patterns are set). The window is
unchanged: the subcommands are handed over in `main` before any settings
or GPUI code runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant