Revision history for MCP-Run
0.106 2026-08-13 18:19:29Z
- Fix mcp-run-bash dying on every tools/call in its default
configuration. MCP::Run::format_result calls
MCP::Run::Compress->new, but nothing under lib/ ever loaded that
class, and bin/mcp-run-bash enables compression unconditionally.
Every tools/call therefore hit "Can't locate object method new via
package MCP::Run::Compress": under MCP 0.13 the server process
died outright without answering, under 0.15 the client got
-32603 Internal error. MCP::Run now loads MCP::Run::Compress at
compile time, so a broken or missing compressor fails at server
startup instead of mid-session. MCP_RUN_COMPRESS=0 was the only
working configuration and is why the bug survived this long.
- Add t/50-bash-bin.t, driving bin/mcp-run-bash as a subprocess over
real stdio. None of the seven existing test files could have
caught the above: the compress attribute defaults to off in the
module, and every test instantiated the classes in-process, which
loads MCP::Run::Compress and papers over the missing load. Only a
fresh interpreter running the shipped script sees it. The test
also asserts that the ls filter visibly rewrites the listing, so
the crash cannot be "fixed" by quietly disabling compression.
- Report mcp-run-bash and the distribution version in serverInfo
instead of MCP::Server's PerlServer / 1.0.0 defaults. The defaults
live on MCP::Run::Bash rather than in bin/mcp-run-bash, so library
users are identified correctly too; passing name/version to the
constructor still overrides them.
- Require MCP 0.15 and rework t/20-integration.t for its protocol
handling, fixing the smoker failures reported in GH #2. MCP 0.15
made the protocol revision part of every request: params._meta
must carry io.modelcontextprotocol/protocolVersion (checked
against MCP::Constants::SUPPORTED_VERSIONS) and
io.modelcontextprotocol/clientCapabilities, or MCP::Server
rejects the request with "Missing protocol version" before
dispatch. The test built a bare MCP::Server::Context and sent no
_meta, so all four subtests failed. Only the test was wrong —
MCP::Run does no protocol handling, and real clients are
unaffected because the stdio transport serves the classic
initialize handshake through MCP::Server::Legacy, which skips
_check_meta entirely.
- Follow the two 0.15 renames in the same test: the 'initialize'
handshake became 'server/discover', and serverInfo moved out of
the result body into result._meta, where it now accompanies every
response. Assertions on server name, version, tool surface and
execution are unchanged.
- Add a 'protocol contract' subtest pinning both rejection paths
(missing _meta, unsupported protocol revision) so a future
protocol change fails with a legible message instead of
scattering failures across every other subtest. The protocol
revision is taken from MCP::Constants rather than hardcoded.
- Pin the MCP prereq in cpanfile (was unversioned, which is why the
smokers kept pulling the latest release). The suite targets the
0.15 API only; there is no compatibility path back to 0.13.
- Enforce that pin at runtime with a plain `use MCP 0.15` in
t/20-integration.t. A cpanfile prereq is install-time metadata:
prove never reads it and Makefile.PL only warns, so a stale MCP
in @INC reached the suite untouched and aborted with three "not
exported by the MCP::Constants module" import errors that named
neither MCP nor a version. It now dies with "MCP version 0.15
required--this is only version 0.13". No other test file needs
the guard — they either avoid the protocol layer or, in the case
of t/50-bash-bin.t, deliberately straddle both revisions.
- GH #1 ("Can't call method has_scope on unblessed reference") was
already fixed in 0.104 by passing MCP::Server::Context instances
instead of raw hashrefs, and is not reproducible since. The
smoker report predates that release.
- Fix mcp-run-compress injecting the Co-Authored-By trailer into the
wrong quote. The git-commit transform targeted the last double quote
of the whole command line, so a compound command like
`git commit -m "x" && git config user.name "y"` had the trailer
written into the git config value (corrupting it) instead of the
commit message. The injection now lands after the closing quote of
the commit's own -m/--message argument; commands without one (e.g.
`git commit --amend` opening the editor) are left alone. The flag
match also covers bundled short options ending in the message flag
(`git commit -am "..."`, -sm, -anm), which contain no literal `-m`
and had lost the trailer entirely; the flag must now start an
argument, so a `-m` inside a value like /tmp/msg-m is no longer
mistaken for it.
- Fix the same transform's replacement path swallowing the closing
quote: s/Co-Authored-By: [^\n]+/.../g matched the quote too when
the trailer ended a -m "..." message, leaving the shell with an
unterminated string. The character class now stops at the quote
([^\n"]+), so replacing an existing Co-Authored-By line keeps the
message intact.
0.105 2026-08-10 16:59:50Z
- Migrate the release process fully into the [@Author::GETTY] bundle
and drop the hand-rolled maint/release-after.pl. Its
run_after_release hook created the GitHub release first, so the
bundle's GitHub::CreateRelease plugin always hit 422
already_exists and died with a generic "Unable to create GitHub
release". GitHub::CreateRelease now owns the GitHub release (with
ChangeLog notes), and a new [@Author::GETTY::Docker / compress]
dist.ini section builds and pushes the raudssus/mcp-run-compress
image (:latest, :0, :<VERSION>) as part of `dzil release`.
- Dockerfile: rename the runtime stage to `compress`, matching the
bundle's docker target and what the image actually ships (the
mcp-run-compress hook only, not the mcp-run-bash runtime).
- Make the ls-filter subtest in t/40-compress-bin.t deterministic:
list a process-private tempdir instead of the shared /tmp, where a
readdir race could make ls transiently exit 1 and fail the test
intermittently.
0.104 2026-08-09 18:57:54Z
- Fix "Use of uninitialized value in join or string" warning from
MCP::Run::Compress::compress/process when a transform coderef
returns undef (e.g. git diff --stat's summary-line drop). undef
values are now filtered out before join, so dropping a line via
`return undef` no longer warns.
- Normalize undef $stdout/$stderr inputs to empty strings at the
top of compress()/process() so a caller passing undef for an
empty stream no longer trips warnings through the filter
pipeline.
- Fix filter resolution: when both a parsed_command filter and a
legacy regex filter match the same command, the parsed_command
filter now wins (it is strictly more specific). Previously hash
iteration order decided, which meant `git diff --stat` could
land on the bare `^git\s+diff\b` filter and skip the
"N+M- filename" transform rewrite entirely.
- Add t/30-no-warnings.t and t/40-compress-bin.t covering the
regression and the previously untested bin/mcp-run-compress
modes (--hook, --b64, --filter-files, --install-claude).
- Fix t/20-integration.t: pass MCP::Server::Context instances to
$server->handle() instead of raw hashes. MCP::Server's
_handle_tools_list calls $context->has_scope, which crashes
on an unblessed reference. The test was the bug, not the
server.
0.103 2026-05-07 13:28:39Z
- Fix MCP::Run::Compress->transform_command injecting Co-Authored-By
*outside* the closing quote when the original git commit had no
signature, which made bash run the model line as a separate command
and choke on `<model@host>` as a redirection. The signature now
lands inside the quoted commit message where it belongs.
0.102 2026-05-02 03:19:04Z
- Pass the original command through to the compressor in
MCP::Run::format_result so command-specific filters (ls, git,
make, …) match when mcp-run-bash runs with compression enabled.
Subclasses overriding format_result should accept the new
fourth argument: format_result($tool, $result, $compress, $command).
- Add CLAUDE.md and reframe README around mcp-run-bash as the
primary product (stdio MCP server) and mcp-run-compress as the
bonus PreToolUse hook for Claude Code, standalone-installable
via the raudssus/mcp-run-compress Docker image.
0.101 2026-05-02 02:08:56Z
- Fix "Wide character in subroutine entry" in bin/mcp-run-compress
--hook when the Bash command contains non-ASCII characters: encode
the command as UTF-8 octets before base64-encoding it.
0.100 2026-04-27 02:36:52Z
- Add command_transform filter and process() method for git commit hooks
- Register command_transform filter for git commit to auto-add/replace
Co-Authored-By line using CO_AUTHORED_BY or ANTHROPIC_MODEL env vars
- Add process() method that takes command, stdout, stderr and returns
a hash with processed output
- Add MCP_RUN_COMPRESS_NO_CO_AUTHORED to disable Co-Authored-By replacement
- Update README with new environment variables and feature documentation
0.004 2026-04-25 04:02:17Z
- Add C<validator> attribute to MCP::Run and MCP::Run::Bash. A coderef
can be provided to perform custom command validation before execution.
Return C<"1"> to allow, any other defined value to deny with a reason
string. Undefined return is treated as denied.
- Add parsed_command filter matching in MCP::Run::Compress using
Getopt::Long for order-independent flag parsing. New filters can use
C<< parsed_command->{program => 'git', subcommand => 'diff', flags => { stat => 1 } } >>
instead of regex C<< command => '^git\s+diff\b' >>.
- Add C<git diff --stat> filter with compact "N+M- filename" format.
- Add filters: C<netstat>, C<ip addr/route/link>, C<mount>, C<lsblk>, C<blkid>.
- Convert all existing filters (git, docker, kubectl, cargo, terraform,
helm, npm, pip, pytest, curl, wget, ansible-playbook, rsync, iptables,
ping, etc.) to use parsed_command approach.
0.003 2026-04-24 19:22:16Z
- Ship a Docker image (raudssus/mcp-run-compress) so the Claude Code
hook can be installed without a Perl toolchain on the host. The
image is a multi-stage Dockerfile (perl:5.40-slim builder +
slim runtime) with ENTRYPOINT mcp-run-compress
- New env var MCP_RUN_COMPRESS_INSTALL_MODE selects `native`
(default, in-process wrapper) or `docker` (pipe-through-docker).
The shipped image bakes the var to `docker`, native Perl installs
leave it unset, so the same `--install-claude` call produces the
right hook in either context
- New env var MCP_RUN_COMPRESS_IMAGE overrides the image ref the
docker-mode hook uses. The shipped image pins itself to the exact
version that built it, so `--install-claude` from inside the
container writes a versioned hook into ~/.claude/settings.json
- In docker mode, --hook rewrites Bash commands to a small host-side
snippet that runs the original command on the host (host cwd,
host env, host binaries) and streams stdout/stderr through a
docker container for compression — the host only needs bash,
coreutils (mktemp, base64) and docker
- New --filter-files mode reads captured stdout/stderr from two
files and emits compressed streams; used by the docker rewrite
- Dist::Zilla release hook (maint/release-after.pl) builds and
pushes both :VERSION and :latest tags to Docker Hub as part of
`dzil release`, and uploads the CPAN tarball to the matching
GitHub release
- Declare JSON::MaybeXS and Path::Tiny as explicit runtime prereqs
(previously pulled in transitively)
- Add POD to bin/mcp-run-compress, rewrite README.md around the
Docker-first install flow
0.002 2026-04-24 17:57:40Z
- Add bin/mcp-run-bash — stdio entry point for MCP::Run::Bash,
configurable via MCP_RUN_ALLOWED_COMMANDS, MCP_RUN_WORKING_DIRECTORY,
MCP_RUN_TIMEOUT, MCP_RUN_COMPRESS and MCP_RUN_TOOL_NAME env vars.
Compression is enabled by default; disable with MCP_RUN_COMPRESS=0
- Add MCP::Run::run_stdio convenience constructor
- Add bin/mcp-run-compress — wrap a shell command through
MCP::Run::Compress filters. Ships a Claude Code PreToolUse hook
(--hook) plus an --install-claude flag that registers the hook
in ~/.claude/settings.json so every Bash tool call gets its
output compressed automatically
- Prefix a Bash command with `no-compress ` to bypass compression
for that single invocation — the hook strips the prefix and
passes the raw command through unwrapped
- --install-claude also drops a minimal skill at
~/.claude/skills/bash-output-is-compressed-prefix-no-compress-to-bypass/
that teaches Claude about the bypass
- Add MCP::Run::Compress for automatic output compression
- 8-stage filter pipeline (strip_ansi, match_output, transform,
strip_lines, keep_lines, truncate, head/tail, max_lines, on_empty)
- 30+ command-specific filters (ls, git, make, docker, kubectl, etc.)
- Output detection to differentiate command output formats
- Per-server and per-tool-call compression toggle
- Add MCP::Run::Compress::Filters.pod documentation
- Add Text::Trim dependency
0.001 2026-02-25 01:32:04Z
- Initial release
- MCP::Run base class with configurable run tool
- MCP::Run::Bash implementation using IPC::Open3
- Command whitelist via allowed_commands
- Per-command working_directory and timeout
- Timeout enforcement with alarm/SIGTERM (exit code 124)