MCP::Client::Test::ProgressKit
NAME
MCP::Client::Test::ProgressKit - a toolkit whose tools report progress
DESCRIPTION
An MCP::Server::Toolkit for testing the progress path end to end: plug it
into a real MCP::Server, put a client (or an agent) in front of that, and the
tools report progress the way a long-running tool really does ā through
$*MCP-REQUEST-CONTEXT.progress, which quotes back the progressToken the
client attached to the request and stays quiet when there was none.
It is deliberately separate from MCP::Client::Test::TestKit, which a great
many tests compare answers against: adding progress to echo would make every
one of those tests carry a notification they never asked about.
What is in it
marchā reports progressstepstimes (totaland amessageon each), then answers with how many of those reports were actually delivered.mssleeps that long between steps, for a caller that wants to observe progress before the call answers.silentā answers without reporting anything at all: the well-behaved server that was given a token and had nothing to say, which must leave a progress hook cold.strayā emits anotifications/progressunder a token nobody minted, which is what a late report from a finished call looks like from the client's side. A client must drop it rather than attribute it to whatever call happens to be in flight.
Every handler counts its invocations (count-of('march')), which is how a
test proves a call really ran rather than that its answer looked plausible.
EXAMPLES
use MCP::Server;
use MCP::Client;
use MCP::Client::Test::InProcessTransport;
use MCP::Client::Test::ProgressKit;
my $server = MCP::Server.new(:name<progress>, :version<1.0.0>);
$server.plug(MCP::Client::Test::ProgressKit.new);
my @seen;
my $client = MCP::Client.new(
transport => MCP::Client::Test::InProcessTransport.new(:$server),
on-progress => -> %p { @seen.push(%p) },
);
$client.execute-tool-calls([
%( id => 'call_1', function => %( name => 'march', arguments => %( steps => 3 ) ) ),
]);
say @seen.map({ $_<progress> }); # (1 2 3)
say @seen.map({ $_<tool-call-id> }).unique; # (call_1)