NAME
AnyEvent::MCP::Client::JSONL - MCP byte streams and stdio subprocesses
SYNOPSIS
use AnyEvent::MCP::Client::JSONL;
my $client = AnyEvent::MCP::Client::JSONL->new (
command => [$program, @args],
env => { PERL_ANYEVENT_MCP_MULTIBROKER_PROTOCOL => "stdio" },
);
DESCRIPTION
Use this transport when the MCP server is a subprocess or an already connected byte stream. It reads one JSON-RPC message per line, so stdout on a conventional stdio transport must contain protocol messages only. The command mode here connects the child's stdin and stdout to a socketpair; diagnostic output belongs on stderr. A stray banner or log message on the protocol stream can break the connection.
An argument array executes the program directly; a command string runs through a shell. Prefer the array form when no shell syntax is needed. The process starts on the first connection, not when the client object is constructed.
FIRST CALL
To invoke a bundled server as a subprocess:
use AnyEvent;
use AnyEvent::MCP::Client::JSONL;
my $client = AnyEvent::MCP::Client::JSONL->new (
command => ["mcp-fs", "--stdio", "/home/alice/src/project"],
);
my $cv = AE::cv;
$client->call (workdirs => {}, sub { $cv->send (@_) });
my ($success, $result) = $cv->recv;
$success or die "$result->{message}\n";
!$result->{isError} or die "$result->{content}[0]{text}\n";
This assumes the project exists and mcp-fs is installed on PATH. The subprocess is created on first use and reaped when it exits. The caller owns the client and its event loop; callbacks also distinguish protocol success from tool errors. See "TUTORIAL" in AnyEvent::MCP::Server::Simple for a complete echo server and a matching client example.
METHODS
- $client = AnyEvent::MCP::Client::JSONL->new (%config)
-
Accepts the common AnyEvent::MCP::Client options and these transport options:
- command => [$program, $arg...]
- command => $shell_command
-
Argument array for direct execution, or a string run through
/bin/sh -c. The child inherits stderr. No command runs until connecting. Omit this option when usingattachinstead. - env => { %environment }
-
Child environment overrides. Unspecified variables are inherited from the parent; the parent environment is unchanged.
- handle => { %handle }
-
AnyEvent::Handle options. The filehandle and read/error/EOF callbacks are managed by this transport.
- max_size => $bytes
-
Bounds a buffered incoming JSON line, defaulting to 16 MiB.
namedoes not load transport defaults. The library does not infer a subprocess command or environment. - $client->attach ($fh_in, $fh_out)
-
Uses existing handles instead of a command.
$fh_inmay also be an AnyEvent::Handle, whose buffered input is preserved.$fh_outdefaults to$fh_infor a bidirectional socket. The client takes ownership. Attach before sending requests. Closing an attached connection requires attaching new handles before reconnecting.Closing a subprocess connection sends SIGTERM and keeps its child watcher alive until the child has been reaped. It does not restart an outstanding call. An accepted multibroker idle shutdown is the exception: its child can exit and perform cleanup without receiving SIGTERM.