PHPackages                             milpa/mcp-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. milpa/mcp-client

ActiveLibrary[HTTP &amp; Networking](/categories/http)

milpa/mcp-client
================

MCP (Model Context Protocol) client for the Milpa PHP framework: stdio and HTTP/SSE transports, JSON-RPC framing, capability negotiation, and tool/resource discovery for connecting to external MCP servers.

v0.2.0(2d ago)01Apache-2.0PHP &gt;=8.3

Since Jul 7Compare

[ Source](https://github.com/getmilpa/mcp-client)[ Packagist](https://packagist.org/packages/milpa/mcp-client)[ RSS](/packages/milpa-mcp-client/feed)WikiDiscussions Synced today

READMEChangelog (2)Dependencies (11)Versions (3)Used By (0)

 [   ![Milpa](https://raw.githubusercontent.com/getmilpa/core/main/art/lockup/milpa-lockup-v-color-light.svg)  ](https://github.com/getmilpa)

Milpa MCP Client
================

[](#milpa-mcp-client)

> An **MCP (Model Context Protocol) client** for PHP — stdio and HTTP/SSE transports, typed tool/resource contracts, and connection management for talking to external MCP servers. `McpClientManager` connects to several servers at once and routes tool calls to the right one by a namespaced registry name.

[![CI](https://github.com/getmilpa/mcp-client/actions/workflows/ci.yml/badge.svg)](https://github.com/getmilpa/mcp-client/actions/workflows/ci.yml)[![Packagist](https://camo.githubusercontent.com/9ff2338d30e142501f3b65094ae9c71992947de3c0c99d3ff7a365dc870c7bb3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d696c70612f6d63702d636c69656e742e737667)](https://packagist.org/packages/milpa/mcp-client)[![PHP](https://camo.githubusercontent.com/ca03f11ea27dac4dedc8ad56a7bdfc4a9ff5feb825055f9d2983616115076607/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135253230382e332d3737376262342e737667)](https://www.php.net/)[![License](https://camo.githubusercontent.com/798509b4df525f56802b56f8096862487f08023e3d7561c68656f8dab10d0d6e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4170616368652d2d322e302d626c75652e737667)](LICENSE)[![Docs](https://camo.githubusercontent.com/c6dc6a3411e15b0ac7cc4583e8e6a8144181caedb82f5d98753353decda06d77/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f63732d4150492532307265666572656e63652d626c75652e737667)](https://getmilpa.github.io/mcp-client/)

`milpa/mcp-client` is the *outbound* half of MCP for the Milpa framework: it lets a PHP process act as an **MCP client**, connecting to one or more external MCP servers — local ones spawned as subprocesses (stdio) or remote ones over HTTP with Server-Sent Events — and discovering, listing, and calling the tools and resources they expose. **No product coupling**: `HttpSseTransport` speaks HTTP through **PSR-18** (`Psr\Http\Client\ClientInterface`), defaulting to `guzzlehttp/guzzle` when no client is injected.

Install
-------

[](#install)

```
composer require milpa/mcp-client
```

Quick example
-------------

[](#quick-example)

Register a server, connect, and call one of its tools. `McpClientManager` picks the right `TransportInterface` from the `transport` key in the config you pass it:

```
use Milpa\McpClient\McpClientManager;

$manager = new McpClientManager();

$manager->registerServer('calculator', [
    'transport' => 'stdio',
    'command' => 'php',
    'args' => [__DIR__ . '/mcp-servers/calculator.php'],
]);

$manager->connect('calculator');
```

`connect()` runs the MCP handshake (`initialize` → `notifications/initialized`) and discovers the server's tools and resources; every tool is indexed under a namespaced **registry name** — `mcp_{server}_{tool}` — so a `McpClientManager` juggling several servers never collides on a bare tool name:

```
foreach ($manager->listAllTools() as $tool) {
    echo $tool->getRegistryName(), ' — ', $tool->description, "\n";
}
// mcp_calculator_add — Add two numbers

$result = $manager->callTool('mcp_calculator_add', ['a' => 2, 'b' => 3]);
// ['sum' => 5]

$manager->disconnectAll();
```

A remote server over HTTP/SSE looks the same, just with a different transport config:

```
$manager->registerServer('remote-docs', [
    'transport' => 'http-sse',
    'url' => 'https://mcp.example.com/',
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'timeout' => 30,
]);
```

`connectAll()` connects every registered server and returns a `[name => Throwable]` map of the ones that failed, instead of throwing on the first bad server.

### Bringing your own HTTP client (PSR-18)

[](#bringing-your-own-http-client-psr-18)

`McpClientManager::registerServer()` always builds its own `HttpSseTransport` from the config array, so it always gets the default Guzzle client. To inject your own PSR-18 client — a shared connection pool, a client wrapped with retry/circuit-breaker middleware, or a test double — construct `HttpSseTransport` directly and wrap it in a `McpConnection` yourself:

```
use Milpa\McpClient\McpConnection;
use Milpa\McpClient\Transports\HttpSseTransport;

$transport = new HttpSseTransport(
    baseUrl: 'https://mcp.example.com/',
    headers: ['Authorization' => 'Bearer ' . $token],
    serverName: 'remote-docs',
    httpClient: $yourPsr18Client,     // Psr\Http\Client\ClientInterface; omit for Guzzle
    requestFactory: $yourPsr17Factory, // Psr\Http\Message\RequestFactoryInterface; optional
    streamFactory: $yourPsr17Factory,  // Psr\Http\Message\StreamFactoryInterface; optional
    logger: $yourLogger,               // Psr\Log\LoggerInterface; optional, see below
);

$connection = new McpConnection('remote-docs', $transport);
$connection->connect();
```

`$timeout` only configures the *default* Guzzle client — it has no effect once you inject your own `$httpClient`, since PSR-18's `sendRequest()` takes no per-call options.

`notify()` (fire-and-forget JSON-RPC notifications) never throws — that's the `TransportInterface` contract — but a transport failure during `notify()` is reported to `$logger` (at `warning` level) instead of being silently dropped, when a logger is injected.

What it is
----------

[](#what-it-is)

- **`McpClientManager`** — the entry point most callers use. Registers servers from plain config arrays, owns their `McpConnection`s, and aggregates + routes tool calls across all of them by registry name.
- **`McpConnection`** — one server's live session: handshake, capability discovery, `listTools()` / `getTool()` / `callTool()`, `listResources()` / `readResource()`, and `refresh()` to re-discover after the server's tool list changes.
- **`TransportInterface`** — the seam a transport implements: `connect()`, `disconnect()`, `isConnected()`, `request()` (JSON-RPC call, blocks for a reply), and `notify()` (JSON-RPC notification, no reply expected).
    - **`StdioTransport`** — spawns the server as a subprocess (`proc_open`) and speaks newline-delimited JSON-RPC over its stdin/stdout.
    - **`HttpSseTransport`** — POSTs JSON-RPC through an injectable PSR-18 `ClientInterface` (Guzzle by default) and parses the reply whether the server answers with a plain JSON body or a `text/event-stream` SSE payload.
- **Typed contracts** — `McpTool`, `McpResource`, and `McpCapabilities` are immutable value objects built with `fromArray()` from the server's raw JSON-RPC responses, not arrays passed around by convention.
- **A layered exception hierarchy** — every exception extends `McpException`, which carries the offending `serverName` for context; `McpConnectionException`, `McpTransportException`, `McpToolException`, and `McpServerException` (which also carries the JSON-RPC `errorCode` and `errorData`) narrow the failure mode.

**Be honest about scope:** this package is a client only — it does not implement or host an MCP server. It does not persist tool schemas, cache results, or retry failed calls; those policies belong to the host application.

What's inside
-------------

[](#whats-inside)

NamespaceWhat it provides`Milpa\McpClient``McpClientManager`, `McpConnection``Milpa\McpClient\Contracts``TransportInterface`, `McpTool`, `McpResource`, `McpCapabilities`, `McpException`, `McpConnectionException`, `McpServerException`, `McpToolException`, `McpTransportException``Milpa\McpClient\Transports``StdioTransport`, `HttpSseTransport`Every public symbol carries a DocBlock.

Requirements
------------

[](#requirements)

- PHP **≥ 8.3**
- [`guzzlehttp/guzzle`](https://packagist.org/packages/guzzlehttp/guzzle) **^7.10** — the default PSR-18 implementation `HttpSseTransport` falls back to when no `ClientInterface`is injected (also brings `guzzlehttp/psr7`, used as the default PSR-17 factory)
- `psr/http-client`, `psr/http-factory`, `psr/http-message`, `psr/log` — the interfaces `HttpSseTransport`'s constructor is typed against

Documentation
-------------

[](#documentation)

**Full API reference: [getmilpa.github.io/mcp-client](https://getmilpa.github.io/mcp-client/)** — generated straight from the source DocBlocks and dressed with the Milpa design system.

Contributing
------------

[](#contributing)

Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Please report security issues via [SECURITY.md](SECURITY.md), and note that this project follows a [Code of Conduct](CODE_OF_CONDUCT.md).

License
-------

[](#license)

[Apache-2.0](LICENSE) © TeamX Agency.

---

Milpa is designed, built, and maintained by **[TeamX Agency](https://teamx.agency/?utm_source=github&utm_medium=readme&utm_campaign=milpa&utm_content=mcp-client)**.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance99

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

2d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1993784?v=4)[rodrigomx](/maintainers/rodrigomx)[@rodrigomx](https://github.com/rodrigomx)

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/milpa-mcp-client/health.svg)

```
[![Health](https://phpackages.com/badges/milpa-mcp-client/health.svg)](https://phpackages.com/packages/milpa-mcp-client)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.3k543.5M2.7k](/packages/aws-aws-sdk-php)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M423](/packages/drupal-core-recommended)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M582](/packages/shopware-core)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k5.9M750](/packages/sylius-sylius)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
