furl-ctx
This server provides reversible context compression and retrieval tools for saving AI agent context window space.
furl_compress: Compress inline text or a local file to shrink context usage; supports normal/lossless-only/aggressive modes, include/exclude patterns, and optional persistence of no-op compressions.
furl_retrieve: Fetch original content by hash, search across stored entries, regex-filter lines, select JSON rows by field/equality/numeric range, project specific fields, request line ranges, and get byte-exact raw rows for hashing/diffing.
furl_stats: View compression counters, token savings, cost estimates for this process, and live store-wide statistics across all processes.
furl_search: Find stored originals by case-insensitive literal substring when the hash marker is lost.
furl_list: List stored CCR entries with hashes, age, TTL, size, content kind, and previews, with paging.
furl_purge: Erase stored originals by hash or wipe all entries as a data-deletion escape hatch.
furl_read (mentioned in schema as a possible tool): Not present in the exposed schema here; retrieval is handled via the listed tools.
furl-ctx
Reversible context compression for AI agents. furl-ctx shrinks large tool outputs, logs, web fetches, and RAG chunks before they fill your agent's context window, and keeps every original byte retrievable on demand. Think prompt compression and context pruning for token optimization, without losing data. CCR, short for Compress-Cache-Retrieve, is the core: compression where every dropped byte stays retrievable.
What works today: Claude Code gets the MCP tools, skill, and automatic compression hooks. Codex gets the same MCP tools and retrieval-aware skill; use
furl_compresson demand because Codex plugins do not yet expose Furl's output-replacement hook. Automatic hands-off compression works on Claude Code 2.1.163 and newer: the PostToolUse hook mirrors each replacement to the tool's output shape, so the harness honors it, verified live by both external audits on 2.1.212. This shape-mirroring was built in response to upstream issue #68951, where an unmirrored replacement was dropped. The manual MCP tools furl_compress, furl_retrieve, and furl_search work in both hosts. furl-ctx never touches your Read, Grep, or Glob file reads by design. See LIBRARY.md for the canonical harness status.
Keep finding yourself waiting on the next usage limit reset?
Answer: Stop making your AI agent read everything.
Quick install
Prerequisite: uv on your PATH (same as the official serena plugin).
Claude Code — run inside Claude Code:
/plugin marketplace add omar-y-abdi/furl-ctx
/plugin install furl@furlThat's it — this installs the compression hook, the MCP tools, and the skill. No pip install, no setup: Furl fetches itself on first use.
Codex — run in your terminal:
codex plugin marketplace add omar-y-abdi/furl-ctx
codex plugin add furl@furlStart a new Codex thread after installation. This installs the MCP tools and skill from a Codex-only package root; Claude Code's incompatible automatic hooks are not bundled.
Furl also works as a Python library
The PyPI package is furl-ctx. Do not run pip install furl, which installs an unrelated URL-manipulation library.
The same engine drops into any Python app or MCP host:
from furl_ctx import compress
messages = [{"role": "tool", "content": "..."}]
result = compress(messages, model="claude-sonnet-4")
# result.messages → compressed when content is large enough; CCR keeps originals retrievableInstall, usage, pipeline internals, prompt-caching contract, and the full FURL_* config reference live in LIBRARY.md.
How it works
furl-ctx filters out unwanted noise while the agent searches for the sections it needs, so input token usage drops while the answer stays the same.
Instead of pushing thousands of irrelevant lines into the model, Furl gives the agent a compressed view of the data. If it later needs something that was omitted, it explicitly retrieves just that portion—by pattern, field, or line range—without materializing the entire payload again. The MCP tools your agent calls directly are listed under What you get.
Unlike token compressors or summarizers, Furl never throws data away. Compression is reversible: every original text payload remains byte-exact and retrievable.
Where furl-ctx saves little or nothing. Repetitive text with no newlines compresses at roughly 0 percent, because the engine is line and structure oriented. Single-line high-entropy content is near 0 percent. Code and file reads are 0 percent by design, because Read, Grep, and Glob are never touched. So a coding session's expected savings come only from large structured tool outputs, for example JSON, logs, and search results from Bash, WebFetch, and sub-agent tasks.
Retrieval model: Furl is pull-based, not push-based.
Dropped content does not automatically reappear. The compressed representation intentionally removes those sections from the model-visible context. If the agent needs a specific omitted item by pattern, field, or line range, it retrieves it explicitly. The data is never lost, every retrieval is byte-exact and done by the agent.
Tradeoff is visibility:
A unique anomaly hidden inside repetitive data will not appear in the compressed summary unless the agent already knows to search for it. Furl preserves data availability, not automatic anomaly discovery.
Furl compresses what is already in context, not files on disk. It shrinks a payload your agent has already read into its context window. It cannot reach into a large file on disk to pull out the part that matters, and it cannot take a file path and return compressed output. For a genuinely large file, the first and biggest reduction comes from pre-filtering with tools like grep, awk, sed, or jq to extract the relevant slice; Furl then compresses that slice further and keeps every dropped byte retrievable. Treat the two as layers: pre-filter megabytes down to a focused excerpt, then let Furl compress the excerpt. Furl is a strong second layer on top of pre-filtering, not a replacement for it.
Why "Furl"?
To furl a sail is to roll it up and keep it out of the way until needed. Furl does the same for context: it rolls large amounts of information out of the active window while keeping it ready to unfurl when retrieval is required.
Furl is a hard fork of Headroom's compression engine, stripped and rebuilt around the reversible-compression core. About a third of the engine still has traces of Headroom (see NOTICE).
Related MCP server: headroom
What you get
Auto-compression hook (Claude Code) — shrinks large
Bash/WebFetch/WebSearch/Task(sub-agent) outputs before they enter context. Fail-open: never breaks a tool call. It does not touch yourRead/Grep/Globfile reads — by design, so a laterEditstill sees exact file bytes; those reads (often a coding agent's largest context cost) pass through uncompressed (why). One honest limit: when an output is so large that Claude Code itself persists it to a file and hands the model only a file reference, there is no inline output for the hook to compress.PreToolUse Bash pipe (Claude Code) — on by default, but it rewrites a
Bashcommand only when you have no Bash permission rules configured; with any Bash allow, deny, or ask rule it stays out of the way so your rules apply exactly as native. Disable it withFURL_PRETOOL_PIPE=0.Signal-aware offload + sliceable retrieval — a payload too big to compress inline (e.g. a 33 MB trace) comes back as a structured summary (schema, per-field value histograms, example rows) instead of a truncated head/tail, and the agent pulls a narrow slice on demand —
retrieve(hash, select_field="name", select_equals="DroppedFrame")or a numeric range — without materializing the whole thing.MCP tools —
furl_compress,furl_retrieve,furl_stats,furl_purge(erase stored originals),furl_search(find by content substring),furl_list(list stored entries).furl_compressaccepts inlinecontent, a local jailedfile_path, or an OpenAI/ChatGPT host-providedfileattachment declared throughopenai/fileParams, so large uploads can be transferred out-of-band instead of crossing model context first. A seventh tool,furl_read, exists but is off by default — enable withFURL_MCP_READ=1(see LIBRARY.md).Skill — explains the
<<ccr:HASH>>retrieval flow and how to tune or disable it.
Tuning, disabling with FURL_HOOK_ENABLED=0, and the full reference live in plugins/furl/README.md. Retrieval TTL differs by surface:
Surface | Retrieval TTL |
Library | 30 minutes |
| 24 hours |
Claude Code / Codex plugin | 24 hours |
Bare MCP server | 1 hour session, plus 30 minutes for dropped-row originals |
The plugin sets FURL_CCR_TTL_SECONDS=86400, which governs both the hook's offloads and the MCP tools' stores; the full 24 hour window needs that env set, as the plugin ships it.
A note on version numbers: the Claude Code and Codex plugin manifests version independently from the furl-ctx engine they pin — a plugin release doesn't always mean an engine release, and vice versa. /plugin or codex plugin list shows the plugin version; GitHub Releases and CHANGELOG.md track the engine version; Claude Code's SessionStart banner shows both together (furl <plugin> · engine furl-ctx <engine>).
Proof
Token reduction on real captured data — a dated snapshot (inputs committed under benchmarks/data/ for auditability; a re-run measures the current engine, so absolute counts can drift from this table — the honest-read band below is the authoritative check).
Every number uses the engine's own tokenizer and measures compress() directly — independent of the PostToolUse hook-delivery issue noted above; needle recall is 100% (a known unique row is always recoverable, in the output or via CCR).
This table is measured with the gpt-4o model string (real tiktoken BPE, see BENCHMARKS.md). compress()'s own default model is claude-sonnet-4-5-20250929 — the shape Claude Code and the plugin actually call with — and claude-* routes through the exact same o200k_base encoding as gpt-4o, since Anthropic's own tokenizer is not publicly available. That makes this table's shape representative of what a real Claude Code run sees internally, but the counts themselves are a documented PROXY for Anthropic's tokenizer, not real Anthropic billing tokens: per Anthropic's own developer guidance, tiktoken undercounts Claude tokens by roughly 15-20% on typical text and by more on code or non-English text. Read every "token savings" percentage you see from a claude-* call, here or in your own agent, as an approximation on that basis — not an exact Anthropic token count.
Read every figure below as a best-case ceiling, not a typical — the honest read follows.
Best-case ceilings — low-entropy dev fixtures (the compressor's happy path):
Dataset | Items | Before | After | Reduction | Info retention |
code | 7 | 41,025 | 1,678 | 95.9% | 100% |
multiturn | 135 | 14,686 | 2,283 | 84.5% | 100% |
logs | 90 | 8,556 | 632 | 92.6% | 100% |
search | 90 | 4,102 | 365 | 91.1% | 100% |
repeated logs | 90 | 3,621 | 171 | 95.3% | 100% |
disk | 9 | 694 | 347 | 50.0% | 100% |
Every cell is the committed capture in benchmarks/baseline_results.json, rendered in full at benchmarks/BASELINE.md.
Across these six datasets: 92% fewer tokens (72,684 → 5,476) at 100% information retention. Full methodology and the 6-seed adversarial sweep: BENCHMARKS.md.
Information retention here means every byte is recoverable byte-exact through furl_retrieve. It does not mean the compressed view shows every row. Retrieval is pull-based, so an agent has to query for a specific dropped item to see it, and a lone anomaly will not surface in the compressed summary on its own.
Honest read: the numbers above are best-case, low-entropy ceilings measured on the dev fixtures — two independent, out-of-sample audits show they degrade by 6–43pp on fresh high-entropy / near-unique / realistic data (exactly where real logs and listings live). On genuinely high-entropy content, honest lossless savings sit in the 0–54% band, not the 50–96% above (code 0%, search 40%, repeated_logs 54%); read every figure here as a ceiling, not a typical, and see the tier-aware breakdown in BENCHMARKS.md.
The code row is not reversible structural compression like the logs, search, and disk rows. It is an opaque whole-blob offload: the router cannot shrink source code structurally, so it moves the whole blob to the CCR store behind one marker and leaves a small summary. The headline percent is a marker reduction, not a token saving, and the offload has no granular row index, so an agent that needs the code must retrieve the entire blob back. That round trip is net-negative: measured fresh this fixture is raw 95.9% but effective -4.1% after one retrieval. See the code row in the effective-savings section of BENCHMARKS.md; compress() also reports each opaque offload per call as result.opaque_offloads so a caller can see the round trip is net-negative before paying for it. An agent's own Read, Grep, or Glob file access bypasses the compression hook by design and passes through unchanged, at 0%.
Stability: The public API is what furl_ctx exports at the top level, including compress(), retrieve(), purge(), and resolve_markers(). Those signatures are the surface to build against. Submodule internals under furl_ctx.* may change between releases, so import from the top-level package rather than reaching into submodules. Releases have been frequent during early development, so pin a minor version if you need a fixed surface to depend on.
Community
Questions or bug reports → open a GitHub issue (the surest way to reach the maintainer).
Maintainer note: Furl is solo-maintained today — one person handles issues, PRs, and security reports, so response times vary with availability. CONTRIBUTING.md covers how PRs get reviewed and SECURITY.md covers the vulnerability-disclosure process; both hold regardless of team size.
License
Apache 2.0 — see LICENSE.
Available Tools
6 toolsfurl_compressA
Compress content to save context window space. Use this on large tool outputs, file contents, search results, or any content you want to shrink before reasoning over it. Pass 'content' with inline text, OR 'file_path' to have the server read and compress a file from disk (ideal for a large trace/log that would overflow context if pasted inline) — exactly one of the two. When it compresses, the original is stored and can be retrieved later via mcp__furl__furl_retrieve: returns compressed text + a hash for retrieval. When Furl decides NOT to compress (a no-op: the content is too small or would not shrink) it returns the original unchanged with hash null and stores NOTHING, so a no-op does not consume a retrieval slot — pass persist=true to store it anyway and get a hash back. Optional 'mode' controls aggressiveness; optional include/exclude patterns limit which lines are compressed.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Compression aggressiveness (default 'normal' = current behavior). 'lossless_only': only proven-lossless transforms run — nothing is dropped or substituted, so the output carries no retrieval markers (larger, fully reversible). 'aggressive': keep fewer items per crush and accept marginal compressions the default would reject (smaller output; all drops stay CCR-recoverable). | |
| content | No | The inline content to compress. Any text: tool output, JSON, search results, logs, code, etc. Provide EITHER content OR file_path, not both. | |
| persist | No | Store the original even when compression is a no-op (default false). When Furl decides not to compress because the content is too small or would not shrink, it returns the original unchanged and, by default, does NOT store it, so a no-op no longer consumes a retrieval slot. Set true to store it anyway and get a retrieval hash back. | |
| file_path | No | Absolute path to a file the server reads from disk and compresses, so a large artifact (e.g. a multi-MB trace or log) never has to be pasted inline and pay the full context cost first. Confined to the workspace. Provide EITHER file_path OR content, not both. Larger byte ceiling than inline content (override with FURL_MCP_MAX_FILE_BYTES). | |
| exclude_patterns | No | Glob-or-regex patterns. Any content line matching one is PROTECTED — passed through verbatim, never compressed. Applied on top of include_patterns. | |
| include_patterns | No | Glob-or-regex patterns (regex tried first, glob fallback). When set, ONLY content lines matching at least one pattern are eligible for compression; all other lines pass through verbatim. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It transparently discloses key behavioral traits: the original is stored and retrievable via furl_retrieve, a no-op returns original unchanged with hash null and stores nothing, persist=true overrides this, mode controls aggressiveness, and include/exclude patterns gate which lines compress. This is far beyond minimal disclosure.
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 information-dense without fluff. It front-loads the core purpose, then methodically covers usage modes, side effects, and parameters. Every sentence contributes value, and the use of dashes to separate clauses improves readability without losing precision.
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 no output schema, the description adequately explains return behavior: compressed text plus a hash for retrieval, and original unchanged with hash null for no-op. It also covers disk file reading, pattern restrictions, and persistence. The tool's complexity is high, but the description leaves no major functional gap for an agent to select and invoke it correctly.
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?
Even though schema coverage is 100%, the description adds significant meaning beyond the schema. It explains the 'exactly one of the two' constraint for content/file_path, clarifies the consequence of persist=true in no-op scenarios, and contextualizes mode and pattern parameters. This transforms raw parameter listings into functional understanding.
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 opens with a specific verb and resource: 'Compress content to save context window space.' It clearly distinguishes from sibling tools (furl_retrieve, furl_search, etc.) by focusing on the compression operation. The scope of when to use it (large tool outputs, file contents, search results) further clarifies its purpose.
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 the tool: 'Use this on large tool outputs, file contents, search results, or any content you want to shrink before reasoning over it.' It also explains the alternative path via mcp__furl__furl_retrieve for retrieving compressed content. The mutual exclusivity of 'content' and 'file_path' is clearly spelled out, along with the persists=true option for no-op cases, providing thorough usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
furl_listA
List stored CCR entries, newest first — a directory of what furl_compress / furl_read have stashed this session. Returns per entry: hash, created-at, age (humanized time since storage), ttl (the entry's retention window), expires_in (humanized time left before the TTL evicts it, e.g. "23h"), size (characters), content-kind (the originating tool, when known), and a short preview. Page with limit/offset. Use furl_retrieve with a hash for the full original, or furl_search to find by content.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max entries to return (default 20, capped at 100). | |
| offset | No | Number of newest entries to skip, for paging (default 0). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description details the behavior: listing entries, ordering, pagination, and return fields. It does not mention any side effects, but the tool appears read-only. Given no annotations, it is sufficiently transparent.
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 concise with clear structure: purpose, return fields, pagination, alternatives. Every sentence adds value 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?
It explains return fields and pagination comprehensively. No output schema exists, so the description compensates well. Minor gap: assumes knowledge of 'CCR entries' and 'session'.
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 coverage is 100% with descriptions for limit and offset. The description only mentions pagination generally, adding no new semantics beyond the 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 tool lists stored CCR entries, newest first, and distinguishes it from siblings by mentioning alternatives: furl_retrieve for full original and furl_search for content search.
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?
It provides explicit guidance on when to use this tool (listing entries) and when to use alternatives (furl_retrieve, furl_search), covering typical usage scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
furl_purgeA
Permanently erase stored originals from the CCR store — the data-erase escape hatch (offloaded content otherwise persists for the session TTL). Pass EXACTLY ONE of: 'hash' (delete one entry by its CCR hash) or 'all'=true (wipe every entry). Returns how many entries were deleted. A hash that is already absent deletes nothing and is not an error. There is no undo.
| Name | Required | Description | Default |
|---|---|---|---|
| all | No | When true, erase EVERY stored entry. Mutually exclusive with 'hash'. | |
| hash | No | CCR hash of the single entry to erase (12 or 24 lowercase-hex chars). Mutually exclusive with 'all'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses permanence ('no undo'), the escape hatch context, offloaded content TTL behavior, and return value. No annotations provided, so description carries full burden and delivers well.
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?
Three sentences with no wasted words. First sentence states purpose and context, second explains parameters, third covers return and edge behavior. Front-loaded and efficient.
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 tool with 2 parameters and no output schema, the description covers all essential aspects: action, parameters, return count, and edge cases. Minor omission: behavior if both parameters provided, but mutual exclusivity is implied.
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?
Adds value beyond schema: specifies hash format ('12 or 24 lowercase-hex chars'), mutual exclusivity, and non-error on missing hash. Schema already covers descriptions, so credit for extra detail.
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 action ('Permanently erase stored originals from the CCR store'), uses specific verbs ('erase', 'purge'), and distinguishes it from sibling tools (e.g., furl_list, furl_retrieve). The 'escape hatch' metaphor further clarifies its role.
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?
Explicitly states 'Pass EXACTLY ONE of: hash or all=true' and notes that a missing hash is not an error. Does not explicitly contrast with alternatives like furl_compress, but the parameter constraints are clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
furl_retrieveA
Retrieve original uncompressed content by hash. Use this when you need full details from previously compressed content. The hash comes from furl_compress results or from compression markers like [N items compressed... hash=abc123]. Two extra modes: (1) OMIT hash and pass query to search across ALL stored entries (returns ranked hash/score/preview matches to retrieve individually); (2) pass hash with pattern/fields/line_range, or a select_field row-filter (keep the rows of a JSON array by exact value or numeric range), to project just part of the original. Filters cannot be combined with query. Examples: furl_retrieve(hash) -> the whole original; furl_retrieve(hash, pattern="ERROR") -> only the matching lines; furl_retrieve(hash, select_field="id", select_equals=42) -> the rows where id==42.
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | Return each matched row byte-identical to its source bytes instead of a re-serialized, pretty-printed copy — for hashing, diffing, or signature checks where the exact formatting must survive. Requires select_field (only a row-select yields whole source rows) and cannot be combined with fields (a projection has no source span). Every returned row is byte-exact; the rows are rejoined with fresh JSON array punctuation, so the blob as a whole is not a contiguous slice. If more rows match than 'limit', a single trailing {"__ccr_truncated__": ...} object is appended as a synthetic marker (NOT a source row); strip that one element by its key before hashing, or raise 'limit' to avoid truncation. Default false keeps the re-serialized output. | |
| hash | No | Hash key from compression (e.g., 'abc123' from hash=abc123). Omit to search across all entries via 'query'. | |
| limit | No | Max rows returned by a select_field row-select OR a fields projection; a positive integer. A select without an explicit limit defaults to 1000; a fields projection without a limit is unbounded. When more rows match than the limit, only the first 'limit' ship plus one explicit truncation-marker row. It does not bound a pattern or line_range window, which line_range bounds instead. | |
| query | No | Search query. WITH a hash: return only items in that entry matching the query. WITHOUT a hash: full-text search (BM25-ranked) across every stored entry, returning top matches as hash/score/preview. Mutually exclusive with pattern/fields/line_range. | |
| fields | No | For a JSON-array original: project only these keys out of each object element (requires a hash, no query). Errors if the original is not a JSON array. Cannot be combined with pattern/line_range; composes with select_field (projects the columns of the kept rows). | |
| pattern | No | Regex applied line-by-line to the full original (requires a hash, no query). Returns matching lines (prefixed with 1-based line numbers) plus 'context_lines' lines of surrounding context. Invalid regex returns an error. | |
| line_range | No | [start, end] 1-based inclusive line window over the full original (requires a hash, no query). Either bound may be null for an open end. Composes with 'pattern' (the range is applied first). | |
| select_max | No | Numeric-range mode: keep rows whose select_field is a number <= select_max (inclusive; open upper bound when omitted). Must be >= select_min. Mutually exclusive with select_equals. | |
| select_min | No | Numeric-range mode: keep rows whose select_field is a number >= select_min (inclusive; open lower bound when omitted). A row whose field is missing or non-numeric is skipped, never an error. Mutually exclusive with select_equals. | |
| select_field | No | Row-select over a JSON array of objects (requires a hash, no query): the field/column name to match on. It anchors the whole select family — select_equals / select_min / select_max / limit are honored ONLY alongside select_field (any of them without it is an error). Reads a top-level JSON array of objects OR a JSON object with exactly one dominant inner array (e.g. a '{metadata, traceEvents:[...]}' trace). Composes with 'fields'; cannot be combined with pattern/line_range or query. | |
| context_lines | No | Lines of context to include on each side of a 'pattern' match (default 0, max 50). | |
| select_equals | No | Equality mode: keep rows whose select_field equals this JSON scalar (string/number/boolean/null; a list or object is rejected). Bool-safe — true never matches the number 1. Mutually exclusive with select_min/select_max. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. It details three operating modes, states that filters cannot combine with query, and gives illustrative outputs ('returns ranked hash/score/preview matches', 'only the matching lines'). It does not explicitly assert read-only/non-destructive status, but the word 'retrieve' and the context of accessing previously compressed content make that clear.
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 dense but well-structured: core purpose in the first sentence, then two numbered extra modes, then examples. Every sentence adds functional value or constraints; there is no filler. The length is appropriate for a tool with 12 parameters and three usage modes.
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?
Despite complexity and no output schema, the description is complete: it covers full retrieval, search mode, and projection modes, shows expected outputs for each, and mentions key constraints (filter/query exclusivity). It also references the source of the hash ('from furl_compress results or compression markers'), giving enough context to use the tool effectively.
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 high-level parameter usage beyond the schema by explaining combinations (e.g., omitting hash enables search; select_field anchors the row-filter family) and gives concrete examples like 'furl_retrieve(hash, select_field="id", select_equals=42) -> the rows where id==42,' which clarifies parameter interactions.
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 opens with a specific verb+resource: 'Retrieve original uncompressed content by hash.' It immediately states the core purpose and distinguishes the tool from siblings like furl_compress, furl_stats, furl_purge, and furl_list by focusing on retrieval. Concrete examples (e.g., 'furl_retrieve(hash) -> the whole original') reinforce the exact behavior.
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 says 'Use this when you need full details from previously compressed content,' giving a clear context for use. It also explains two alternative modes (query search, projection) and warns that filters cannot be combined with query, but it does not explicitly name sibling tools like furl_search as alternatives for when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
furl_searchA
Find stored originals by a case-insensitive SUBSTRING of their content — for when you lost a <ccr:HASH> marker but remember some of the text. Returns per hit: hash, a short preview around the match, created-at, and size (characters), newest first. Substring only (NOT a regex). Then call furl_retrieve with a returned hash for the full original.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max hits to return (default 20, capped at 100). | |
| query | Yes | Non-empty substring to look for (case-insensitive). Matched literally — not a regex. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavioral traits: case-insensitive search, literal substring matching, return fields (hash, preview, created-at, size), ordering by newest first, and workflow to retrieve full original. It does not mention rate limits or auth needs, but for a search tool this is acceptable.
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 two sentences plus a parenthetical note, all front-loaded with the core purpose. Every sentence adds value: use case, return details, constraints, and follow-up action. No wasted 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 no output schema, the description adequately explains return values and ordering. It also provides a complete workflow (search then retrieve). Missing: error handling, empty result behavior, or pagination info, but these are minor for a search tool.
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 description adds no new information beyond the schema. It repeats the query parameter's behavior (case-insensitive, literal) but omits mention of the limit parameter. Baseline 3 is appropriate as the schema already does the heavy lifting.
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 finds stored originals by a case-insensitive substring of their content. It specifies the use case (when lost a hash marker) and distinguishes it from sibling tools like furl_retrieve or furl_list, making the purpose unambiguous.
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 (when you lost a hash marker but remember some text) and advises subsequent use of furl_retrieve. It also notes that it's substring only, not regex, which gives clear usage constraints. It does not explicitly mention when not to use or compare to other search methods, but the context is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
furl_statsA
Show compression statistics. Two clearly-labeled scopes: (1) this-server-process counters — compressions, tokens saved, estimated cost savings, and recent events done by THIS process; and (2) a live 'store' section derived from the shared CCR store for this namespace — live_entries, original vs compressed bytes and tokens, estimated tokens saved, and oldest/newest entry age. The store section reflects entries written by ALL processes (including the PostToolUse hook and sub-agents), so it stays truthful even when this server process compressed nothing.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Given no annotations, the description fully discloses that the tool is read-only and describes the two sections, including that the store section reflects all processes.
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?
Front-loaded with purpose, each sentence adds value, no wasted 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?
Fairly complete given no parameters or output schema; could optionally mention return format but not necessary.
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?
No parameters, schema coverage is 100%, so description does not need to add parameter info. Baseline 4 for 0 params.
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?
Clearly states 'Show compression statistics' and details two scopes, distinguishing from sibling tools like furl_compress or furl_list.
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?
Explains when to use (to view compression stats) and describes what each scope covers, though does not explicitly state when not to use.
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.
2 tool updates
v1.2.2- Changed
furl_compress4 fields changed- changed
Input schema / properties / content / descriptionPrevious value: -"The content to compress. Can be any text: file contents, JSON, search results, logs, code, etc."New value: +"The inline content to compress. Any text: tool output, JSON, search results, logs, code, etc. Provide EITHER content OR file_path, not both." - added
Input schema / properties / file_pathAdded value: +{ + "description": "Absolute path to a file the server reads from disk and compresses, so a large artifact (e.g. a multi-MB trace or log) never has to be pasted inline and pay the full context cost first. Confined to the workspace. Provide EITHER file_path OR content, not both. Larger byte ceiling than inline content (override with FURL_MCP_MAX_FILE_BYTES).", + "type": "string" +} - added
Input schema / properties / persistAdded value: +{ + "description": "Store the original even when compression is a no-op (default false). When Furl decides not to compress because the content is too small or would not shrink, it returns the original unchanged and, by default, does NOT store it, so a no-op no longer consumes a retrieval slot. Set true to store it anyway and get a retrieval hash back.", + "type": "boolean" +} - changed
Input schema / requiredPrevious value: -[ - "content" -]New value: +[]
- Changed
furl_retrieve2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max rows a select_field row-select returns (positive integer; defaults to 1000 when a select is requested without it). When more rows match, only the first 'limit' ship plus one explicit truncation-marker row. Applies only to select_field row-selects."New value: +"Max rows returned by a select_field row-select OR a fields projection; a positive integer. A select without an explicit limit defaults to 1000; a fields projection without a limit is unbounded. When more rows match than the limit, only the first 'limit' ship plus one explicit truncation-marker row. It does not bound a pattern or line_range window, which line_range bounds instead." - added
Input schema / properties / rawAdded value: +{ + "description": "Return each matched row byte-identical to its source bytes instead of a re-serialized, pretty-printed copy — for hashing, diffing, or signature checks where the exact formatting must survive. Requires select_field (only a row-select yields whole source rows) and cannot be combined with fields (a projection has no source span). Every returned row is byte-exact; the rows are rejoined with fresh JSON array punctuation, so the blob as a whole is not a contiguous slice. If more rows match than 'limit', a single trailing {\"__ccr_truncated__\": ...} object is appended as a synthetic marker (NOT a source row); strip that one element by its key before hashing, or raise 'limit' to avoid truncation. Default false keeps the re-serialized output.", + "type": "boolean" +}
6 tool updates
v1.2.0- First observed
furl_compress - First observed
furl_list - First observed
furl_purge - First observed
furl_retrieve - First observed
furl_search - First observed
furl_stats
TDQS
Scored across 6 tools
Each tool has a distinct primary role—compress, retrieve by hash, stats, purge, search, and list—making the set largely easy to navigate. The main ambiguity is that furl_retrieve also supports a query-based search mode, which overlaps with furl_search, though the descriptions are detailed enough to mitigate confusion.
All tools follow the same furl_ prefix followed by a clear action verb (compress, retrieve, stats, purge, search, list). This creates a predictable and consistent naming pattern across the entire server.
Six tools is well-scoped for a context compression and retrieval server. Each tool covers a necessary function—compressing, retrieving, searching, listing, purging, and statistics—without unnecessary bloat or missing essentials.
The toolkit provides a full lifecycle for compressed content: create (compress), read (retrieve/list/search), delete (purge), and observability (stats). This is a complete and coherent surface for the stated purpose of managing offloaded context.
Maintenance
Related MCP Connectors
Deterministic AI agent microtools, no accounts/API keys. fetch_extract: 98% token cut. 38 tools.
Portable AI memory shared across models and harnesses - plain markdown you own.
Verified doc corpora for agents: grep-first retrieval, hashed pages, Merkle+RFC-3161 receipts
Persistent memory for Claude Code, Cursor and Codex. Facts retire when they change.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA terminal live-tail and a browser dashboard — one process, one event stream, served from localhost. Unified timeline across Claude Code, Codex, Gemini CLI, Cursor, Hermes, and OpenClaw. Token + cost accounting, compaction + anomaly detection, hybrid search, SVG call graphs, monaco-style diff attribution, agent-aware replay ("what would the agent say if I edited the prompt?"), policy editor, MCP s11 npm14MIT
- AlicenseNot gradedqualityAmaintenanceMCP server that provides real-time rate-limit and context budget awareness to Claude Code, enabling it to plan tasks that fit within its constraints and defer work when needed.5 npmApache 2.0
- AlicenseNot gradedqualityCmaintenanceGuck is a tiny, MCP-first telemetry store for agentic debugging. It provides token-efficient log analytics by capturing JSONL telemetry events and exposing a minimal MCP toolset for fast, filtered queries.2MIT
- AlicenseAqualityAmaintenanceCompacts noisy test, build, and cloud-log output before an AI agent reads it — dedupes repeats, collapses stack frames, folds Playwright retries, and renders CloudWatch/GCP JSON logs down to the signal. Typically 80–95% fewer tokens on failures. Tool: compact_output. Run: npx -y logslim logslim-mcp19 npm5MIT