[Client] docs: introduce client SDK documentation and improve README structure (#259)
* docs: add comprehensive client SDK documentation - Create new client documentation with examples and API reference - Restructure README for better clarity and flow - Add client examples to documentation index - Update roadmap to mark client component as complete * Update README.md Co-authored-by: Christopher Hertel <mail@christopher-hertel.de> * Apply suggestions from code review Co-authored-by: Christopher Hertel <mail@christopher-hertel.de> --------- Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
This commit is contained in:
parent
7a5d5f5cfb
commit
85fddf5f0f
5 changed files with 1019 additions and 158 deletions
334
README.md
334
README.md
|
|
@ -10,202 +10,143 @@ development practices and standards from the Symfony project, including [Coding
|
|||
Until the first major release, this SDK is considered [experimental](https://symfony.com/doc/current/contributing/code/experimental.html), please see the [roadmap](./ROADMAP.md) for
|
||||
planned next steps and features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Overview](#overview)
|
||||
- [Server SDK](#server-sdk)
|
||||
- [Client SDK](#client-sdk)
|
||||
- [Documentation](#documentation)
|
||||
- [External Resources](#external-resources)
|
||||
- [PHP Libraries Using the MCP SDK](#php-libraries-using-the-mcp-sdk)
|
||||
- [Contributing](#contributing)
|
||||
- [Credits](#credits)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer require mcp/sdk
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
## Overview
|
||||
|
||||
This example demonstrates the most common usage pattern - a STDIO server using attribute discovery.
|
||||
The MCP PHP SDK provides both **server** and **client** implementations for the Model Context Protocol, enabling you to:
|
||||
|
||||
### 1. Define Your MCP Elements
|
||||
- **Build MCP Servers**: Expose your PHP application's functionality (tools, resources, prompts) to AI agents
|
||||
- **Build MCP Clients**: Connect to and interact with MCP servers from your PHP applications
|
||||
|
||||
Create a class with MCP capabilities using attributes:
|
||||
## Server SDK
|
||||
|
||||
Build MCP servers to expose your PHP application's capabilities to AI agents like Claude, Codex, and others.
|
||||
|
||||
### Quick Start
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Transport\StdioTransport;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
|
||||
class CalculatorElements
|
||||
// Define capabilities using PHP attributes
|
||||
class CalculatorCapabilities
|
||||
{
|
||||
/**
|
||||
* Adds two numbers together.
|
||||
*
|
||||
* @param int $a The first number
|
||||
* @param int $b The second number
|
||||
* @return int The sum of the two numbers
|
||||
*/
|
||||
#[McpTool]
|
||||
public function add(int $a, int $b): int
|
||||
{
|
||||
return $a + $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs basic arithmetic operations.
|
||||
*/
|
||||
#[McpTool(name: 'calculate')]
|
||||
public function calculate(float $a, float $b, string $operation): float|string
|
||||
{
|
||||
return match($operation) {
|
||||
'add' => $a + $b,
|
||||
'subtract' => $a - $b,
|
||||
'multiply' => $a * $b,
|
||||
'divide' => $b != 0 ? $a / $b : 'Error: Division by zero',
|
||||
default => 'Error: Unknown operation'
|
||||
};
|
||||
}
|
||||
|
||||
#[McpResource(
|
||||
uri: 'config://calculator/settings',
|
||||
name: 'calculator_config',
|
||||
mimeType: 'application/json'
|
||||
)]
|
||||
#[McpResource(uri: 'config://calculator/settings')]
|
||||
public function getSettings(): array
|
||||
{
|
||||
return ['precision' => 2, 'allow_negative' => true];
|
||||
return ['precision' => 2];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Create the Server Script
|
||||
|
||||
Create your MCP server:
|
||||
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Transport\StdioTransport;
|
||||
|
||||
// Build and run the server
|
||||
$server = Server::builder()
|
||||
->setServerInfo('Calculator Server', '1.0.0')
|
||||
->setDiscovery(__DIR__, ['.'])
|
||||
->setDiscovery(__DIR__, ['.']) // Auto-discover attributes
|
||||
->build();
|
||||
|
||||
$transport = new StdioTransport();
|
||||
|
||||
$server->run($transport);
|
||||
```
|
||||
|
||||
### 3. Configure Your MCP Client
|
||||
### Server Capabilities
|
||||
|
||||
Add to your client configuration (e.g., Claude Desktop's `mcp.json`):
|
||||
- **Tools**: Executable functions that AI agents can call
|
||||
- **Resources**: Data sources that can be read (files, configs, databases)
|
||||
- **Resource Templates**: Dynamic resources with URI parameters
|
||||
- **Prompts**: Pre-defined templates for AI interactions
|
||||
- **Server-Initiated Communication**: Elicitations, sampling, logging, progress notifications
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"php-calculator": {
|
||||
"command": "php",
|
||||
"args": ["/absolute/path/to/your/server.php"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
### Registration Methods
|
||||
|
||||
### 4. Test Your Server
|
||||
|
||||
```bash
|
||||
# Test with MCP Inspector
|
||||
npx @modelcontextprotocol/inspector php /path/to/server.php
|
||||
|
||||
# Your AI assistant can now call:
|
||||
# - add: Add two integers
|
||||
# - calculate: Perform arithmetic operations
|
||||
# - Read config://calculator/settings resource
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### Attribute-Based Discovery
|
||||
|
||||
Define MCP elements using PHP attributes with automatic discovery:
|
||||
There are multiple ways to register your MCP capabilities—choose the approach that best fits your application's architecture:
|
||||
|
||||
**1. Attribute-Based Discovery** — Define capabilities using PHP attributes for automatic discovery:
|
||||
```php
|
||||
// Tool with automatic name and description from method
|
||||
#[McpTool]
|
||||
public function generateReport(): string { /* ... */ }
|
||||
|
||||
// Tool with custom name
|
||||
#[McpTool(name: 'custom_name')]
|
||||
public function myMethod(): string { /* ... */ }
|
||||
|
||||
// Resource with URI and metadata
|
||||
#[McpResource(uri: 'config://app/settings', mimeType: 'application/json')]
|
||||
#[McpResource(uri: 'config://app/settings')]
|
||||
public function getConfig(): array { /* ... */ }
|
||||
```
|
||||
|
||||
### Manual Registration
|
||||
|
||||
Register capabilities programmatically:
|
||||
|
||||
**2. Manual Registration** — Register capabilities programmatically without attributes:
|
||||
```php
|
||||
$server = Server::builder()
|
||||
->addTool([MyClass::class, 'myMethod'], 'tool_name')
|
||||
->addResource([MyClass::class, 'getData'], 'data://config')
|
||||
->addTool([Calculator::class, 'add'], 'add_numbers')
|
||||
->addResource([Config::class, 'get'], 'config://app')
|
||||
->build();
|
||||
```
|
||||
|
||||
### Multiple Transport Options
|
||||
**3. Hybrid Approach** — Combine both methods for maximum flexibility:
|
||||
```php
|
||||
$server = Server::builder()
|
||||
->setDiscovery(__DIR__, ['.'])
|
||||
->addTool([ExternalService::class, 'process'], 'external')
|
||||
->build();
|
||||
```
|
||||
|
||||
**STDIO Transport** (Command-line integration):
|
||||
### Transports
|
||||
|
||||
Choose the transport that matches your deployment environment:
|
||||
|
||||
**1. STDIO Transport** — For command-line integration and local processes:
|
||||
```php
|
||||
$transport = new StdioTransport();
|
||||
$server->run($transport);
|
||||
```
|
||||
|
||||
**HTTP Transport** (Web-based communication):
|
||||
**2. HTTP Transport** — For web-based servers and distributed systems:
|
||||
```php
|
||||
$transport = new StreamableHttpTransport($request, $responseFactory, $streamFactory);
|
||||
$response = $server->run($transport);
|
||||
// Handle $response in your web application
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
By default, the SDK uses in-memory sessions. You can configure different session stores:
|
||||
Configure session storage to maintain state between requests. Choose the backend that fits your infrastructure:
|
||||
|
||||
**In-Memory** (default, suitable for STDIO):
|
||||
```php
|
||||
use Mcp\Server\Session\FileSessionStore;
|
||||
use Mcp\Server\Session\InMemorySessionStore;
|
||||
use Mcp\Server\Session\Psr16SessionStore;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
|
||||
// Use default in-memory sessions with custom TTL
|
||||
$server = Server::builder()
|
||||
->setSession(ttl: 7200) // 2 hours
|
||||
->build();
|
||||
```
|
||||
|
||||
// Override with file-based storage
|
||||
**File-Based** (suitable for single-server HTTP deployments):
|
||||
```php
|
||||
$server = Server::builder()
|
||||
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
|
||||
->build();
|
||||
```
|
||||
|
||||
// Override with in-memory storage and custom TTL
|
||||
$server = Server::builder()
|
||||
->setSession(new InMemorySessionStore(3600))
|
||||
->build();
|
||||
|
||||
// Override with PSR-16 cache-based storage
|
||||
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
|
||||
// composer require psr/simple-cache symfony/cache
|
||||
$redisAdapter = new RedisAdapter(
|
||||
RedisAdapter::createConnection('redis://localhost:6379'),
|
||||
'mcp_sessions'
|
||||
);
|
||||
|
||||
**PSR-16 Cache** (for example with Redis for scaled deployments):
|
||||
```php
|
||||
$server = Server::builder()
|
||||
->setSession(new Psr16SessionStore(
|
||||
cache: new Psr16Cache($redisAdapter),
|
||||
|
|
@ -215,60 +156,141 @@ $server = Server::builder()
|
|||
->build();
|
||||
```
|
||||
|
||||
### Discovery Caching
|
||||
[→ Server Documentation](docs/server-builder.md)
|
||||
|
||||
Use any PSR-16 cache implementation to cache discovery results and avoid running discovery on every server start:
|
||||
## Client SDK
|
||||
|
||||
Connect to MCP servers from your PHP applications to access their tools, resources, and prompts.
|
||||
|
||||
### Quick Start
|
||||
|
||||
```php
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
use Mcp\Client;
|
||||
use Mcp\Client\Transport\StdioTransport;
|
||||
|
||||
$cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery'));
|
||||
// Build the client
|
||||
$client = Client::builder()
|
||||
->setClientInfo('My Application', '1.0.0')
|
||||
->setInitTimeout(30)
|
||||
->setRequestTimeout(120)
|
||||
->build();
|
||||
|
||||
$server = Server::builder()
|
||||
->setDiscovery(
|
||||
basePath: __DIR__,
|
||||
scanDirs: ['.', 'src'], // Default: ['.', 'src']
|
||||
excludeDirs: ['vendor'], // Default: ['vendor', 'node_modules']
|
||||
cache: $cache
|
||||
)
|
||||
// Connect to a server
|
||||
$transport = new StdioTransport(
|
||||
command: 'php',
|
||||
args: ['/path/to/server.php'],
|
||||
);
|
||||
|
||||
$client->connect($transport);
|
||||
|
||||
// Discover and use capabilities
|
||||
$tools = $client->listTools();
|
||||
$result = $client->callTool('add', ['a' => 5, 'b' => 3]);
|
||||
|
||||
$resources = $client->listResources();
|
||||
$content = $client->readResource('config://calculator/settings');
|
||||
|
||||
$client->disconnect();
|
||||
```
|
||||
|
||||
### Client Capabilities
|
||||
|
||||
- **Tool Calling**: List and execute tools from any MCP server
|
||||
- **Resource Access**: Read static and dynamic resources
|
||||
- **Prompt Management**: List and retrieve prompt templates
|
||||
- **Completion Support**: Request argument completion suggestions
|
||||
|
||||
### Advanced Features
|
||||
|
||||
- **Progress Tracking**: Real-time progress during long operations
|
||||
```php
|
||||
$result = $client->callTool(
|
||||
name: 'process_data',
|
||||
arguments: ['dataset' => 'large_file.csv'],
|
||||
onProgress: function (float $progress, ?float $total, ?string $message) {
|
||||
echo "Progress: {$progress}/{$total} - {$message}\n";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
- **Sampling Support**: Handle server LLM sampling requests
|
||||
```php
|
||||
$samplingHandler = new SamplingRequestHandler($myCallback);
|
||||
$client = Client::builder()
|
||||
->setCapabilities(new ClientCapabilities(sampling: true))
|
||||
->addRequestHandler($samplingHandler)
|
||||
->build();
|
||||
```
|
||||
|
||||
- **Logging Notifications**: Receive server log messages
|
||||
```php
|
||||
$loggingHandler = new LoggingNotificationHandler($myCallback);
|
||||
$client = Client::builder()
|
||||
->addNotificationHandler($loggingHandler)
|
||||
->build();
|
||||
```
|
||||
|
||||
### Transports
|
||||
|
||||
Connect to MCP servers using the transport that matches your setup:
|
||||
|
||||
**1. STDIO Transport** — Connect to local server processes:
|
||||
```php
|
||||
$transport = new StdioTransport(
|
||||
command: 'php',
|
||||
args: ['/path/to/server.php'],
|
||||
);
|
||||
|
||||
$client->connect($transport);
|
||||
```
|
||||
|
||||
**2. HTTP Transport** — Connect to remote or web-based servers:
|
||||
```php
|
||||
$transport = new HttpTransport('http://localhost:8000');
|
||||
|
||||
$client->connect($transport);
|
||||
```
|
||||
|
||||
[→ Client Documentation](docs/client.md)
|
||||
|
||||
## Documentation
|
||||
|
||||
**Core Concepts:**
|
||||
- [Server Builder](docs/server-builder.md) - Complete ServerBuilder reference and configuration
|
||||
- [Transports](docs/transports.md) - STDIO and HTTP transport setup and usage
|
||||
- [MCP Elements](docs/mcp-elements.md) - Creating tools, resources, and prompts
|
||||
- [Client Communication](docs/client-communication.md) - Communicating back to the client from server-side
|
||||
- [Events](docs/events.md) - Hooking into server lifecycle with events
|
||||
### Core Concepts
|
||||
|
||||
**Learning:**
|
||||
- [Examples](docs/examples.md) - Comprehensive example walkthroughs
|
||||
- **[Server Builder](docs/server-builder.md)** — Complete ServerBuilder reference and configuration
|
||||
- **[Client](docs/client.md)** — Client SDK for connecting to and communicating with MCP servers
|
||||
- **[Transports](docs/transports.md)** — STDIO and HTTP transport setup and usage
|
||||
- **[MCP Elements](docs/mcp-elements.md)** — Creating tools, resources, prompts, and templates
|
||||
- **[Server-Client Communication](docs/server-client-communication.md)** — Sampling, logging, progress, and notifications
|
||||
- **[Events](docs/events.md)** — Hooking into server lifecycle with events
|
||||
|
||||
**External Resources:**
|
||||
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
|
||||
- [Model Context Protocol specification](https://spec.modelcontextprotocol.io)
|
||||
- [Officially supported servers](https://github.com/modelcontextprotocol/servers)
|
||||
### Learning & Examples
|
||||
|
||||
- **[Examples](docs/examples.md)** — Comprehensive example walkthroughs for servers and clients
|
||||
- **[ROADMAP.md](ROADMAP.md)** — Planned features and development roadmap
|
||||
|
||||
## External Resources
|
||||
|
||||
- **[Model Context Protocol Documentation](https://modelcontextprotocol.io)** — Official MCP documentation
|
||||
- **[Model Context Protocol Specification](https://spec.modelcontextprotocol.io)** — Protocol specification
|
||||
- **[Officially Supported Servers](https://github.com/modelcontextprotocol/servers)** — Reference server implementations
|
||||
|
||||
## PHP Libraries Using the MCP SDK
|
||||
|
||||
* [pronskiy/mcp](https://github.com/pronskiy/mcp) - Additional DX layer
|
||||
* [symfony/mcp-bundle](https://github.com/symfony/mcp-bundle) - Symfony integration bundle
|
||||
* [josbeir/cakephp-synapse](https://github.com/josbeir/cakephp-synapse) - CakePHP integration plugin
|
||||
- [pronskiy/mcp](https://github.com/pronskiy/mcp) — Additional developer experience layer
|
||||
- [symfony/mcp-bundle](https://github.com/symfony/mcp-bundle) — Symfony integration bundle
|
||||
- [josbeir/cakephp-synapse](https://github.com/josbeir/cakephp-synapse) — CakePHP integration plugin
|
||||
|
||||
## Contributing
|
||||
|
||||
We are passionate about supporting contributors of all levels of experience and would love to see you get involved in
|
||||
the project. See the [contributing guide](CONTRIBUTING.md) to get started before you [report issues](https://github.com/modelcontextprotocol/php-sdk/issues) and [send pull requests](https://github.com/modelcontextprotocol/php-sdk/pulls).
|
||||
We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project.
|
||||
|
||||
See the [Contributing Guide](CONTRIBUTING.md) to get started before you [report issues](https://github.com/modelcontextprotocol/php-sdk/issues) and [send pull requests](https://github.com/modelcontextprotocol/php-sdk/pulls).
|
||||
|
||||
## Credits
|
||||
|
||||
The starting point for this SDK was the [PHP-MCP](https://github.com/php-mcp/server) project, initiated by
|
||||
[Kyrian Obikwelu](https://github.com/CodeWithKyrian), and the [Symfony AI initiative](https://github.com/symfony/ai). We are grateful for the work
|
||||
done by both projects and their contributors, which created a solid foundation for this SDK.
|
||||
The starting point for this SDK was the [PHP-MCP](https://github.com/php-mcp/server) project, initiated by [Kyrian Obikwelu](https://github.com/CodeWithKyrian), and the [Symfony AI initiative](https://github.com/symfony/ai). We are grateful for the work done by both projects and their contributors, which created a solid foundation for this SDK.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License, Version 2.0 for new contributions, with existing code under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
This project is licensed under the Apache License, Version 2.0 for new contributions, with existing code under the MIT License — see the [LICENSE](LICENSE) file for details.
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ This roadmap is a living document that outlines the planned features and improve
|
|||
- [ ] Implement full support for elicitations
|
||||
- [ ] Implement OAuth2 authentication for server
|
||||
- **Client**
|
||||
- [ ] Implement client-side support
|
||||
- [ ] Implement client examples and documentation
|
||||
- [x] Implement client-side support
|
||||
- [x] Implement client examples and documentation
|
||||
- **Schema**
|
||||
- [ ] Implement schema generation based on TS or JSON Schema
|
||||
|
||||
|
|
|
|||
691
docs/client.md
Normal file
691
docs/client.md
Normal file
|
|
@ -0,0 +1,691 @@
|
|||
# Client
|
||||
|
||||
The MCP Client SDK provides a synchronous, framework-agnostic API for communicating with MCP servers from PHP applications.
|
||||
It handles connection management, request/response correlation, server-initiated requests (sampling), and real-time notifications.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Client Builder](#client-builder)
|
||||
- [Transports](#transports)
|
||||
- [Connecting to Servers](#connecting-to-servers)
|
||||
- [Server Information](#server-information)
|
||||
- [Working with Tools](#working-with-tools)
|
||||
- [Working with Resources](#working-with-resources)
|
||||
- [Working with Prompts](#working-with-prompts)
|
||||
- [Server-Initiated Communication](#server-initiated-communication)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Complete Example](#complete-example)
|
||||
|
||||
## Overview
|
||||
|
||||
The client follows a builder pattern for configuration and provides a synchronous API for all operations:
|
||||
|
||||
```php
|
||||
use Mcp\Client;
|
||||
use Mcp\Client\Transport\StdioTransport;
|
||||
|
||||
// Build and configure the client
|
||||
$client = Client::builder()
|
||||
->setClientInfo('My Client', '1.0.0')
|
||||
->setInitTimeout(30)
|
||||
->setRequestTimeout(120)
|
||||
->build();
|
||||
|
||||
// Create a transport
|
||||
$transport = new StdioTransport(
|
||||
command: 'php',
|
||||
args: ['/path/to/server.php'],
|
||||
);
|
||||
|
||||
// Connect and use the server
|
||||
$client->connect($transport);
|
||||
$tools = $client->listTools();
|
||||
$client->disconnect();
|
||||
```
|
||||
|
||||
## 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) // Retry attempts for failed connections
|
||||
->build();
|
||||
```
|
||||
|
||||
### 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 (defaults to latest):
|
||||
|
||||
```php
|
||||
use Mcp\Schema\Enum\ProtocolVersion;
|
||||
|
||||
$client = Client::builder()
|
||||
->setProtocolVersion(ProtocolVersion::V2025_06_18)
|
||||
->build();
|
||||
```
|
||||
|
||||
### 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):
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
## Transports
|
||||
|
||||
Transports handle the communication layer between client and server.
|
||||
|
||||
### STDIO Transport
|
||||
|
||||
Spawns a server process and communicates via standard input/output:
|
||||
|
||||
```php
|
||||
use Mcp\Client\Transport\StdioTransport;
|
||||
|
||||
$transport = new StdioTransport(
|
||||
command: 'php',
|
||||
args: ['/path/to/server.php'],
|
||||
cwd: '/working/directory', // Optional working directory
|
||||
env: ['KEY' => 'value'], // Optional environment variables
|
||||
);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `command` (string): The command to execute
|
||||
- `args` (array): Command arguments
|
||||
- `cwd` (string|null): Working directory for the process
|
||||
- `env` (array|null): Environment variables
|
||||
- `logger` (LoggerInterface|null): Optional PSR-3 logger
|
||||
|
||||
### HTTP Transport
|
||||
|
||||
Communicates with remote MCP servers over HTTP:
|
||||
|
||||
```php
|
||||
use Mcp\Client\Transport\HttpTransport;
|
||||
|
||||
$transport = new HttpTransport(
|
||||
endpoint: 'http://localhost:8000',
|
||||
headers: ['Authorization' => 'Bearer token'],
|
||||
);
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `endpoint` (string): The MCP server URL
|
||||
- `headers` (array): Additional HTTP headers
|
||||
- `httpClient` (ClientInterface|null): PSR-18 HTTP client (auto-discovered)
|
||||
- `requestFactory` (RequestFactoryInterface|null): PSR-17 request factory (auto-discovered)
|
||||
- `streamFactory` (StreamFactoryInterface|null): PSR-17 stream factory (auto-discovered)
|
||||
- `logger` (LoggerInterface|null): Optional PSR-3 logger
|
||||
|
||||
**PSR-18 Auto-Discovery:**
|
||||
|
||||
The transport automatically discovers PSR-18 HTTP clients from:
|
||||
- `php-http/guzzle7-adapter`
|
||||
- `php-http/curl-client`
|
||||
- `symfony/http-client`
|
||||
- And other PSR-18 compatible implementations
|
||||
|
||||
```bash
|
||||
# Install any PSR-18 client - discovery works automatically
|
||||
composer require php-http/guzzle7-adapter
|
||||
```
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 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";
|
||||
}
|
||||
```
|
||||
|
||||
## Working with Tools
|
||||
|
||||
### Listing Tools
|
||||
|
||||
```php
|
||||
$toolsResult = $client->listTools();
|
||||
|
||||
foreach ($toolsResult->tools as $tool) {
|
||||
echo "- {$tool->name}: {$tool->description}\n";
|
||||
}
|
||||
|
||||
// Handle pagination
|
||||
if ($toolsResult->nextCursor) {
|
||||
$moreTools = $client->listTools($toolsResult->nextCursor);
|
||||
}
|
||||
```
|
||||
|
||||
### Calling Tools
|
||||
|
||||
```php
|
||||
$result = $client->callTool(
|
||||
name: 'calculate',
|
||||
arguments: ['a' => 5, 'b' => 3, 'operation' => 'add'],
|
||||
);
|
||||
|
||||
// Access results
|
||||
foreach ($result->content as $content) {
|
||||
if ($content instanceof TextContent) {
|
||||
echo $content->text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Notifications
|
||||
|
||||
Hook into tool execution progress (if server supports it):
|
||||
|
||||
```php
|
||||
$result = $client->callTool(
|
||||
name: 'long_running_task',
|
||||
arguments: ['data' => 'large_dataset'],
|
||||
onProgress: static function (float $progress, ?float $total, ?string $message) {
|
||||
$percent = $total > 0 ? round(($progress / $total) * 100) : 0;
|
||||
echo "Progress: {$percent}% - {$message}\n";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Progress notifications are only received if the server sends them. The callback will not be invoked if the server doesn't support or send progress updates.
|
||||
|
||||
## Working with Resources
|
||||
|
||||
### Listing Resources
|
||||
|
||||
```php
|
||||
$resourcesResult = $client->listResources();
|
||||
|
||||
foreach ($resourcesResult->resources as $resource) {
|
||||
echo "- {$resource->uri}: {$resource->name}\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Listing Resource Templates
|
||||
|
||||
```php
|
||||
$templatesResult = $client->listResourceTemplates();
|
||||
|
||||
foreach ($templatesResult->resourceTemplates as $template) {
|
||||
echo "- {$template->uriTemplate}: {$template->name}\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Reading Resources
|
||||
|
||||
```php
|
||||
$resourceResult = $client->readResource('config://app/settings');
|
||||
|
||||
foreach ($resourceResult->contents as $content) {
|
||||
if ($content instanceof TextResourceContents) {
|
||||
echo "Text: {$content->text}\n";
|
||||
} elseif ($content instanceof BlobResourceContents) {
|
||||
echo "Binary data (base64): {$content->blob}\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Resources also support progress notifications:
|
||||
|
||||
```php
|
||||
$result = $client->readResource(
|
||||
uri: 'file://large-file.bin',
|
||||
onProgress: static function (float $progress, ?float $total, ?string $message) {
|
||||
echo "Reading: {$progress}/{$total} bytes\n";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Working with Prompts
|
||||
|
||||
### Listing Prompts
|
||||
|
||||
```php
|
||||
$promptsResult = $client->listPrompts();
|
||||
|
||||
foreach ($promptsResult->prompts as $prompt) {
|
||||
echo "- {$prompt->name}: {$prompt->description}\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Getting Prompts
|
||||
|
||||
```php
|
||||
$promptResult = $client->getPrompt(
|
||||
name: 'code_review',
|
||||
arguments: ['language' => 'php', 'code' => '...'],
|
||||
);
|
||||
|
||||
foreach ($promptResult->messages as $message) {
|
||||
echo "{$message->role->value}: {$message->content->text}\n";
|
||||
}
|
||||
```
|
||||
|
||||
Prompts also support progress notifications:
|
||||
|
||||
```php
|
||||
$result = $client->getPrompt(
|
||||
name: 'generate_report',
|
||||
arguments: ['topic' => 'quarterly_analysis'],
|
||||
onProgress: static function (float $progress, ?float $total, ?string $message) {
|
||||
echo "Generating: {$message}\n";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Requesting Completions
|
||||
|
||||
Request auto-completion suggestions for prompt or resource arguments:
|
||||
|
||||
```php
|
||||
use Mcp\Schema\PromptReference;
|
||||
|
||||
$completionResult = $client->complete(
|
||||
ref: new PromptReference('code_review'),
|
||||
argument: ['name' => 'language', 'value' => 'ph'],
|
||||
);
|
||||
|
||||
foreach ($completionResult->values as $value) {
|
||||
echo "Suggestion: {$value}\n";
|
||||
}
|
||||
```
|
||||
|
||||
## Server-Initiated Communication
|
||||
|
||||
The client can receive requests and notifications from the server when configured with appropriate handlers.
|
||||
|
||||
### Logging Notifications
|
||||
|
||||
Receive structured log messages from the server:
|
||||
|
||||
```php
|
||||
use Mcp\Client\Handler\Notification\LoggingNotificationHandler;
|
||||
use Mcp\Schema\Notification\LoggingMessageNotification;
|
||||
use Mcp\Schema\Enum\LoggingLevel;
|
||||
|
||||
$loggingHandler = new LoggingNotificationHandler(
|
||||
static function (LoggingMessageNotification $notification) {
|
||||
// Route to your application's logging system
|
||||
$level = $notification->level;
|
||||
$message = $notification->data;
|
||||
|
||||
match ($level) {
|
||||
LoggingLevel::Debug => logger()->debug($message),
|
||||
LoggingLevel::Info => logger()->info($message),
|
||||
LoggingLevel::Warning => logger()->warning($message),
|
||||
LoggingLevel::Error => logger()->error($message),
|
||||
default => logger()->info($message),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
$client = Client::builder()
|
||||
->addNotificationHandler($loggingHandler)
|
||||
->build();
|
||||
|
||||
// Set minimum log level (optional)
|
||||
$client->setLoggingLevel(LoggingLevel::Info);
|
||||
```
|
||||
|
||||
### Sampling (LLM Requests)
|
||||
|
||||
Handle server requests for LLM completions:
|
||||
|
||||
```php
|
||||
use Mcp\Client\Handler\Request\SamplingRequestHandler;
|
||||
use Mcp\Client\Handler\Request\SamplingCallbackInterface;
|
||||
use Mcp\Exception\SamplingException;
|
||||
use Mcp\Schema\ClientCapabilities;
|
||||
use Mcp\Schema\Request\CreateSamplingMessageRequest;
|
||||
use Mcp\Schema\Result\CreateSamplingMessageResult;
|
||||
use Mcp\Schema\Content\TextContent;
|
||||
use Mcp\Schema\Enum\Role;
|
||||
|
||||
class LlmSamplingCallback implements SamplingCallbackInterface
|
||||
{
|
||||
public function __invoke(CreateSamplingMessageRequest $request): CreateSamplingMessageResult
|
||||
{
|
||||
try {
|
||||
// Call your LLM provider
|
||||
$response = $this->llmClient->complete(
|
||||
messages: $request->messages,
|
||||
maxTokens: $request->maxTokens,
|
||||
temperature: $request->temperature ?? 0.7,
|
||||
);
|
||||
|
||||
return new CreateSamplingMessageResult(
|
||||
role: Role::Assistant,
|
||||
content: new TextContent($response->text),
|
||||
model: $response->model,
|
||||
stopReason: $response->stopReason,
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
// Throw SamplingException to surface error to server
|
||||
throw new SamplingException(
|
||||
"LLM sampling failed: {$e->getMessage()}",
|
||||
(int) $e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$client = Client::builder()
|
||||
->setCapabilities(new ClientCapabilities(sampling: true))
|
||||
->addRequestHandler(new SamplingRequestHandler(new LlmSamplingCallback))
|
||||
->build();
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Error Handling in Sampling Callbacks:**
|
||||
>
|
||||
> When implementing sampling callbacks, error handling is critical:
|
||||
>
|
||||
> - **Throw `SamplingException`** to forward specific error messages to the server
|
||||
> - **Any other exception** will be logged but return a generic error to the server
|
||||
>
|
||||
> This distinction allows you to control what error information the server receives:
|
||||
>
|
||||
> ```php
|
||||
> // Good: Server receives "Rate limit exceeded" message
|
||||
> throw new SamplingException('Rate limit exceeded. Retry after 60 seconds.');
|
||||
>
|
||||
> // Bad: Server receives generic "Error while sampling LLM" message
|
||||
> throw new \RuntimeException('Rate limit exceeded');
|
||||
> ```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The client throws exceptions for various error conditions:
|
||||
|
||||
### ConnectionException
|
||||
|
||||
Thrown when connection or initialization fails:
|
||||
|
||||
```php
|
||||
use Mcp\Exception\ConnectionException;
|
||||
|
||||
try {
|
||||
$client->connect($transport);
|
||||
} catch (ConnectionException $e) {
|
||||
echo "Failed to connect: {$e->getMessage()}\n";
|
||||
}
|
||||
```
|
||||
|
||||
### RequestException
|
||||
|
||||
Thrown when a request returns an error response:
|
||||
|
||||
```php
|
||||
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";
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
Here's a comprehensive example demonstrating client usage:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Mcp\Client;
|
||||
use Mcp\Client\Handler\Notification\LoggingNotificationHandler;
|
||||
use Mcp\Client\Handler\Request\SamplingCallbackInterface;
|
||||
use Mcp\Client\Handler\Request\SamplingRequestHandler;
|
||||
use Mcp\Client\Transport\StdioTransport;
|
||||
use Mcp\Exception\SamplingException;
|
||||
use Mcp\Schema\ClientCapabilities;
|
||||
use Mcp\Schema\Content\TextContent;
|
||||
use Mcp\Schema\Enum\LoggingLevel;
|
||||
use Mcp\Schema\Enum\Role;
|
||||
use Mcp\Schema\Notification\LoggingMessageNotification;
|
||||
use Mcp\Schema\Request\CreateSamplingMessageRequest;
|
||||
use Mcp\Schema\Result\CreateSamplingMessageResult;
|
||||
|
||||
// Configure logging notification handler
|
||||
$loggingHandler = new LoggingNotificationHandler(
|
||||
static function (LoggingMessageNotification $notification) {
|
||||
echo "[LOG {$notification->level->value}] {$notification->data}\n";
|
||||
}
|
||||
);
|
||||
|
||||
// Configure sampling callback
|
||||
$samplingCallback = new class implements SamplingCallbackInterface {
|
||||
public function __invoke(CreateSamplingMessageRequest $request): CreateSamplingMessageResult
|
||||
{
|
||||
echo "[SAMPLING] Processing request (max {$request->maxTokens} tokens)\n";
|
||||
|
||||
try {
|
||||
// Integration with your LLM provider
|
||||
$response = "This is a mock LLM response for: " .
|
||||
json_encode($request->messages);
|
||||
|
||||
return new CreateSamplingMessageResult(
|
||||
role: Role::Assistant,
|
||||
content: new TextContent($response),
|
||||
model: 'mock-llm',
|
||||
stopReason: 'end_turn',
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
throw new SamplingException(
|
||||
"Sampling failed: {$e->getMessage()}",
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Build client
|
||||
$client = Client::builder()
|
||||
->setClientInfo('Example Client', '1.0.0')
|
||||
->setInitTimeout(30)
|
||||
->setRequestTimeout(120)
|
||||
->setCapabilities(new ClientCapabilities(sampling: true))
|
||||
->addNotificationHandler($loggingHandler)
|
||||
->addRequestHandler(new SamplingRequestHandler($samplingCallback))
|
||||
->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";
|
||||
}
|
||||
|
||||
// Set logging level
|
||||
$client->setLoggingLevel(LoggingLevel::Debug);
|
||||
|
||||
// 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";
|
||||
}
|
||||
```
|
||||
151
docs/examples.md
151
docs/examples.md
|
|
@ -7,7 +7,8 @@ specific features and can be run independently to understand how the SDK works.
|
|||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Running Examples](#running-examples)
|
||||
- [Examples](#examples)
|
||||
- [Server Examples](#server-examples)
|
||||
- [Client Examples](#client-examples)
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ curl -X POST http://localhost:8000 \
|
|||
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1.0.0"},"capabilities":{}}}'
|
||||
```
|
||||
|
||||
## Examples
|
||||
## Server Examples
|
||||
|
||||
### Discovery Calculator
|
||||
|
||||
|
|
@ -353,3 +354,149 @@ npx @modelcontextprotocol/inspector php examples/server/elicitation/server.php
|
|||
1. **book_restaurant** - Multi-field reservation form with number, date, and enum fields
|
||||
2. **confirm_action** - Simple boolean confirmation dialog
|
||||
3. **collect_feedback** - Rating and comments form with optional fields
|
||||
|
||||
## Client Examples
|
||||
|
||||
### STDIO Discovery Calculator (Client)
|
||||
|
||||
**File**: `examples/client/stdio_discovery_calculator.php`
|
||||
|
||||
**What it demonstrates:**
|
||||
- Basic MCP client usage with STDIO transport
|
||||
- Connecting to a local MCP server process
|
||||
- Listing and calling tools
|
||||
- Reading resources
|
||||
|
||||
**Key Features:**
|
||||
```php
|
||||
$client = Client::builder()
|
||||
->setClientInfo('STDIO Example Client', '1.0.0')
|
||||
->setInitTimeout(30)
|
||||
->setRequestTimeout(60)
|
||||
->build();
|
||||
|
||||
$transport = new StdioTransport(
|
||||
command: 'php',
|
||||
args: [__DIR__.'/../server/discovery-calculator/server.php'],
|
||||
);
|
||||
|
||||
$client->connect($transport);
|
||||
$tools = $client->listTools();
|
||||
$result = $client->callTool('calculate', ['a' => 5, 'b' => 3, 'operation' => 'add']);
|
||||
$resourceContent = $client->readResource('config://calculator/settings');
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Run the client (automatically starts the server)
|
||||
php examples/client/stdio_discovery_calculator.php
|
||||
```
|
||||
|
||||
### HTTP Discovery Calculator (Client)
|
||||
|
||||
**File**: `examples/client/http_discovery_calculator.php`
|
||||
|
||||
**What it demonstrates:**
|
||||
- MCP client with HTTP transport
|
||||
- Connecting to remote MCP servers
|
||||
- Listing tools, resources, and prompts
|
||||
|
||||
**Key Features:**
|
||||
```php
|
||||
$transport = new HttpTransport('http://localhost:8000');
|
||||
$client->connect($transport);
|
||||
|
||||
$tools = $client->listTools();
|
||||
$resources = $client->listResources();
|
||||
$prompts = $client->listPrompts();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Start the server first
|
||||
php -S localhost:8000 examples/server/http-discovery-calculator/server.php
|
||||
|
||||
# Then run the client
|
||||
php examples/client/http_discovery_calculator.php
|
||||
```
|
||||
|
||||
### STDIO Client Communication
|
||||
|
||||
**File**: `examples/client/stdio_client_communication.php`
|
||||
|
||||
**What it demonstrates:**
|
||||
- Server-to-client communication (logging, progress, sampling)
|
||||
- Handling logging notifications from server
|
||||
- Implementing sampling callbacks for LLM requests
|
||||
- Progress tracking during tool execution
|
||||
|
||||
**Key Features:**
|
||||
```php
|
||||
use Mcp\Client\Handler\Notification\LoggingNotificationHandler;
|
||||
use Mcp\Client\Handler\Request\SamplingRequestHandler;
|
||||
use Mcp\Client\Handler\Request\SamplingCallbackInterface;
|
||||
use Mcp\Schema\ClientCapabilities;
|
||||
|
||||
$loggingHandler = new LoggingNotificationHandler(
|
||||
static function (LoggingMessageNotification $n) {
|
||||
echo "[LOG {$n->level->value}] {$n->data}\n";
|
||||
}
|
||||
);
|
||||
|
||||
$samplingHandler = new SamplingRequestHandler(new class implements SamplingCallbackInterface {
|
||||
public function __invoke(CreateSamplingMessageRequest $request): CreateSamplingMessageResult
|
||||
{
|
||||
// Perform LLM sampling and return result
|
||||
}
|
||||
});
|
||||
|
||||
$client = Client::builder()
|
||||
->setCapabilities(new ClientCapabilities(sampling: true))
|
||||
->addNotificationHandler($loggingHandler)
|
||||
->addRequestHandler($samplingHandler)
|
||||
->build();
|
||||
|
||||
// Call tool with progress tracking
|
||||
$result = $client->callTool(
|
||||
name: 'run_dataset_quality_checks',
|
||||
arguments: ['dataset' => 'customer_orders_2024'],
|
||||
onProgress: static function (float $progress, ?float $total, ?string $message) {
|
||||
$percent = $total > 0 ? round(($progress / $total) * 100) : '?';
|
||||
echo "[PROGRESS {$percent}%] {$message}\n";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Run the client (automatically starts the communication server)
|
||||
php examples/client/stdio_client_communication.php
|
||||
```
|
||||
|
||||
### HTTP Client Communication
|
||||
|
||||
**File**: `examples/client/http_client_communication.php`
|
||||
|
||||
**What it demonstrates:**
|
||||
- Server-to-client communication over HTTP
|
||||
- Receiving logging and progress notifications via SSE streaming
|
||||
- Implementing sampling for HTTP-based servers
|
||||
- Progress tracking with long-running operations
|
||||
|
||||
**Key Features:**
|
||||
- Same client-side code as STDIO version
|
||||
- Uses HttpTransport instead of StdioTransport
|
||||
- Demonstrates SSE-based real-time notifications
|
||||
- Shows HTTP session management
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Start the server
|
||||
php -S 127.0.0.1:8000 examples/server/client-communication/server.php
|
||||
|
||||
# Run the client
|
||||
php examples/client/http_client_communication.php
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> For sampling with HTTP transport, the server must support concurrent request processing (e.g., using Symfony CLI, PHP-FPM, or a production web server). PHP's built-in development server cannot handle the concurrent requests required for sampling.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- [MCP Elements](mcp-elements.md) — Core capabilities (Tools, Resources, Resource Templates, and Prompts) with registration methods.
|
||||
- [Server Builder](server-builder.md) — Fluent builder class for creating and configuring MCP server instances.
|
||||
- [Client](client.md) — Client SDK for connecting to and communicating with MCP servers.
|
||||
- [Transports](transports.md) — STDIO and HTTP transport implementations with guidance on choosing between them.
|
||||
- [Server-Client Communication](server-client-communication.md) — Methods for servers to communicate back to clients: sampling, logging, progress, and notifications.
|
||||
- [Examples](examples.md) — Example projects demonstrating attribute-based discovery, dependency injection, HTTP transport, and more.
|
||||
|
|
|
|||
Loading…
Reference in a new issue