# Connecting to a server A client is configured once through its builder, then connected to a [transport](transports.md). Connecting performs the MCP initialization handshake, after which the server's capabilities are known and its elements can be used. On protocol revision `2026-07-28` there is no handshake to perform — see [Clients on this revision](../protocol-versions.md). ## Client Builder The `Client\Builder` provides fluent configuration of client instances. ### Basic Configuration ```php use Mcp\Client; $client = Client::builder() ->setClientInfo('My Application', '1.0.0', 'Description of my client') ->setInitTimeout(30) // Seconds to wait for initialization ->setRequestTimeout(120) // Seconds to wait for request responses ->setMaxRetries(3) // Retries for failed connections ->build(); ``` ### Connection Retries `setMaxRetries()` controls how often `connect()` retries a failed connection. It counts retries rather than attempts, so the default of `3` means one initial attempt plus up to three retries — four in total — before the `ConnectionException` of the last attempt is rethrown: ```php $client = Client::builder() ->setMaxRetries(0) // Fail on the first failed attempt ->build(); ``` Between two attempts the transport is closed, so a retry never reuses a half-established connection: a `StdioTransport` spawns a fresh server process and an `HttpTransport` discards the session ID of the failed attempt. Each retry is preceded by a short, linearly growing delay (100ms, 200ms, 300ms, …). Only the connection handshake is retried. Individual requests such as `callTool()` are always sent once — retrying them is unsafe as tool calls are not necessarily idempotent. ### Client Information Set the client's identity reported to servers during initialization: ```php $client = Client::builder() ->setClientInfo( name: 'AI Assistant Client', version: '2.1.0', description: 'Client for automated AI workflows' ) ->build(); ``` ### Protocol Version Specify the MCP protocol version to offer during the handshake (defaults to `V2025_11_25`, the latest handshake revision — the modern `2026-07-28` revision must be chosen explicitly): ```php use Mcp\Schema\Enum\ProtocolVersion; $client = Client::builder() ->setProtocolVersion(ProtocolVersion::V2025_11_25) ->build(); ``` This is an offer, not a demand. A server that does not support the requested revision counter-offers one it does, as described in the specification's [protocol version negotiation](https://modelcontextprotocol.io/specification/latest/basic/versioning#protocol-version-negotiation) section. The client accepts any counter-offer it knows about and continues on that revision; a counter-offer the SDK cannot speak fails the handshake with a `ConnectionException` rather than continuing on a revision neither side agreed on. Use `$client->getProtocolVersion()` after connecting to read what was actually negotiated. Setting a modern revision such as `2026-07-28` selects the other lifecycle rather than making an offer: there is no `initialize` to negotiate with, so `connect()` sends none and every request carries its own revision instead. Nothing else about the client API changes. See [Clients on this revision](../protocol-versions.md) for what happens underneath. See [Protocol versions](../protocol-versions.md#negotiating-in-the-handshake-era) for the server side of the exchange. ### Capabilities Declare client capabilities to enable server features: ```php use Mcp\Schema\ClientCapabilities; $client = Client::builder() ->setCapabilities(new ClientCapabilities( sampling: true, // Enable LLM sampling requests from server roots: true, // Enable filesystem root listing )) ->build(); ``` ### Notification Handlers Register handlers for server-initiated notifications: ```php use Mcp\Client\Handler\Notification\LoggingNotificationHandler; use Mcp\Schema\Notification\LoggingMessageNotification; $loggingHandler = new LoggingNotificationHandler( static function (LoggingMessageNotification $notification) { echo "[{$notification->level->value}] {$notification->data}\n"; } ); $client = Client::builder() ->addNotificationHandler($loggingHandler) ->build(); ``` ### Request Handlers Register handlers for server-initiated requests (e.g., sampling). The same handlers answer a [multi round-trip](../handlers/input-required.md) `input_required` result on a modern revision, where the server returns its ask instead of sending a request: ```php use Mcp\Client\Handler\Request\SamplingRequestHandler; use Mcp\Client\Handler\Request\SamplingCallbackInterface; use Mcp\Schema\Request\CreateSamplingMessageRequest; use Mcp\Schema\Result\CreateSamplingMessageResult; $samplingCallback = new class implements SamplingCallbackInterface { public function __invoke(CreateSamplingMessageRequest $request): CreateSamplingMessageResult { // Perform LLM sampling and return result } }; $client = Client::builder() ->addRequestHandler(new SamplingRequestHandler($samplingCallback)) ->build(); ``` ### Logger Configure PSR-3 logging for debugging: ```php use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('mcp-client'); $logger->pushHandler(new StreamHandler('client.log', Logger::DEBUG)); $client = Client::builder() ->setLogger($logger) ->build(); ``` ## Connecting to Servers ### Establishing Connection ```php $client->connect($transport); ``` The `connect()` method performs the MCP initialization handshake: 1. Opens the transport connection 2. Sends InitializeRequest with client capabilities 3. Waits for InitializeResult from server 4. Sends InitializedNotification On a modern revision it opens the transport and asks `server/discover` for the server's identity instead; a server that does not answer that optional method still yields a usable connection. !!! warning Always wrap connection in try/catch to handle `ConnectionException` for failed connections. ### Checking Connection State ```php if ($client->isConnected()) { // Client is connected and initialized } ``` ### Disconnecting ```php $client->disconnect(); ``` Always disconnect when finished to clean up resources: ```php try { $client->connect($transport); // ... use the client ... } finally { $client->disconnect(); } ``` ## Server Information After successful connection, retrieve server metadata: ```php // Get server implementation info $serverInfo = $client->getServerInfo(); echo "Server: {$serverInfo->name} v{$serverInfo->version}\n"; // Get server instructions $instructions = $client->getInstructions(); if ($instructions) { echo "Instructions: {$instructions}\n"; } ```