Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

isthmos

isthmos

Build Status Latest Release OpenSSF Scorecard License

isthmos (Greek: ισθμός), the narrow passage your tool outputs squeeze through.

A local context-compression layer for agent tool outputs. The core is agent-agnostic: JSON field pruning driven by per-tool rules, importable as a Go package. Adapters connect it to whatever runs your LLM: a native Claude Code PostToolUse hook that rewrites tool_response via updatedToolOutput, and a generic filter mode that works with any agent or CLI that can pipe through a command. Nothing leaves your machine and nothing sits in the credential path.

Status

Early but working: rule-based JSON field pruning, text compression for plain-text payloads, plus byte-level measurement.

Why

Verbose tool outputs (fat MCP JSON, log dumps, API payloads) can be a large share of an agent's context on some workflows and a rounding error on others. Which one your machine has is an empirical question, so isthmos ships measurement first: shadow mode and a per-call byte log show what pruning would save on your traffic before anything is rewritten. A per-tool saving is a local percentage, not a whole-task cost reduction; isthmos claims neither and reports both the local number and its share of everything it measured.

Install

Via Homebrew (macOS / Linux):

brew install --cask sphragis-oss/sphragis/isthmos

Or with Go:

go install github.com/sphragis-oss/isthmos/cmd/isthmos@latest

Or from source:

git clone https://github.com/sphragis-oss/isthmos.git
cd isthmos
make install PREFIX=$HOME/.local

Release artifacts ship with checksums, a cosign-signed bundle, SBOMs, and build provenance; SECURITY.md shows how to verify them. The hook example below points at $HOME/.local/bin/isthmos; adjust it to wherever your install landed (which isthmos).

Usage

Claude Code (native hook)

Start in shadow mode. isthmos measures what its rules would save on your own traffic and rewrites nothing, so the first week costs you no risk:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "mcp__.*|Read|Bash|WebFetch|Grep",
        "hooks": [
          {"type": "command", "command": "ISTHMOS_SHADOW=1 $HOME/.local/bin/isthmos hook", "timeout": 5}
        ]
      }
    ]
  }
}

Let it run, then read isthmos stats. Whether pruning is worth anything on your machine is an empirical question and the answer varies a lot by workload: MCP-heavy traffic is where field pruning pays, while a session dominated by short Bash outputs is close to the worst case.

Only once the numbers justify it, drop ISTHMOS_SHADOW=1 so isthmos rewrites live:

{"type": "command", "command": "$HOME/.local/bin/isthmos hook", "timeout": 5}

Narrow the matcher to the tools your stats show are actually worth pruning. The text limits under Configuration are truncation, not compression, so treat them as the opt-in step: they are the fastest way to save bytes and the only way to lose something you wanted.

Any other agent (generic filter)

some-tool --json | isthmos filter -tool mcp__github__search_repos

Stdin in, pruned stdout out. Wire it into any wrapper, shell function, or orchestrator that can interpose a pipe. Non-JSON payloads pass through untouched unless a matching rule sets the text limits below.

Checking your setup

$ isthmos doctor
version: 0.3.0
rules:   /Users/you/.config/isthmos/rules.json: ok, 4 rules
store:   ok, 12 entries
measure: /Users/you/.local/state/isthmos/measure.jsonl: 84.2KB, last write 2026-07-21T09:14:02Z
hook:    wired in ~/.claude/settings.json
shadow:  ON, measuring only, nothing is rewritten
next:    let it run, then read the numbers with: isthmos stats

Exits non-zero when something is actually broken (unreadable or invalid rules, unusable store, or a hook that fires but only ever receives empty payloads, which means the wiring or input field is wrong); a missing rules file is just reported, since no rules means isthmos is a deliberate no-op. Once shadow mode is off, doctor also warns when reveals are piling up, the signal that a rule is cutting more than the agent can do without.

As a Go library

import "github.com/sphragis-oss/isthmos"

rs := isthmos.LoadRules(path)
out, changed := isthmos.Apply(rs, toolName, rawOutput)

Configuration

Rules live in ~/.config/isthmos/rules.json (override with ISTHMOS_RULES). Tool names are glob-matched, listed keys are dropped recursively:

{
  "rules": [
    {"tool": "mcp__github__*", "drop_keys": ["node_id", "avatar_url"], "max_items": 30},
    {"tool": "mcp__*", "max_str": 8000}
  ]
}

Besides drop_keys, a rule can cap payload size generically: max_items truncates any array beyond N elements and max_str truncates any string beyond N bytes (at a rune boundary). Both replace the removed tail with an explicit [isthmos: ... truncated] marker so the model knows the payload is partial; truncation is never silent. When several rules match, the strictest positive limit wins.

