Skip to content

Latest commit

 

History

History
151 lines (123 loc) · 5.18 KB

errors.md

File metadata and controls

151 lines (123 loc) · 5.18 KB

Error Handling

Every exception the client throws implements Mcp\Exception\ExceptionInterface. Catch it to handle all client errors in one place. All classes below live in the Mcp\Exception namespace:

Exception Thrown when
ConnectionException connect() fails on every attempt, an HTTP request fails on the network level, or you call a method before connect()
RequestException the server answers with an error, the request reaches the default request timeout (code -32603, message Request timed out), the server answers with an HTTP error status, or the STDIO server process exits while you wait for an answer
TimeoutException the timeoutSeconds deadline of a callTool() call expires
RequestCancelledException the cancellation token of a callTool() call is canceled, see Cancellation and deadlines
InvalidArgumentException callTool() gets a timeoutSeconds that is not finite and positive, readResource() gets an empty URI, the builder gets an invalid timeout, retry count or fallback revision, the request can't be encoded as JSON (for example invalid UTF-8 in tool arguments), or the server returns a malformed result (also from connect())
RuntimeException callTool() calls a tool with malformed x-mcp-header annotations, you call sendRootsListChanged() without the rootsListChanged capability, or, on 2026-07-28, the server asks for input (elicitation, sampling, roots) that no request handler answers, or whose callback throws

An HTTP error status or an exited server process fails the request at once. The client doesn't wait for the request timeout.

ConnectionException

use Mcp\Exception\ConnectionException;

try {
    $client->connect($transport);
} catch (ConnectionException $e) {
    echo "Failed to connect: {$e->getMessage()}\n";
}

RequestException

use Mcp\Exception\RequestException;

try {
    $result = $client->callTool('unknown_tool', []);
} catch (RequestException $e) {
    echo "Request failed: {$e->getMessage()}\n";
    echo "Error code: {$e->getCode()}\n";
}

TimeoutException

Thrown when the server does not answer within the request timeout. It extends RequestException, so catch it first to tell a timeout from an error response:

use Mcp\Exception\RequestException;
use Mcp\Exception\TimeoutException;

try {
    $result = $client->callTool('slow_tool', []);
} catch (TimeoutException $e) {
    echo "The server did not answer in time\n";
} catch (RequestException $e) {
    echo "Request failed: {$e->getMessage()}\n";
}

Complete Example

Here's a comprehensive example demonstrating client usage:

<?php

use Mcp\Client;
use Mcp\Client\Handler\Request\ElicitationCallbackInterface;
use Mcp\Client\Handler\Request\ElicitationRequestHandler;
use Mcp\Client\Transport\StdioTransport;
use Mcp\Schema\ClientCapabilities;
use Mcp\Schema\Content\TextContent;
use Mcp\Schema\Enum\ElicitAction;
use Mcp\Schema\Request\ElicitRequest;
use Mcp\Schema\Result\ElicitResult;

// Configure elicitation callback
$elicitationCallback = new class implements ElicitationCallbackInterface {
    public function __invoke(ElicitRequest $request): ElicitResult
    {
        echo "[ELICITATION] {$request->message}\n";

        // This client has no user interface, so it declines every request
        return new ElicitResult(ElicitAction::Decline);
    }
};

// Build client
$client = Client::builder()
    ->setClientInfo('Example Client', '1.0.0')
    ->setInitTimeout(30)
    ->setRequestTimeout(120)
    ->setCapabilities(new ClientCapabilities(elicitation: true))
    ->addRequestHandler(new ElicitationRequestHandler($elicitationCallback))
    ->build();

// Create transport
$transport = new StdioTransport(
    command: 'php',
    args: [__DIR__ . '/server.php'],
);

// Connect and use server
try {
    echo "Connecting to server...\n";
    $client->connect($transport);
    
    // Get server info
    $serverInfo = $client->getServerInfo();
    echo "Connected to: {$serverInfo?->name} v{$serverInfo?->version}\n\n";
    
    // List capabilities
    echo "Available tools:\n";
    $tools = $client->listTools();
    foreach ($tools->tools as $tool) {
        echo "  - {$tool->name}\n";
    }
    
    echo "\nAvailable resources:\n";
    $resources = $client->listResources();
    foreach ($resources->resources as $resource) {
        echo "  - {$resource->uri}\n";
    }
    
    // Call tool with progress
    echo "\nCalling tool with progress...\n";
    $result = $client->callTool(
        name: 'process_data',
        arguments: ['dataset' => 'large_file.csv'],
        onProgress: static function (float $progress, ?float $total, ?string $message) {
            $percent = $total > 0 ? round(($progress / $total) * 100) : 0;
            echo "  Progress: {$percent}% - {$message}\n";
        }
    );
    
    echo "\nResult:\n";
    foreach ($result->content as $content) {
        if ($content instanceof TextContent) {
            echo $content->text . "\n";
        }
    }
    
} catch (\Throwable $e) {
    echo "Error: {$e->getMessage()}\n";
    echo $e->getTraceAsString() . "\n";
} finally {
    $client->disconnect();
    echo "\nDisconnected.\n";
}