Revision history for MCP::Server::Tool::Shell
0.2.0 2026-08-09T20:02:19+01:00
- Background jobs: new "start", "poll", "output" and "kill" tools run a
command that outlives the tool call which started it. Jobs are capped
(max-jobs, default 4), remembered after they finish (max-finished-jobs,
default 16) and eventually forgotten (job-ttl, default 600s); "output"
takes an opaque cursor for incremental reads, or none at all for a
capped snapshot of the whole run.
- Output is now bounded. Each stream of each run keeps a head and a
rolling tail (max-output-bytes, default 128 KiB; max-output-lines,
default 2000) with a "[... N bytes / M lines dropped ...]" marker where
the middle went and a "truncated" key reporting the same counts. A run
that stays inside its budget is byte-for-byte what it always was, and
its payload still has exactly the four keys it always had.
- Standard input: "run" and "start" take an optional "stdin" string, fed
to the command and then closed. Omitting it leaves standard input
unconnected, exactly as before; an empty string is an immediate EOF.
The write is detached, so a command that never reads cannot stall the
server.
- Live progress: a 2026-07-28 request that opted into logging (_meta
logLevel) gets the command's output as notifications/message while it
runs, batched at 32 KiB and flushed from the handler thread. Background
jobs push through MCP::Server.notify instead, which reaches a legacy
stdio session's transport-wide channel; on Streamable HTTP there is no
server-initiated stream between requests, so "poll" and "output" are the
reliable way to follow a job there.
- "run" takes an optional per-call "timeout" that overrides the server
default; "start" takes an optional one with no default.
- A cancelled request (a caller that hung up) now kills the command and
unwinds instead of running to the end of its budget: the run loop wakes
at least every 0.25s to flush, check the deadline and check for
cancellation. Jobs deliberately do not observe cancellation.
- New "allow-any" configuration key for deployments that gate consent
elsewhere (an MCP::Client::Policy in front of the tools, say). It is
mutually exclusive with "allow", and one of the two is required.
- New MCP::Server::Tool::Shell::Buffer (capped capture buffer with cursor
reads) and MCP::Server::Tool::Shell::Jobs (Job + Registry), both
documented and usable on their own.
- Requires MCP::Server 0.4.0 or newer, for MCP::Server.notify.
0.1.0 2026-07-29T00:00:00+01:00
- Initial release
- MCP::Server::Toolkit pack providing an allowlisted command runner:
"run" executes one of the exact command strings in the allow list and
returns JSON with exit, stdout, stderr and timed-out; "allowed" lists
the allow list.
- Commands are spawned via Proc::Async as an argv vector, never through a
shell, so no quoting, globbing, redirection or interpolation happens.
- Configurable allow list (required), default working directory and
per-run timeout, all validated at construction time.
- Runs are killed with SIGKILL once the timeout expires and reported with
timed-out: true; unspawnable commands report exit -1 with the reason on
stderr instead of failing the call.
- Default prefix "sh", so tools register as sh_run and sh_allowed.