Lossless skim-then-expand reading of big files, repos, and command output with far fewer tokens
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Token-efficient skim-then-expand I/O for agentic models (Claude Code / desktop Claude).
Independent community project β not affiliated with, endorsed by, or sponsored by Anthropic. No telemetry; runs entirely on your machine. See Trademarks, privacy & license.
Instead of reading a whole large file into context, the model calls skim_open(path) and gets a
compact skeleton β structure, signatures, and (for logs/data) preserved critical values like ids,
numbers, dates, and error codes β plus anchor ids. It reads the skeleton cheaply, then calls
skim_expand(handle, anchors=[...]) to pull only the exact spans it needs, verbatim and lossless.
Compression is lazy, not lossy: nothing is paraphrased or destroyed, only deferred. Expansion is always one call away β which is what makes it safe for a closed model that can't ingest latent vectors.
The mainstream tools are lossy in context β repo-maps and --compress drop function bodies;
summarizers and compression models paraphrase. The closest neighbors either cover code only, or show
the model a transformed view and keep originals in a side cache. skim's line is stricter: whatever
lands in context is verbatim source, and everything not shown is one anchored expand away β an agent
can edit code from what it read without re-reading. That holds everywhere:
ast) + ~17 languages (tree-sitter); bodies folded, one expand away.skim_repo builds a ranked, token-budgeted map; expand exact code from any file.skim_run compresses verbose test / build / log output, fully recoverable.See DESIGN.md for the architecture and verified prior-art positioning.
A reduction percentage is marketing until you can check it. skim ships the checker:
Five invariants, verified on your files, locally: every non-blank line recoverable, expands
byte-exact, anchors in-bounds, reconstruction equal to the decoded file, deterministic output. The
test suite enforces the same contract with Hypothesis fuzzing on every path; a reproducible FAIL
on any readable file is a bug β please report it. Summarize-first tools cannot ship this command,
because for them the equivalent check fails by design.
The cost-vs-correctness question has its own open yardstick β eval/ACCURACY.md prices every eval question under a full read vs skim (including the rows where skim loses), and any other context tool can be scored under the same protocol.
mcp, plus
optional tiktoken/tree-sitter extras). An afternoon's security review covers all of it:
SECURITY.md is the threat model.SKIM_RUN_DISABLED=1 removes the
shell tool, SKIM_PATCH_DISABLED=1 removes the file editor; the readers keep working.taskkill /F /T), CRLF preserved by
skim_patch, and CI runs the full suite on windows-latest alongside Ubuntu.Every number below is generated live by uv run python benchmarks.py β full reproducible tables in
BENCHMARKS.md. Measured on the running interpreter's standard library (real code);
token counts via tiktoken cl100k_base (a proxy for Claude's tokenizer; ratios are tokenizer-robust).
Token savings on real code β 60 Python stdlib files: 387,911 β 122,660 tokens (68% fewer, 3.16Γ); per-file 1.5Γβ11Γ; 100% lossless, deterministic; ~linear runtime (~4 ms / 1k lines); pure CPU, no GPU, no network, no model calls.
Multi-language (tree-sitter, [lang] extra) β the same lossless engine on JS / TS / Go / Rust /
Java / C / C++ / Ruby / PHP / C# and more. Example: a 481-line JavaScript module β 6.6Γ / 85% saved, lossless.
1:1 before/after on a real task (distribution-level) β "read the largest function in this module"; the model opens the file, reads the skeleton, expands exactly the one function it needs. Same answer, full skim cost (skeleton + expand) counted, across 24 large modules:
| file | lines | function read | full read | skim (skeleton+expand) | saved |
|---|---|---|---|---|---|
tarfile.py | 3,032 | _proc_pax (119 lines) | 25,180 | 8,906 | 65% |
optparse.py | 1,682 | parse_args (37) | 12,830 | 5,610 | 56% |
pathlib.py | 1,436 | walk (43) | 11,242 | 5,421 | 52% |
bdb.py | 921 | effective (48) | 7,279 | 3,243 | 55% |
socketserver.py | 864 | serve_forever (27) | 5,824 | 3,615 | 38% |
| 24-file total | 176,897 | 71,877 | 59% |
Per-task savings: median 51%, mean 51%, range 30β91% across 24 tasks β the honest distribution, not a cherry-picked best case. The win shrinks when you need most of a file.
Head-to-head β same 30 files (compare.py + real Repomix via npx):
| approach | tokens | % of full | lossless? |
|---|---|---|---|
full read (Claude Read) | 184,277 | 100% | yes |
| skim | 64,043 | 34.8% | yes (lazy-expand) |
Repomix --compress | 105,963 | 57.5% | no (bodies dropped) |
| signatures-only (Aider/Basemind mechanism) | 20,276 | 11.0% | no |
skim is the only lossless option and uses 40% fewer tokens than Repomix --compress. The repo-map
approach is ~3Γ smaller but discards bodies/docstrings/comments irreversibly. (Basemind is pure Rust and
wasn't installed; its row reproduces the signatures-only mechanism β see COMPARISON.md.)
Correctness: 0 of 54,215 non-blank lines unrecoverable across 80 files; 202 tests / ~80% coverage with
Hypothesis property fuzzing of both paths; an ~18,000-case adversarial campaign (every bug found is fixed
and regression-locked). Reproduce: uv run python bench.py / pytest / benchmarks.py.
The one-liner (installs from PyPI on first run, tree-sitter languages included):
Or from source:
Register with Claude Code (use the repo's absolute path; forward slashes work on Windows):
Or Claude Desktop β add to claude_desktop_config.json (%APPDATA%\Claude\ on Windows,
~/Library/Application Support/Claude/ on macOS), then restart:
(If uv isn't found, use its full path from where uv / which uv.)
skim_open(path, query="") β compact skeleton + anchor ids (read the skeleton; ids are in its expand("aN")
markers). Pass query to also get matches (line + covering anchor) in the same call, no search round-trip.skim_expand(handle, anchors=[...]) β exact verbatim spans. Items are anchor ids ("a7") or literal
line ranges ("L120-180") for when a grep already gave you line numbers.skim_search(handle, query) β which anchors/lines contain a string, without reading them.skim_run(command) β run a shell command, get a compact expandable view of its output (tests / builds / logs).skim_repo(path, query) β a lossless, ranked, token-budgeted map of a whole repo; expand exact code from any
file. Ranked by query match when you pass one, else by import-graph centrality (PageRank over which files
import which), so the load-bearing modules surface first.skim_patch(handle, anchor, new_text) β replace exactly one expanded span on disk, drift-safe: refused
if the file changed since skim_open, LF/CRLF preserved, result re-verified from disk, fresh handle returned.
Because expands are verbatim, an edit built from one applies safely β read 8% of the file, edit it anyway.
(SKIM_PATCH_DISABLED=1 turns it off.)@skim:skim://doc/<handle>/span/<anchor> pulls a span by reference.No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/skim-mcp)<a href="https://allmcps.com/mcp/skim-mcp"><img src="https://allmcps.com/api/badge/skim-mcp?style=directory" alt="Skim MCP on AllMCPs" /></a>