Text payloads get their own limits: max_lines keeps head and tail lines with the same reversible marker, and dedup collapses runs of 3 or more identical lines into a labelled count. Error-looking lines (error, fatal, panic, traceback, ...) are never dropped by max_lines. Both apply to raw non-JSON payloads, to a JSON string carrying text, and to long strings embedded in JSON objects, which is where real hook payloads keep their text (stdout for Bash, file.content for Read).

Truncation is head-and-tail, not naive: keep_last reserves part of the max_items budget for the newest entries, and items that look like errors (a truthy error field, or status/level/conclusion values such as failed or fatal) are always kept regardless of position, because those are the items an agent is usually looking for. min_bytes gates a whole rule: payloads smaller than it pass through untouched, so tiny outputs are never rewritten.

See rules.example.json for a starter set covering Atlassian and GitHub MCP noise fields. No config means no rewriting: isthmos is fail-open and only ever emits a replacement when the result is strictly smaller.

Measurement

Every invocation appends one line to ~/.local/state/isthmos/measure.jsonl with before/after byte counts per tool, including calls the rules left untouched, so pruning rules are driven by real data, not guesses. The log is capped at 5MB; when it grows past that, the oldest half is trimmed. isthmos stats turns that log into a savings table (illustrative output):

$ isthmos stats -since 168h
TOOL                                      CALLS  IN     OUT    SAVED  SAVED%  %ALL   ~TOKENS  REVEALS
mcp__atlassian__searchJiraIssuesUsingJql  42     1.9MB  0.6MB  1.3MB  68.4%   68.0%  340787   3
mcp__github__get_me                       7      12.3KB 4.1KB  8.2KB  66.7%   0.4%   2099     0
TOTAL                                     49     1.9MB  0.6MB  1.3MB  68.4%   68.4%  342886   3
scope: only tool calls that reached isthmos; whole-session context is a larger denominator

SAVED% is local to that tool; %ALL is the same saving as a share of every byte isthmos measured in the window, so a flashy local percentage cannot pose as an overall one. Neither is a session-level or dollar figure: tools your hook matcher never routes to isthmos are not in the log, and published agent traces show repeated context (system prompt, history) is typically the far larger consumer. -file points at a different log, -since bounds the window. The ~TOKENS column is a rough 4-bytes-per-token estimate, not a tokenizer.

REVEALS counts isthmos reveal recoveries attributed to each tool. A reveal means a rule cut something the agent then had to fetch back, paying an extra tool call, so a tool with a rising reveal count is over-pruned: loosen its rule instead of celebrating its SAVED%.

Shadow mode

ISTHMOS_SHADOW=1 is the recommended way to start, as above. isthmos computes what the rules would save and logs it, but the hook emits nothing and filter passes stdin through untouched. Nothing is written to the reversibility store, and nothing can be lost. Unset it once isthmos stats shows the savings are worth it.

Sharing your numbers

isthmos stats -share replaces third-party tool names with stable placeholders (mcp__server1__*), so a report can be pasted into a public issue. The measurement log never contains file paths, arguments, or payload contents, only byte counts, so the redacted table is the whole of what leaves your machine.

$ isthmos stats -share
TOOL              CALLS  IN     OUT    SAVED  SAVED%  %ALL   ~TOKENS  REVEALS
mcp__server1__*   42     1.9MB  0.6MB  1.3MB  68.4%   68.0%  340787   3
Read              93     0.9MB  0.5MB  0.4MB  41.1%   27.3%  99264    0

Workload is the thing nobody can guess for you, so results from a workload unlike the maintainer's are the most useful thing you can contribute.

Reversibility

Truncation is reversible. When items or bytes are cut, the original payload is encrypted (AES-256-GCM) into ~/.local/state/isthmos/store/ and the marker carries the recovery command:

[isthmos: 17 of 20 items truncated, full: isthmos reveal de6ac0410501901b]

An agent that needs the full payload can simply run that command; a human can too. Entries expire after 7 days. If the store cannot be written, isthmos does not truncate at all: a marker must never point at a payload that was not stored. Field-pruned payloads (no truncation) are not stored.

Design constraints

  • No proxy in the credential path
  • One static binary, fast cold start (runs on every tool call)
  • Fail-open: any error means untouched passthrough
  • Lossy steps must be reversible or clearly labelled

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md for setup, style, and the DCO sign-off requirement, and GOVERNANCE.md for how decisions get made. Security reports go through SECURITY.md, never a public issue.

Pruning rules are the highest-value contribution: bring one backed by real before/after byte counts.

License

Apache-2.0

About

Local context-compression layer for agent tool outputs. Claude Code PostToolUse hook or generic filter, single Go binary.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages