Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,26 @@
"psr/clock": "^1.0",
"psr/container": "^2.0",
"psr/event-dispatcher": "^1.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.1",
"psr/http-message": "^2.0",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/finder": "^6.4 || ^7.3 || ^8.0",
"symfony/uid": "^6.4 || ^7.3 || ^8.0"
},
"require-dev": {
"laminas/laminas-httphandlerrunner": "^2.12",
"nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.1",
"php-cs-fixer/shim": "^3.91",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^10.5",
"psr/cache": "^3.0",
"psr/simple-cache": "^3.0",
"symfony/cache": "^6.4 || ^7.3 || ^8.0",
"symfony/console": "^6.4 || ^7.3 || ^8.0",
"symfony/process": "^6.4 || ^7.3 || ^8.0",
"nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.1",
"laminas/laminas-httphandlerrunner": "^2.12"
"symfony/http-client": "^7.4",
"symfony/process": "^6.4 || ^7.3 || ^8.0"
},
"autoload": {
"psr-4": {
Expand All @@ -52,18 +54,18 @@
},
"autoload-dev": {
"psr-4": {
"Mcp\\Example\\CachedDiscovery\\": "examples/cached-discovery/",
"Mcp\\Example\\ClientCommunication\\": "examples/client-communication/",
"Mcp\\Example\\CombinedRegistration\\": "examples/combined-registration/",
"Mcp\\Example\\ComplexToolSchema\\": "examples/complex-tool-schema/",
"Mcp\\Example\\CustomDependencies\\": "examples/custom-dependencies/",
"Mcp\\Example\\CustomMethodHandlers\\": "examples/custom-method-handlers/",
"Mcp\\Example\\DiscoveryCalculator\\": "examples/discovery-calculator/",
"Mcp\\Example\\DiscoveryUserProfile\\": "examples/discovery-userprofile/",
"Mcp\\Example\\EnvVariables\\": "examples/env-variables/",
"Mcp\\Example\\ExplicitRegistration\\": "examples/explicit-registration/",
"Mcp\\Example\\SchemaShowcase\\": "examples/schema-showcase/",
"Mcp\\Example\\ClientLogging\\": "examples/client-logging/",
"Mcp\\Example\\Server\\CachedDiscovery\\": "examples/server/cached-discovery/",
"Mcp\\Example\\Server\\ClientCommunication\\": "examples/server/client-communication/",
"Mcp\\Example\\Server\\CombinedRegistration\\": "examples/server/combined-registration/",
"Mcp\\Example\\Server\\ComplexToolSchema\\": "examples/server/complex-tool-schema/",
"Mcp\\Example\\Server\\CustomDependencies\\": "examples/server/custom-dependencies/",
"Mcp\\Example\\Server\\CustomMethodHandlers\\": "examples/server/custom-method-handlers/",
"Mcp\\Example\\Server\\DiscoveryCalculator\\": "examples/server/discovery-calculator/",
"Mcp\\Example\\Server\\DiscoveryUserProfile\\": "examples/server/discovery-userprofile/",
"Mcp\\Example\\Server\\EnvVariables\\": "examples/server/env-variables/",
"Mcp\\Example\\Server\\ExplicitRegistration\\": "examples/server/explicit-registration/",
"Mcp\\Example\\Server\\SchemaShowcase\\": "examples/server/schema-showcase/",
"Mcp\\Example\\Server\\ClientLogging\\": "examples/server/client-logging/",
"Mcp\\Tests\\": "tests/"
}
},
Expand All @@ -73,4 +75,4 @@
"php-http/discovery": false
}
}
}
}
27 changes: 27 additions & 0 deletions examples/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Client Examples

These examples demonstrate how to use the MCP PHP Client SDK.

## STDIO Client

Connects to an MCP server running as a child process:

```bash
php examples/client/stdio_discovery_calculator.php
```

## HTTP Client

Connects to an MCP server over HTTP:

```bash
# First, start an HTTP server
php -S localhost:8080 examples/http-discovery-userprofile/server.php

# Then run the client
php examples/client/http_discovery_userprofile.php
```

## Requirements

All examples require the server examples to be available. The STDIO examples spawn the server process, while the HTTP examples connect to a running HTTP server.
122 changes: 122 additions & 0 deletions examples/client/http_client_communication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

/**
* HTTP Client Communication Example
*
* This example demonstrates server-to-client communication over HTTP:
* - Logging notifications
* - Progress notifications (via SSE streaming)
* - Sampling requests (mocked LLM response)
*
* Usage:
* 1. Start the server: php -S 127.0.0.1:8000 examples/server/client-communication/server.php
* 2. Run this script: php examples/client/http_client_communication.php
*
* Note: PHP's built-in server works for listing tools, calling tools, and receiving
* progress/logging notifications. However, sampling requires a concurrent-capable server
* (e.g., Symfony CLI, PHP-FPM) since the server must process the client's sampling
* response while the original tool request is still pending.
*
* Eg. symfony serve --passthru=examples/server/client-communication/server.php --no-tls
*/

declare(strict_types=1);

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

use Mcp\Client\Client;
use Mcp\Client\Handler\LoggingNotificationHandler;
use Mcp\Client\Handler\SamplingRequestHandler;
use Mcp\Client\Transport\HttpClientTransport;
use Mcp\Schema\ClientCapabilities;
use Mcp\Schema\Content\TextContent;
use Mcp\Schema\Enum\Role;
use Mcp\Schema\Notification\LoggingMessageNotification;
use Mcp\Schema\Request\CreateSamplingMessageRequest;
use Mcp\Schema\Result\CreateSamplingMessageResult;

$endpoint = 'http://127.0.0.1:8000';

$loggingNotificationHandler = new LoggingNotificationHandler(function (LoggingMessageNotification $n) {
echo "[LOG {$n->level->value}] {$n->data}\n";
});

$samplingRequestHandler = new SamplingRequestHandler(function (CreateSamplingMessageRequest $request): CreateSamplingMessageResult {
echo "[SAMPLING] Server requested LLM sampling (max {$request->maxTokens} tokens)\n";

$mockResponse = "Based on the incident analysis, I recommend: 1) Activate the on-call team, " .
"2) Isolate affected systems, 3) Begin root cause analysis, 4) Prepare stakeholder communication.";

return new CreateSamplingMessageResult(
role: Role::Assistant,
content: new TextContent($mockResponse),
model: 'mock-gpt-4',
stopReason: 'end_turn',
);
});

$client = Client::builder()
->setClientInfo('HTTP Client Communication Test', '1.0.0')
->setInitTimeout(30)
->setRequestTimeout(120)
->setCapabilities(new ClientCapabilities(sampling: true))
->addNotificationHandler($loggingNotificationHandler)
->addRequestHandler($samplingRequestHandler)
->build();

$transport = new HttpClientTransport(endpoint: $endpoint);

try {
echo "Connecting to MCP server at {$endpoint}...\n";
$client->connect($transport);

$serverInfo = $client->getServerInfo();
echo "Connected to: " . ($serverInfo?->name ?? 'unknown') . "\n\n";

echo "Available tools:\n";
$toolsResult = $client->listTools();
foreach ($toolsResult->tools as $tool) {
echo " - {$tool->name}\n";
}
echo "\n";

echo "Calling 'run_dataset_quality_checks'...\n\n";
$result = $client->callTool(
name: 'run_dataset_quality_checks',
arguments: ['dataset' => 'sales_transactions_q4'],
onProgress: function (float $progress, ?float $total, ?string $message) {
$percent = $total > 0 ? round(($progress / $total) * 100) : '?';
echo "[PROGRESS {$percent}%] {$message}\n";
}
);

echo "\nResult:\n";
foreach ($result->content as $content) {
if ($content instanceof TextContent) {
echo $content->text . "\n";
}
}

echo "\nCalling 'coordinate_incident_response'...\n\n";
$result = $client->callTool(
name: 'coordinate_incident_response',
arguments: ['incidentTitle' => 'Database connection pool exhausted'],
onProgress: function (float $progress, ?float $total, ?string $message) {
$percent = $total > 0 ? round(($progress / $total) * 100) : '?';
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();
}
69 changes: 69 additions & 0 deletions examples/client/http_discovery_calculator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/**
* HTTP Client Example
*
* This example demonstrates how to use the MCP client with an HTTP transport
* to communicate with a remote MCP server over HTTP.
*
* Usage: php examples/client/http_discovery_calculator.php
*
* Before running, start an HTTP MCP server:
* php -S localhost:8080 examples/server/http-discovery-calculator/server.php
*/

declare(strict_types=1);

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

use Mcp\Client\Client;
use Mcp\Client\Transport\HttpClientTransport;

$endpoint = 'http://localhost:8000';

$client = Client::builder()
->setClientInfo('HTTP Example Client', '1.0.0')
->setInitTimeout(30)
->setRequestTimeout(60)
->build();

$transport = new HttpClientTransport($endpoint);

try {
echo "Connecting to MCP server at {$endpoint}...\n";
$client->connect($transport);

echo "Connected! Server info:\n";
$serverInfo = $client->getServerInfo();
echo " Name: " . ($serverInfo?->name ?? 'unknown') . "\n";
echo " Version: " . ($serverInfo?->version ?? 'unknown') . "\n\n";

echo "Available tools:\n";
$toolsResult = $client->listTools();
foreach ($toolsResult->tools as $tool) {
echo " - {$tool->name}: {$tool->description}\n";
}
echo "\n";

echo "Available resources:\n";
$resourcesResult = $client->listResources();
foreach ($resourcesResult->resources as $resource) {
echo " - {$resource->uri}: {$resource->name}\n";
}
echo "\n";

echo "Available prompts:\n";
$promptsResult = $client->listPrompts();
foreach ($promptsResult->prompts as $prompt) {
echo " - {$prompt->name}: {$prompt->description}\n";
}
echo "\n";

} catch (\Throwable $e) {
echo "Error: {$e->getMessage()}\n";
echo $e->getTraceAsString() . "\n";
} finally {
echo "Disconnecting...\n";
$client->disconnect();
echo "Done.\n";
}
Loading
Loading