Search by

eskono / mcp-client

eskono

Framework-agnostic PHP 8.3+ SDK implementing the client side of the Model Context Protocol (MCP).

Package info

github.com/eskono/mcp-client

pkg:composer/eskono/mcp-client

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-07 10:31 UTC

This package is auto-updated.

Last update: 2026-09-07 11:42:08 UTC


README

CI PHP License Protocol

Framework-agnostic PHP 8.3+ SDK implementing the client side of the Model Context Protocol (MCP). It lets any PHP application connect to MCP servers โ€” both local (spawned processes over stdio) and, soon, remote (Streamable HTTP) โ€” to discover and invoke tools, read resources and render prompts over JSON-RPC 2.0.

No framework required. HTTP support is built on PSR interfaces (PSR-18/PSR-17), logging on PSR-3 โ€” you bring the implementations via dependency injection.

Features

  • ๐Ÿงฉ Strictly typed โ€” every request, response and MCP entity is a readonly DTO. No raw arrays leaking out of the public API.
  • ๐Ÿ”Œ Pluggable transport โ€” one TransportInterface; ship with a dependency-free StdioTransport (native proc_open + non-blocking streams).
  • ๐Ÿ“ž Full JSON-RPC 2.0 โ€” requests, responses, notifications, errors, id correlation, and handling of server-initiated requests.
  • ๐Ÿ” Discovery โ€” listTools(), listResources(), listPrompts() with transparent cursor pagination.
  • โšก Execution โ€” callTool(), readResource(), getPrompt().
  • ๐Ÿชต Observable โ€” pass any PSR-3 logger; JSON-RPC traffic is logged at debug, server stderr is forwarded automatically.
  • โœ… Tested โ€” PHPUnit 10 suite covering the protocol layer and the client.

Requirements

  • PHP 8.3+
  • psr/log (PSR-3)
  • psr/http-client + psr/http-factory (PSR-18/PSR-17, used by the upcoming HTTP transport)
  • For the stdio transport: the ability to run the target server binary (node, python, npx, โ€ฆ). No PHP extension required.

Installation

composer require eskono/mcp-client

Quick start

Connect to a local MCP server started as a child process, run the handshake, list its tools and call one:

<?php

require __DIR__ . '/vendor/autoload.php';

use Mcp\McpClient;
use Mcp\Transport\StdioTransport;
use Mcp\Transport\StdioServerParameters;

// 1. Describe how to launch the server (argv form โ€” no shell, no injection).
$transport = new StdioTransport(
    new StdioServerParameters(
        command: 'node',
        args: ['weather-server.js'],
    ),
);

// 2. Create the client and perform the MCP handshake.
$client = new McpClient($transport);
$init = $client->initialize();

echo "Connected to {$init->serverInfo->name} {$init->serverInfo->version}\n";

// 3. Discover tools.
foreach ($client->listTools() as $tool) {
    echo "- {$tool->name}: {$tool->description}\n";
}

// 4. Call a tool.
$result = $client->callTool('get_weather', ['city' => 'Paris']);
echo $result->text() . "\n";

// 5. Clean up (also terminates the child process).
$client->close();

Shortcut: McpClient::stdio('node', ['weather-server.js']) builds the transport for you (and McpClient::http(...) for remote servers).

More runnable scripts live in examples/.

Core concepts

The SDK is layered so each concern is testable in isolation:

          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚                   McpClient                    โ”‚  facade: initialize / list* / call* / read* / getPrompt
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚ typed DTOs (Tool, Resource, โ€ฆ)  โ–ฒ
                     โ–ผ                                 โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚             JSON-RPC 2.0 protocol              โ”‚  JsonRpcRequest/Response/Notification/Error, MessageParser
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚ JsonRpcMessageInterface          โ–ฒ
                     โ–ผ                                  โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚              TransportInterface                โ”‚  StdioTransport (proc_open) โ”‚ StreamableHttpTransport (soon)
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚ stdin/stdout                     โ–ฒ stderr โ†’ PSR-3
                     โ–ผ                                  โ”‚
                              MCP server process
  • Transport moves framed JSON-RPC messages to/from a server.
  • JSON-RPC layer models the wire protocol as immutable DTOs.
  • DTO layer models MCP entities (tools, resources, prompts, content blocks).
  • McpClient ties it together: handshake, request/response correlation, pagination, and error mapping.

See docs/architecture.md for details.

Transports

Transport Status Use for
StdioTransport โœ… available Local servers launched as a child process
StreamableHttpTransport โœ… available Remote servers over HTTP (PSR-18/PSR-17)

StdioTransport reads protocol messages from the server's stdout (newline-delimited JSON), writes to its stdin, and forwards stderr to your logger. StreamableHttpTransport speaks the MCP Streamable HTTP protocol (POST + application/json / text/event-stream, Mcp-Session-Id) over any PSR-18 client you inject. Details in docs/transports.md.

Working with results

$result = $client->callTool('get_weather', ['city' => 'Paris']);

$result->isError;            // tool-level error flag (bool)
$result->text();             // concatenated text of all TextContent blocks
$result->structuredContent;  // structured output (array|null)

foreach ($result->content as $block) {
    // TextContent | ImageContent | AudioContent | EmbeddedResource
    match (true) {
        $block instanceof \Mcp\Model\Content\TextContent  => print($block->text),
        $block instanceof \Mcp\Model\Content\ImageContent => file_put_contents('img', $block->decoded()),
        default => null,
    };
}

$read = $client->readResource('file:///readme.md');
$first = $read->first();     // ?ResourceContentsInterface
echo $first->uri();

Full DTO map: docs/dtos.md.

Error handling

All SDK exceptions extend Mcp\Exception\McpException:

Exception Thrown when
JsonRpcException The server answered a request with a JSON-RPC error. Exposes getRpcError() / getRpcData() / getMethod().
TimeoutException No response arrived within requestTimeout.
TransportException The transport failed (process died, pipe closed, โ€ฆ).
ProtocolException A malformed / unrecognizable JSON-RPC payload.
RequestCancelledException The request was cancelled via a CancellationToken.
use Mcp\Exception\JsonRpcException;

try {
    $client->callTool('unknown_tool');
} catch (JsonRpcException $e) {
    echo "Server error {$e->getRpcError()->code}: {$e->getMessage()}\n";
}

More in docs/error-handling.md.

Logging

Pass any PSR-3 logger; it receives JSON-RPC traffic at debug and forwarded server stderr:

$client = new McpClient($transport, $logger);

See docs/logging.md.

Testing

composer install
vendor/bin/phpunit

Note: this project targets PHP 8.3+. If your default php is older, run the suite with an explicit 8.3 binary, e.g. /opt/homebrew/opt/php@8.3/bin/php vendor/bin/phpunit. See CONTRIBUTING.md.

Documentation

Versioning & compatibility

This package follows Semantic Versioning. While it is pre-1.0, minor releases may include breaking changes (noted in the changelog).

eskono/mcp-client PHP MCP protocol (preferred)
^0.1 8.3, 8.4 2025-06-18

The client sends its preferred protocol version on initialize and records what the server negotiates ($init->protocolVersion), so it also interoperates with servers speaking earlier revisions (2025-03-26, 2024-11-05) for the features they support.

Releases

See the changelog for release notes and RELEASING.md for the maintainer release process.

Contributing

Contributions welcome โ€” see CONTRIBUTING.md.

License

MIT.