MCP::Client::Test::FakeTransport

NAME

MCP::Client::Test::FakeTransport - a scripted MCP::Client::Transport for tests

DESCRIPTION

A transport that answers from a script instead of from a server, and records everything the client sent. It exists so the era machine, the multi round-trip loop and the typed API can be tested exhaustively — including the failure paths a real server will not produce on demand — with no subprocess, no socket and no timing.

Responses are queued per method, so a test scripts server/discover and tools/call independently and does not care what order the client asks in. A method with no script left is a test bug, not a server behaviour, and is reported as one.

Scripting

Each scripted answer is a hash with exactly one of:

  • result => %h — kept, after normalize-result, as a server result.

  • raw => $anything — kept verbatim, normalize-result included, so a test can hand the client a JSON null or a string where a result belongs.

  • error => { code, message?, data? } — broken with an X::MCP::Client::Protocol, exactly as a real transport does for a JSON-RPC error response.

  • timeout => $seconds — broken with an X::MCP::Client::Timeout.

  • throw => $exception — broken with that exception, for ServerGone and friends.

  • respond => -> %msg { ... } — called with the outbound message and returns any of the above, for answers that depend on what was asked.

Any of them may also carry notify => [%n1, %n2] : those notifications are delivered to the request's &on-notification before it is settled.

EXAMPLES

The era probe, twice over:

use MCP::Client;
use MCP::Client::Test::FakeTransport;

my $wire = MCP::Client::Test::FakeTransport.new;
$wire.script('server/discover', {
	result => {
		supportedVersions => ['2026-07-28'],
		capabilities      => { tools => {} },
		_meta => { 'io.modelcontextprotocol/serverInfo' => { name => 'fake' } },
	},
});

my $client = MCP::Client.new(transport => $wire);
is $client.era, 'modern';
is $wire.sent-methods, ['server/discover'];
my $wire = MCP::Client::Test::FakeTransport.new;
$wire.script('server/discover', { error => { code => -32601, message => 'nope' } });
$wire.script('initialize', {
	result => { protocolVersion => '2025-11-25', capabilities => {}, serverInfo => {} },
});

my $client = MCP::Client.new(transport => $wire);
is $client.era, 'legacy';
is $wire.notified-methods, ['notifications/initialized'];

Multi round-trips, one entry per round:

$wire.script('tools/call',
	{ result => {
		resultType   => 'input_required',
		inputRequests => { ask => { method => 'elicitation/create', params => {} } },
		requestState  => 'opaque-blob',
	} },
	# NB: the trailing comma -- a bare [%h] flattens the hash into Pairs.
	{ result => { content => [{ type => 'text', text => 'done' },] } },
);

my %answer = $client.call-tool('t', {});
is $wire.requests-for('tools/call')[1]<params><requestState>, 'opaque-blob';

MCP::Client v0.6.2

talk to an MCP server, in either protocol era

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

MCP::Server:ver<0.6.0+>:auth<zef:apogee>JSON::Fast:ver<0.21.0+>:auth<zef:timo>Cro::HTTP:ver<0.8.11+>:auth<zef:cro>:api<0>MIME::Base64:ver<1.2.5+>:auth<zef:raku-community-modules>

Test Dependencies

Provides

  • MCP::Client
  • MCP::Client::Cache
  • MCP::Client::Correlator
  • MCP::Client::Exceptions
  • MCP::Client::Leases
  • MCP::Client::Leases::Table
  • MCP::Client::Policy
  • MCP::Client::Policy::Commands
  • MCP::Client::Policy::Floor
  • MCP::Client::Policy::Grants
  • MCP::Client::Policy::Rules
  • MCP::Client::Protocol
  • MCP::Client::Reasons
  • MCP::Client::Registry
  • MCP::Client::SSE
  • MCP::Client::Test::FakeTransport
  • MCP::Client::Test::InProcessTransport
  • MCP::Client::Test::ProgressKit
  • MCP::Client::Test::TestKit
  • MCP::Client::Transport
  • MCP::Client::Transport::HTTP
  • MCP::Client::Transport::Stdio
  • MCP::Client::UnknownKeys

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.