NAME
Net::Async::MCP::Transport::InProcess - In-process MCP transport via direct MCP::Server calls
VERSION
version 0.004
SYNOPSIS
# Usually created automatically by Net::Async::MCP
use Net::Async::MCP;
my $mcp = Net::Async::MCP->new(server => $my_mcp_server);
$loop->add($mcp);
# Or construct directly for testing:
use Net::Async::MCP::Transport::InProcess;
my $transport = Net::Async::MCP::Transport::InProcess->new(
server => $my_mcp_server,
);
DESCRIPTION
Net::Async::MCP::Transport::InProcess provides direct in-process communication with an MCP::Server instance. It calls handle() directly on the server object, passing a fresh MCP::Server::Context with each request, making it the most efficient transport for Perl-based MCP servers running in the same process. The context carries no scopes, so MCP::Server's OAuth scope checks impose no restriction for this transport.
If a tool returns a Mojo::Promise (from an async MCP server implementation), the promise is resolved synchronously via wait(). For fully non-blocking async tools, use Net::Async::MCP::Transport::Stdio with a separate subprocess instead.
Communication is strictly request/response: there is no stream the server could push notifications back over, so subscriptions/listen is not usable with this transport. See "send_request".
This transport is selected automatically by Net::Async::MCP when constructed with a server argument.
new
my $transport = Net::Async::MCP::Transport::InProcess->new(
server => $mcp_server,
);
Constructs a new in-process transport. Requires a server argument which must be an MCP::Server instance (or any object with a handle method that accepts a JSON-RPC request hashref and an MCP::Server::Context instance).
send_request
my $future = $transport->send_request($method, \%params);
Sends a JSON-RPC request to the MCP server by calling handle() directly. Returns a Future that resolves to the result value from the response, or fails with an error message if the server returns a JSON-RPC error.
A JSON-RPC error fails the Future with more than its message. Future's failure convention is ( $message, $category, @details ), so the failure reads ( "MCP error $code: $message", 'mcp', $error ): in scalar context ->failure is the message and nothing has changed, and in list context the raw JSON-RPC error object comes with it.
my ( $message, $category, $error ) = $future->failure;
if (($category // '') eq 'mcp') {
my $code = $error->{code}; # -32601, -32602, ...
my $supported = $error->{data}{supported};
}
The mcp category marks a genuine JSON-RPC error from the server and nothing else. The failures this transport raises on its own - a missing or unusable response, an async tool that rejected, and the subscriptions/listen refusal below - carry their message alone, so a caller that finds no category knows there is no server error object behind it.
Accepts the same optional trailing name/value options as the other transports, header_params among them, and ignores all of them: they describe how a request is mirrored into HTTP headers, and this transport hands the request to the server as it stands. See "send_request" in Net::Async::MCP::Transport::HTTP.
subscriptions/listen is the one method that cannot be served here. If the server object has a notification capable transport of its own, MCP::Server answers that request with an MCP::Server::Subscription object rather than a JSON-RPC response, leaving it to the transport to turn it into a notification stream; this transport has none, so the returned Future fails saying that it cannot carry server-initiated notifications. A server without such a transport never gets that far and answers with JSON-RPC error -32601 (METHOD_NOT_FOUND), which is reported like any other server error.
send_notification
my $future = $transport->send_notification($method, \%params);
Sends a JSON-RPC notification (a request with no id, expecting no response) directly to the server via handle(). Returns an immediately resolved Future.
close
my $future = $transport->close;
No-op for the in-process transport since there is no external process or connection to close. Returns an immediately resolved Future.
is_alive
my $alive = $transport->is_alive;
Always true: the server object lives in the same process, so there is no connection state that could go away. Used by "ping" in Net::Async::MCP for its transport-level liveness check.
mirrors_header_params
my $mirrors = $transport->mirrors_header_params;
Always false: there are no HTTP headers here to mirror tool arguments annotated with x-mcp-header into, so "call_tool" in Net::Async::MCP resolves none and never fetches a tool list to do it.
SEE ALSO
Net::Async::MCP - Main client module that uses this transport
Net::Async::MCP::Transport::Stdio - Alternative transport for external subprocesses
MCP::Server - The MCP server this transport communicates with
MCP::Server::Context - Per-request context passed to
handle()
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-net-async-mcp/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.