sarup
Sarup is a Thai-first context compression MCP server that shrinks text (50–88% fewer tokens) while guaranteeing full recovery of the original content via a retrieval store.
Compress text (
sarup_compress): Reduce token size of Thai prose, English prose, JSON, and logs using one of five modes:extractive(default): Offline TF-IDF scoring, ~1ms, no external dependenciessemantic: Embedding-based centrality scoring (~64.6% savings), requires Ollamaabstractive: Local-LLM rewrite via Ollama (~51% savings, slower)pipeline: Cascaded semantic → abstractive for maximum savings (~88.1%), requires Ollamaauto: Uses semantic if Ollama is available, otherwise falls back to extractive
Control compression behavior with optional parameters:
target_ratio: Fraction of prose to retain (0.3–0.7)lossless: Apply only lossless transforms (whitespace normalization, JSON compaction)query: Prioritize sentences relevant to a given query
Retrieve originals (
sarup_retrieve): Recover the exact, byte-for-byte original text using the hash returned bysarup_compressTrack session savings (
sarup_stats): View cumulative token savings for the current session, measured via tiktokencl100k_baseAutomate compression: Use an optional
PostToolUsehook to automatically compress largeRead,Bash, orGrepoutputs; Ollama-dependent modes gracefully fall back toextractiveif unavailable
Provides optional compression modes (semantic, abstractive, pipeline) using local LLM models hosted by Ollama for enhanced context reduction.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sarupCompress this Thai paragraph to half its tokens."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
สรุป · Sarup
Thai-first context compression for Claude Code. An MCP server that actually shrinks Thai — 50–88% fewer tokens — and caches every original so nothing is ever lost.
สรุป means "to summarize." Headroom routes Thai through
noop(0% savings) because its whitespace tokenizer can't find Thai word boundaries. Sarup uses PyThaiNLP segmentation, so it compresses Thai as well as English — and caches every original so nothing is ever lost.
Contents
Related MCP server: cctx
Highlights
🇹🇭 Real Thai compression — PyThaiNLP
newmmword segmentation, not whitespace.♻️ Lossless by guarantee — every compress caches the original;
verified: trueproves a byte-for-byte round-trip.🎚️ Five modes — from offline 1 ms TF-IDF to an 88%-savings cascade.
🧠 Optional local LLM — embeddings + rewrite via Ollama, with automatic offline fallback.
📏 Honest metrics — token counts from a real tokenizer (tiktoken), not byte guesses.
🔌 Content-aware — JSON compaction, log dedup, and verbatim code-fence preservation built in.
🛟 Can't break Claude — it's an MCP tool, not an API proxy; if the server is down the tools just go away and Claude keeps working.
Why it's safe — the two-tier guarantee
Tier | What | Guarantee |
Compressed view | the shrunk text the model works on | lossy · small · cheap |
Retrieval store | the original, keyed by a stable hash | lossless · recoverable |
Aggressive lossy compression is safe because the original is always one sarup_retrieve(hash)
away. This is how "maximum savings" and "100% accuracy" coexist — they live in different tiers.
How it works
Two entry points feed one engine: a cheap compressed view the model reads, and a lossless retrieval store that can restore the original byte-for-byte.
flowchart TD
M["🧑 Manual<br/>sarup_compress()"]:::entry --> R
A["⚙️ Automatic<br/>PostToolUse hook<br/>(Read · Bash · Grep)"]:::entry --> R
R{"Sarup compress<br/>extractive · semantic · abstractive · pipeline"}:::engine
R -- "compressed view<br/>50–88% fewer tokens" --> V["📄 Model context"]:::lossy
R -. "cache original" .-> S[("🗄️ Retrieval store<br/>hash → original")]:::lossless
V -. "need full detail?" .-> RET["🔑 sarup_retrieve(hash)"]:::lossless
RET --> S
S == "byte-for-byte ✓" ==> V
classDef entry fill:#e0e7ff,stroke:#6366f1,color:#111
classDef engine fill:#fde68a,stroke:#d97706,color:#111
classDef lossy fill:#fef3c7,stroke:#f59e0b,color:#111
classDef lossless fill:#bbf7d0,stroke:#16a34a,color:#111Manual — the model calls
sarup_compress/sarup_retrieveitself.Automatic — the hook intercepts large tool outputs, caches the original to
SARUP_DB_PATH, and substitutes the compressed view + a retrieval hash. Source code is skipped; small outputs pass through untouched.
Tools
Tool | Purpose |
| Compress; returns compressed text, hash, token metrics, |
| Recover the original content byte-for-byte. |
| Cumulative session savings. |
sarup_compress arguments
Arg | Type | Default | Meaning |
| string | — | Text to compress (required). |
| number |
| Fraction of prose to keep (0.1–0.9). |
| boolean |
| Only apply lossless transforms (whitespace / JSON compact). |
| string |
| Relevance hint — sentences matching it are kept. |
| string |
| See modes below. |
Compression modes
Mode | How | Needs Ollama | Savings¹ | Speed¹ | Output |
| TF-IDF scoring + n-gram dedup | no | 50.8% | ~1 ms | verbatim subset |
| Embedding centrality + cosine dedup | yes | 64.6% | ~1–2 s | verbatim subset |
| Local-LLM rewrite | yes | ~51% | ~8–20 s | paraphrased |
| Cascade: semantic → abstractive | yes | 88.1% | ~2 s | paraphrased |
| semantic if Ollama is up, else extractive | optional | 64.6% | ~90 ms | subset |
¹ Measured on a 10-sentence Thai paragraph (522 tokens). Every mode stays 100% recoverable via the store; Ollama modes degrade gracefully to extractive when the backend is down.
Measured results
$ .\.venv\Scripts\python.exe bench\benchmark.py
sample before after savings verify
Thai prose 522 257 50.8% OK
Thai prose (aggressive) 522 217 58.4% OK
English prose 105 54 48.6% OK
JSON (lossless) 67 44 34.3% OK
Logs 563 300 46.7% OK
TOTAL 1779 872 51.0% ALL OK → 100% recoverable
Mode comparison (Thai prose, 522 tok):
extractive 50.8% (1ms) · auto 64.6% (~90ms) · semantic 64.6% (2.1s)
abstractive 51.1% (8s) · pipeline 88.1% (2.3s) ← all verified recoverableToken counts via tiktoken cl100k_base — a real tokenizer, not a byte heuristic.
Example
A real sarup_compress call on a Thai paragraph (mode="auto", Ollama up → semantic):
// → sarup_compress(content="…518-token Thai paragraph…", mode="auto")
{
"compressed": "จุดเด่นที่สำคัญที่สุดคือมันไม่มีทางทำให้ Claude พัง…",
"hash": "caa568140bec0ff734937cf5",
"original_tokens": 518,
"compressed_tokens": 154,
"tokens_saved": 364,
"savings_percent": 70.3,
"transforms": ["semantic_extractive", "embeddings", "thai"],
"lossy": true,
"verified": true, // round-trip proven byte-for-byte
"token_method": "tiktoken:cl100k_base"
}The model keeps working on the 154-token view; the full 518-token original is one call away:
// → sarup_retrieve(hash="caa568140bec0ff734937cf5")
{ "content": "…the exact original text, restored byte-for-byte…" }Install
One command (creates the venv, installs everything, registers the MCP server for all projects — idempotent):
.\scripts\setup.ps1 -All # Windows (-All also adds the hook, the /sarup-setup skill, pulls Ollama models)
./scripts/setup.sh --all # Linux / WSL / macOSTip:
-All/--allinstalls a global/sarup-setupskill, so on any other machine you can just type/sarup-setupin Claude Code and it walks through the install. (Or runscripts/install-skill.ps1/install-skill.shon its own.)
Uninstall just as cleanly (only removes what Sarup added; -Purge/--purge also
deletes the venv + cache):
.\scripts\uninstall.ps1 # Windows
./scripts/uninstall.sh # Linux / WSL / macOSpy -3.11 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"Optional local-LLM modes (semantic / abstractive / pipeline) need Ollama:
ollama pull nomic-embed-text # embeddings → semantic mode
ollama pull gemma3:12b # rewrite → abstractive / pipeline (Thai-validated)Register with Claude Code
One-command setup (recommended). Detects this machine's paths, probes Ollama
(picks the best mode + models), and merges into .mcp.json / .claude/settings.json
without clobbering anything already there (a .bak is written first):
.\.venv\Scripts\python.exe scripts\install.py --with-hook --pullNo Ollama? It configures offline
extractivemode — still fully works.Ollama up? It auto-selects
nomic-embed-text(semantic) +gemma3:12b(rewrite) and sets the hook toauto.--pullfetches any missing models.Idempotent — safe to re-run;
--globalwrites to~/.claudeinstead.
Manual — or add it yourself to your MCP config (e.g. .mcp.json or ~/.claude.json).
Replace <SARUP_DIR> with the absolute path where you cloned this repo (the installer
above fills these in for you):
{
"mcpServers": {
"sarup": {
"command": "<SARUP_DIR>/.venv/Scripts/python.exe",
"args": ["-m", "sarup.server"],
"env": { "SARUP_DB_PATH": "<SARUP_DIR>/.sarup-cache.db" }
}
}
}On Linux/macOS the interpreter is
<SARUP_DIR>/.venv/bin/python.
Or run it directly over stdio:
.\.venv\Scripts\python.exe -m sarup.serverAuto-compression hook
Skip manual tool calls entirely: install the PostToolUse hook and large Read/Bash/Grep
outputs are compressed before they enter context, with the original cached for retrieval.
Source-code reads are skipped for safety. Full setup in hooks/README.md.
Experimental — verify on your build. The hook fires and emits a valid
updatedToolOutput, but whether Claude Code applies it is surface-dependent: as of testing, the VS Code extension (2.1.193) does NOT apply it — the model still receives the full output, so the hook is a no-op there. Use the manualsarup_compresstool instead (it works everywhere); the hook may apply on other/CLI builds. Replace<SARUP_DIR>with your clone path, or runinstall.py --with-hook.
{
"hooks": {
"PostToolUse": [
{ "matcher": "Read|Bash|Grep",
"hooks": [{ "type": "command",
"command": "<SARUP_DIR>/.venv/Scripts/python.exe <SARUP_DIR>/hooks/sarup_hook.py" }] }
]
},
"env": { "SARUP_DB_PATH": "<SARUP_DIR>/.sarup-cache.db" }
}Privacy & data
To guarantee recovery, Sarup caches the original content in the store. Two things to know:
With
SARUP_DB_PATHset, originals are written to that SQLite file in plaintext (no encryption). Treat it like a cache of whatever you compressed.If you compress tool outputs that contain secrets (e.g. a
.envdump or credentials in a log), those land in the cache too. The auto-hook skips source-code/config file reads, butBashoutput is fair game — review what you point it at.
*.db is git-ignored, so the cache never gets committed. For zero on-disk
footprint, leave SARUP_DB_PATH unset (memory-only; the MCP server then loses
the cache on restart, and the hook will not substitute — see the hook docs).
Configuration
Var | Default | Meaning |
| (in-memory) | SQLite path for a persistent, cross-process store.Required for hook retrieval. |
|
| Ollama endpoint. |
|
| Model for abstractive / pipeline rewrite. |
|
| Model for semantic embeddings. |
|
| Hook compression mode. |
|
| Hook only compresses outputs with at least this many tokens (token-based, fair across languages). |
Project structure
sarup/
├── src/sarup/
│ ├── server.py # MCP stdio server — 3 tools
│ ├── compressor.py # router + modes (extractive/semantic/abstractive/pipeline/auto)
│ ├── thai.py # PyThaiNLP tokenization, sentence split, TF-IDF
│ ├── semantic.py # embedding centrality + cosine dedup
│ ├── llm.py # optional Ollama backend (generate + embed)
│ ├── tokens.py # real token counting (tiktoken)
│ └── store.py # CCR store: hash → original (memory + SQLite)
├── hooks/
│ ├── sarup_hook.py # PostToolUse auto-compression hook
│ └── README.md # hook install guide
├── bench/benchmark.py # before/after measurement
├── tests/ # test_thai, test_mcp, test_hook, ...
├── README.md
└── STACK.md # full stack + techniquesTech stack & techniques
Python 3.11 · MCP · PyThaiNLP newmm · tiktoken · Ollama (optional) · SQLite · hatchling · pytest.
The technique behind each mode — TF-IDF scoring, embedding centrality, cascade pipeline, content routing, and graceful degradation — is documented in STACK.md.
Testing
.\.venv\Scripts\python.exe -m pytest tests/ -qThe suite covers Thai NLP, the MCP tool contracts, every mode (including Ollama-fallback paths), the roundtrip-verify guarantee, and the auto-compression hook (incl. cross-process retrieval).
Roadmap
Make
autothe default mode forsarup_compress(currentlyextractive).Optional Typhoon 2.1 abstractive (blocked on an Ollama template fix).
Per-content adaptive
target_ratio.Published PyPI package.
License
MIT
Available Tools
3 toolssarup_compressA
Compress content for context efficiency. Supports Thai prose, English prose, mixed Thai+code, JSON, and logs. Returns compressed text, a retrieval hash, and token-saving metrics. Use sarup_retrieve(hash=...) to recover the original when needed.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Prose strategy. 'extractive' (default): offline TF-IDF, verbatim subset, ~1ms. 'semantic': embedding centrality, best ratio (needs Ollama). 'abstractive': local-LLM rewrite (needs Ollama, slow). 'pipeline': cascade semantic -> abstractive for maximum savings. 'auto': semantic if Ollama is up, else extractive. All modes stay 100% recoverable via sarup_retrieve. | extractive |
| query | No | Optional context query. Sentences relevant to this query are scored higher and more likely to be kept. | |
| content | Yes | Content to compress | |
| lossless | No | Only apply lossless transforms (whitespace/JSON compact). Default false. | |
| target_ratio | No | Fraction of prose to keep (0.3–0.7). Default 0.5. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses the return values and the recoverability of the original through sarup_retrieve. It does not mention side effects or performance, but the schema covers behavioral details for the modes. The description provides sufficient transparency for safe usage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is highly concise: two sentences that front-load the primary verb and resource, list supported types, mention return values, and reference the sibling tool. Every sentence serves a clear purpose without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of an output schema, the description correctly mentions the return values (compressed text, retrieval hash, metrics). The input schema is comprehensive, so the description does not need to repeat parameter details. However, it could briefly note that mode selection is covered by the schema. Overall, it is mostly complete for an agent to understand the tool's role and output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds general context (supported content types, recoverability) but does not elaborate on specific parameters like 'mode' or 'target_ratio'. Since the schema already thoroughly describes each parameter, the description adds minimal extra value beyond stating the overall purpose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Compress content for context efficiency.' It specifies supported content types (Thai prose, English prose, mixed Thai+code, JSON, logs) and mentions the return values (compressed text, retrieval hash, token-saving metrics). It also distinguishes itself from its sibling by referencing sarup_retrieve for recovery.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description indicates when to use the tool (compression for context efficiency) and how to recover the original using sarup_retrieve. However, it does not explicitly state when not to use it or provide alternative tools beyond recovery. Given the context of siblings (sarup_retrieve, sarup_stats), the usage context is clear but lacks exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sarup_retrieveA
Retrieve the original uncompressed content by hash. The hash is returned by sarup_compress.
| Name | Required | Description | Default |
|---|---|---|---|
| hash | Yes | 24-char hash returned by sarup_compress |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description indicates a read-only operation ('retrieve'), but lacks disclosure of error behavior, authentication needs, or what happens on invalid hash. Non-destructive nature is implied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with action and resource. No extraneous words. Every part earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple retrieval tool with one parameter and no output schema, the description adequately explains input source and action. Lacks details on response format but sufficient for basic usage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single 'hash' parameter is documented in the schema with type and format. The description confirms its origin ('returned by sarup_compress') but adds no new semantic details beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('retrieve') and the resource ('original uncompressed content'), and distinguishes it from sibling tools (sarup_compress and sarup_stats) by referencing the hash source.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool ('by hash returned by sarup_compress'), providing clear context. It does not explicitly exclude other use cases but the intent is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sarup_statsA
Return cumulative compression statistics for this session.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must fully disclose behavior. It states it returns 'cumulative compression statistics' but does not specify what statistics, the data format, or whether it has side effects. Given the lack of annotations, more details are needed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence that is front-loaded with the action and key information. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has no parameters and no output schema, the description provides the basic purpose but lacks details about what statistics are included or how they are presented. It is adequate but not comprehensive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has no parameters, and the schema coverage is 100% trivially. The description does not need to add parameter information. Baseline score of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns 'cumulative compression statistics for this session', which is a specific action with a clear resource. The sibling tools 'sarup_compress' and 'sarup_retrieve' indicate different operations, so this tool is well-differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is for retrieving statistics about a compression session, but it does not explicitly state when to use it versus alternatives or any prerequisites. No guidelines on exclusions or context are provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
sarup_compress - First observed
sarup_retrieve - First observed
sarup_stats
TDQS
Scored across 3 tools
Each tool has a distinct, well-defined purpose: compressing, retrieving via hash, and reporting statistics. There is no overlap or ambiguity.
All tool names follow a consistent pattern with the 'sarup_' prefix followed by a verb (compress, retrieve, stats), making them predictable and easy to distinguish.
Three tools is appropriate for the focused domain of context compression and retrieval. Each tool serves a clear, necessary function without bloat or shortage.
The tool set provides complete lifecycle coverage: compress, retrieve, and monitor statistics. No obvious gaps for its stated purpose.
Maintenance
Related MCP Connectors
A paid remote MCP for OpenAI Codex context compressor, built to return verdicts, receipts, usage log
Persistent context for Claude. Your AI always knows your projects and next actions across sessions.
No-data MCP handoff for local Claude Code to Codex harness moves. $49 lifetime.
Provide your AI coding tools with token-efficient access to up-to-date technical documentation for…
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables context compression for Claude Code by offloading large file queries and document processing to Ollama, saving context window tokens.MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude Code to reduce token usage by 70-90% using a local LLM for codebase indexing, tool output compression, and turn summarization.12 npm3MIT
- AlicenseNot gradedqualityDmaintenanceAutomatically reduces token usage in Claude Code sessions using algorithmic optimizations like code compression, smart file reading, output summarization, and prompt rewriting, with no extra API calls or cost.13 npm1MIT
- AlicenseNot gradedqualityCmaintenanceToken compression for AI contexts, reducing token consumption by compressing conversation exchanges before they enter the LLM context window.MIT