NAME
mcp-run-compress - Claude Code hooks that run Bash tool output through MCP::Run::Compress
VERSION
version 0.107
SYNOPSIS
# One-shot install (Docker, no Perl needed)
docker run --rm -v "$HOME:$HOME" -e HOME="$HOME" \
raudssus/mcp-run-compress --install-claude
# One-shot install (native Perl)
cpanm MCP::Run
mcp-run-compress --install-claude
# Bypass compression for a single Bash call
no-compress ls -l
DESCRIPTION
Registers two hooks on Claude Code's Bash tool so its output is passed through MCP::Run::Compress. 30+ command-specific filters strip noise (make "Entering directory"), reduce columns (ls -l to type+name), drop progress lines (cpanm, cargo, npm), and cap long output with head+tail.
PostToolUse-
Does the compression, by replacing the recorded tool output through
hookSpecificOutput.updatedToolOutput. The command itself is never touched. PreToolUse-
Only what must happen before the command runs: the Co-Authored-By rewrite of
git commit, and turning ano-compressprefix into a marker (see "THE no-compress MARKER").Those two are independent.
no-compressand the background check govern compression; they never touch the trailer. The Co-Authored-By rewrite is a commit policy that applies to every command and has exactly one off switch,MCP_RUN_COMPRESS_NO_CO_AUTHORED. In particular a command that names this tool is treated like any other — up to 0.106 a self-bypass on the name stopped the hook from wrapping its own wrapper, but nothing is wrapped any more, so all that match still did was drop the trailer from messages likegit commit -m "Fix mcp-run-compress hook".
Both are the same command, mcp-run-compress --hook; the script dispatches on the payload's hook_event_name.
NAME
mcp-run-compress - Claude Code hooks that compress Bash tool output
LIMITS
Commands that fail are not compressed
Claude Code does not fire PostToolUse for a command that exits non-zero. It fires PostToolUseFailure, whose payload carries no tool_response at all and which ignores updatedToolOutput. A failing command's output therefore arrives uncompressed, in full.
This is the one real regression against the pre-0.107 rewrite, which compressed failures too because it worked from inside the execution. Failing builds are the loudest output there is, so the limit is worth knowing rather than discovering. The only thing that event accepts is additionalContext, which adds text instead of replacing it and so cannot save anything.
Background commands are not compressed
The hook fires when a background command starts: tool_response carries backgroundTaskId and noOutputExpected, and both output streams are empty. The real output arrives later over the task file and is not a Bash tool response any more. The hook passes those through untouched rather than gluing a header onto the "Command running in background" notice.
PostToolUse hooks do not chain
Every registered PostToolUse hook is shown the original tool response, not the previous hook's replacement. If more than one returns an updatedToolOutput, the last registered one wins and the others are discarded silently. An unrelated hook on Bash can therefore switch this compression off without any error. --install-claude appends its entry, so whatever is installed after it takes precedence.
Replaced output has a byte ceiling
Claude Code persists a tool output above roughly 30000 bytes to a file and shows the model a 2 KB preview — and it applies that to the replacement as well. A compressor returning 40 KB would therefore deliver 2 KB, worse than no compression at all. Output above the ceiling (MCP_RUN_COMPRESS_MAX_BYTES, default 29000 bytes) is cut head-and-tail with a [mcp-run-compress: … omitted …] line that names the file holding the complete text. The cut is never silent.
When the original was persisted, the hook reads the full file, compresses that, and deletes persistedOutputPath/persistedOutputSize from the replacement — which is what removes the preview framing. Since deleting the path also removes the only pointer to the untouched original, the path is written into the compressed text.
Files above 4 MB are left alone entirely: the harness's own preview plus path is a better answer than pulling that through a regex pipeline inside a hook.
Docker mode cannot read the persisted file
persistedOutputPath is a path on the host. In docker mode the hook compresses the 30000 bytes it was handed, still drops the preview framing, and names the file it could not read. Mounting the file is not offered: it lives in ~/.claude/projects/<project>/<session>/tool-results/, so the only directory known at install time is ~/.claude/projects, which holds every session transcript of every project.
THE no-compress MARKER
PostToolUse is shown the command as PreToolUse rewrote it, never the user's original. A bypass prefix that PreToolUse strips is therefore invisible by the time compression happens, so the wish has to survive inside the command.
no-compress ls -l becomes:
ls -l
# mcp-run-compress: no-compress
A trailing comment line is invisible to bash and leaves the command's first word intact, which keeps Claude Code's permission prefix matching working — a leading comment would not. Measured against 24 command shapes (multiline commands, heredocs including <<- and quoted delimiters, two heredocs on one line, function definitions, if/case/while blocks, subshells, line continuations, background jobs): output and exit code stay byte-identical except in two cases.
- a command ending in a dangling backslash
-
The line continuation would splice the comment onto the last command line. Guarded: the marker is not appended, which costs the bypass for that one call, never the command.
- an unterminated heredoc
-
cat <<EOFwith no closingEOFswallows the marker into the heredoc body, so it appears in the output. bash already warns about such a command ("here-document delimited by end-of-file"); not guarded.
OPTIONS
--hook-
Hook entry point for both events. Reads the hook invocation JSON on stdin, dispatches on
hook_event_name, writes the response JSON on stdout. A payload for any other event is answered with an empty response carrying that event's name.Fail-open: if the script cannot load its own dependencies it still emits a valid pass-through response and exits 0. A
PreToolUsehook that exits non-zero blocks the tool call, so a broken install must never be able to take the user's Bash tool down with it. The same response is emitted when the hook entry carries an option this version no longer knows. --install-claude-
Patch ~/.claude/settings.json to register the hook for
PreToolUseandPostToolUse(idempotent), and drop a skill under ~/.claude/skills/bash-output-is-compressed-prefix-no-compress-to-bypass/ that tells Claude about theno-compressbypass prefix. The hook command matches the current install mode. Hooks and skill are installed independently and each is idempotent, so a deleted skill is restored even if the hooks are already registered. The file is only rewritten when something actually changed.It also migrates an installation from before the PostToolUse move: adding the missing
PostToolUsehook is the migration itself, and any entry still naming a removed flag (--b64,--filter-files) or a stale Docker image ref is rewritten. A stale entry naming a removed flag is not cosmetic — the script would exit non-zero on it, and forPreToolUsethat blocks every Bash call. --help-
Print a more detailed help message.
ENVIRONMENT
MCP_RUN_COMPRESS_INSTALL_MODE-
native(default) ordocker. Controls the hook command written by--install-claude. MCP_RUN_COMPRESS_IMAGE-
Image reference used by the docker-mode hook. Defaults to
raudssus/mcp-run-compress:latest. The shipped Docker image pins this to the exact version that built it, so--install-claudefrom inside the container writes a versioned hook. MCP_RUN_COMPRESS_MAX_BYTES-
Byte ceiling for the replaced output, default 29000. Exists because the ~30000 byte limit it stays below belongs to Claude Code, not to this distribution, and a changed harness would otherwise leave no lever.
MCP_RUN_COMPRESS_NO_CO_AUTHORED-
Disables the Co-Authored-By rewrite in the
PreToolUsehook, and is the only thing that does. Not to be confused withCO_AUTHORED_BY, which replaces the value rather than switching the trailer off. CO_AUTHORED_BY-
Replacement value for the Co-Authored-By trailer.
SEE ALSO
MCP::Run::Compress, MCP::Run::Bash, mcp-run-bash
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-mcp-run/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.