# `ExMCP.Client.RequestHandler`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/client/request_handler.ex#L1)

Request/response processing for ExMCP client.

This module handles all request processing, batch operations, message parsing,
and response handling for MCP clients.

# `handle_batch_request`

Handles batch MCP requests.

Processes multiple MCP requests in a single batch operation.

# `handle_batch_response`

Handles a batch of responses from the transport.

# `handle_cast_notification`

Handles a notification to be sent to the server.

# `handle_request`

Handles individual MCP requests.

Processes a single MCP request and returns the appropriate GenServer response.

The optional `meta` map controls request timeout enforcement:

- `%{timeout: nil}` - the caller did not specify a timeout, so the client
  process enforces its own `default_timeout` by scheduling a
  `{:request_timeout, id}` message for pending requests
- `%{timeout: integer}` - the caller enforces the timeout on its side
  (via the `GenServer.call/3` timeout), so no timer is scheduled
- `%{timeout: :caller_enforced}` - legacy `{:request, method, params}`
  calls; no timer is scheduled

# `handle_server_request`

Routes a legacy server-to-client request to the appropriate handler callback.

Modern MCP 2026-07-28 supplies client input through MRTR result envelopes;
it does not deliver these as independent JSON-RPC requests on the stream.

# `handle_single_response`

Handles a single response from the transport.

# `parse_transport_message`

Parses a message from the transport.

This function is intended to be called from the client's `handle_info/2` callback.
It decodes the message and delegates to the appropriate response handler.

# `send_message`

Encodes and sends a message via the transport.

# `send_ping`

```elixir
@spec send_ping(map()) :: {:ok, term() | nil, map()} | {:error, any()}
```

Sends the era-appropriate liveness request for the client's idle health
check: legacy `ping`, or an uncached modern `server/discover`.

The ping is deliberately kept out of `pending_requests`: it has no caller to
reply to and must not show up in `ExMCP.Client.get_pending_requests/1`.

Returns:

- `{:ok, request_id, state}` - ping sent, the pong will arrive asynchronously
- `{:ok, nil, state}` - a synchronous transport answered inline, so the
  connection is already proven alive
- `{:error, reason}` - the ping could not be sent

---

*Consult [api-reference.md](api-reference.md) for complete listing*
