NAME

AnyEvent::MCP::Client - asynchronous MCP client protocol

SYNOPSIS

use AnyEvent::MCP::Client::HTTP;

my $client = AnyEvent::MCP::Client::HTTP->new (
   url => $url, bearer => $token,
);

$client->call (echo => { text => "hello" }, sub {
   my ($success, $result) = @_;
   ...
});

DESCRIPTION

This is the common protocol implementation used by AnyEvent::MCP::Client::HTTP and AnyEvent::MCP::Client::JSONL. Choose a transport, keep the client object alive, and run an AnyEvent event loop. You can call a tool without connecting explicitly: the client connects and discovers or initializes the server as needed. Concurrent requests share that work instead of starting independent connections.

An interrupted connection does not mean the server did nothing. A tool might have completed its side effects just before the connection failed. For that reason, the client never replays a tool call automatically. Applications that want retries must decide whether the operation is safe to repeat.

Modern clients use discovery and per-request metadata. Legacy clients use initialization and ordinary request/response. Legacy server-initiated requests are accepted on JSONL; legacy HTTP GET streams are not implemented.

For a runnable call, see "FIRST CALL" in AnyEvent::MCP::Client::HTTP when connecting to an HTTP endpoint or "FIRST CALL" in AnyEvent::MCP::Client::JSONL when launching a subprocess. Both use call from this module. In its callback, $success reports JSON-RPC/transport success; a tool can still return isError true. Keep the client alive while its callbacks are pending.

METHODS

$client = AnyEvent::MCP::Client->new (%config)

Normally construct a transport subclass. Its constructor accepts these common options as well as the transport-specific options:

client_info => { name => $name, version => $version }

Advertised client identity. Defaults to AnyEvent::MCP and this distribution's version.

client_capabilities => { %capabilities }

Supported client interactions, defaulting to an empty hash. Advertising a capability does not install a handler; supply on_request when needed.

versions => [$version...]

Supported protocol revisions in preference order. Defaults to 2026-07-28, 2025-11-25, 2025-06-18 and 2025-03-26. Here, modern means the 2026 discovery/input-challenge protocol; legacy means the 2025 initialization protocol.

request_timeout => $seconds

Optional overall deadline for rpc and call, including connection and user input. Omitted or zero means no overall deadline. This is separate from HTTP's inactivity timeout.

max_rounds => $count

Maximum input-challenge rounds handled by call, defaulting to 32.

name => $name

Optional local configuration name, independent of client_info. HTTP loads [client NAME] defaults only when this is supplied; JSONL does not load transport defaults.

Event callbacks are also constructor options; see "EVENTS".

$client->connect ($done->($success, $result))

Discovers or initializes the server. On success $result is undef. Any JSON-RPC error from discovery falls back to legacy initialization. Transport failures do not trigger fallback. After connecting, protocol_version, server_info, server_capabilities and instructions are available directly in the client hash.

$id = $client->rpc ($method, $params, $done->($success, $result))

Sends a request. $params is a hash reference and is not modified. $id identifies the local operation for cancellation. Callbacks may run before this method returns.

On success $result is the entire decoded result, including isError, _meta and resultType. A result with resultType set to input_required is returned unchanged. $success reports whether the JSON-RPC request succeeded, not whether the tool succeeded: a tool result with isError true still has $success true. On failure $result is an error hash: JSON-RPC errors retain their fields, while local errors have message and transport true. Local cancellation also has cancelled true. Each completion callback runs once.

$id = $client->call ($name, $args, $done->($success, $result), %params)

Calls a tool and handles modern input_required results via on_request. $args is a hash reference of tool arguments. %params supplies additional protocol parameters, such as _meta. Set $params{_meta}{"io.modelcontextprotocol/clientCapabilities"} to override the constructor capabilities for this call, allowing brokers to advertise the upstream capabilities without changing shared state. The original method and arguments are retained; only server-requested answers and opaque requestState are added for continuation. No connection failure causes a retry. Without on_request, an interaction fails locally.

Permission challenges are input requests handled by on_request. To ask locally through the external permission helper:

client_capabilities => { elicitation => { form => {} } },
on_request => sub {
   AnyEvent::MCP::Permissions::Ask->elicitation ($_[1]);
},

Load AnyEvent::MCP::Permissions::Ask before using that callback. It handles permission-tagged elicitation only; applications supporting other elicitation requests dispatch those separately. The client does not silently approve permissions. rpc returns input challenges intact for broker forwarding.

$client->tools ($done->($success, $tools))

Collects all pages of tool definitions into an array reference. Names and definitions are preserved. Repeated pagination cursors are rejected.

$client->notify ($method, $params, $done->($success, $result))

Sends a notification after connecting. $done is optional. Success means the transport accepted it, not that the server acted on it.

$client->cancel ($id, $reason)

Cancels a pending local operation, completes its callback with an error and sends best-effort protocol cancellation for an outstanding wire request. $reason is optional. Late responses are ignored.

$client->close

Closes transport resources, cancels input handlers and fails pending callbacks. New operations can reconnect. Previously sent calls are not repeated.

EVENTS

on_request => $callback->($client, $req)

Handles an input request asynchronously. $req is a AnyEvent::MCP::Client::Request with method and params. Declare the corresponding capabilities explicitly in client_capabilities. For a broker, rpc can instead return the complete input challenge upstream.

on_notification => $callback->($client, $method, $params)

Receives server notifications, including progress and tool-list changes.

on_ready => $callback->($client)

Called once after a successful MCP discovery or initialization, including reconnections. It does not fire for repeated connect calls on an already connected client.

on_close => $callback->($client, $error)

Called when an established connection closes unexpectedly, with a transport error hash. pending counts operations interrupted by the close, before their callbacks were failed. An explicit close without a reason does not trigger this event.