PHP MCP Client是一个PHP库,用于与实现Model Context Protocol (MCP)的服务器进行交互。它为开发者提供了友好的接口,可通过不同的传输方式(stdio
、http+sse
)连接到单个MCP服务器,管理连接生命周期,发现服务器功能(工具、资源、提示),并执行诸如调用工具或读取资源等请求。
该库内部通过ReactPHP利用异步I/O来确保健壮性并处理服务器发送事件等功能,同时提供了简单的同步(阻塞)API用于常见用例,以及异步(基于Promise)API用于高级控制和并发操作。它遵循MCP规范模型,即一个客户端实例管理与一个服务器的有状态连接。
Model Context Protocol (MCP)是一个开放标准,旨在规范AI助手和应用程序连接到外部数据源、API和工具(如代码库、数据库、网页浏览器)的方式。它作为一个通信层,使AI模型(如Claude,或通过OpenAI等框架集成的模型)能够安全地访问不同服务器提供的上下文并与之交互。这个客户端库允许你的PHP应用程序(在MCP术语中称为“主机”)使用一个或多个MCP服务器提供的功能。
Client
实例管理与单个配置好的MCP服务器的有状态连接,符合MCP规范。Client::make()->with...()
构建器模式轻松设置每个客户端实例。->initialize()
或->initializeAsync()
连接并执行握手操作后才能发出请求。提供disconnect()
/ disconnectAsync()
方法断开连接。tools/call
、resources/read
、prompts/get
等请求。通过Composer安装该包:
composer require php-mcp/client
必要的ReactPHP依赖(event-loop
、promise
、stream
、child-process
、http
)会自动安装。
这个示例展示了如何连接到通过npx
运行的本地文件系统服务器。
<?php
require'vendor/autoload.php';
usePhpMcp\Client\Client;
usePhpMcp\Client\Enum\TransportType;
usePhpMcp\Client\Model\CapabilitiesasClientCapabilities;
usePhpMcp\Client\ServerConfig;
usePhpMcp\Client\Exception\McpClientException;
$clientCapabilities = ClientCapabilities::forClient(); // 默认客户端功能
$userHome = $_SERVER['HOME'] ?? $_SERVER['USERPROFILE'] ?? getcwd();
$fsServerConfig = new ServerConfig(
name: 'local_filesystem',
transport: TransportType::Stdio,
timeout: 15,
command: 'npx',
args: [
'-y',
'@modelcontextprotocol/server-filesystem',
$userHome . '/Documents',
],
workingDir: $userHome
);
$fsClient = Client::make()
->withClientInfo('MyFileSystemApp', '1.0')
->withCapabilities($clientCapabilities)
// ->withLogger(new MyPsrLogger()) // 可选
->withServerConfig($fsServerConfig)
->build();
try {
// 初始化连接(阻塞)
$fsClient->initialize();
// 同步交互
$tools = $fsClient->listTools(); // 阻塞调用
foreach ($tools as $tool) {
echo"- Tool: {$tool->name}\n";
}
//... 调用其他方法,如 $fsClient->callTool(...)...
} catch (McpClientException $e) {
echo"[MCP ERROR] ". get_class($e). ": ". $e->getMessage(). "\n";
// 检查 $e->getPrevious() 获取底层传输/进程错误
} catch (\Throwable $e) {
echo"[UNEXPECTED ERROR] ". $e->getMessage(). "\n";
} finally {
// 断开连接(阻塞)
if (isset($fsClient)) {
$fsClient->disconnect();
}
}
配置包括以下几个方面:
ClientCapabilities
声明客户端支持的功能。ServerConfig
配置客户端实例要连接的单个服务器的详细信息。用于声明客户端支持的功能,使用静态工厂方法。
use PhpMcp\Client\Model\Capabilities as ClientCapabilities;
// 客户端支持从服务器采样请求
$clientCapabilities = ClientCapabilities::forClient(supportsSampling: true);
// 客户端不支持从服务器采样请求
$clientCapabilities = ClientCapabilities::forClient(supportsSampling: false);
// 待办事项:如果需要,添加对声明“roots”功能的支持
定义如何连接到单个MCP服务器。
use PhpMcp\Client\Enum\TransportType;
usePhpMcp\Client\ServerConfig;
// 示例:Stdio服务器
$stdioConfig = new ServerConfig(
name: 'local_file_server', // 必需:此配置的唯一ID
transport: TransportType::Stdio, // 必需:传输类型
timeout: 15.0, // 可选:请求超时时间(秒)
command: 'npx', // Stdio必需:可执行文件
args: [ // Stdio可选:参数数组
'-y',
'@modelcontextprotocol/server-filesystem',
'/path/to/project'
],
workingDir: '/path/to/project', // Stdio可选:工作目录
env: ['DEBUG' =>'mcp*'] // Stdio可选:环境变量
);
// 示例:HTTP服务器
$httpConfig = new ServerConfig(
name:'remote_web_agent', // 必需:唯一ID
transport: TransportType::Http, // 必需:传输类型
timeout: 45.0, // 可选:请求超时时间
url: 'http://localhost:8080/sse',// Http必需:SSE URL
headers: [ // Http可选:认证/自定义头
'Authorization' => 'Bearer xyz789'
],
);
可以轻松解析存储在数组中的配置(例如从JSON文件或框架配置中获取)。
use PhpMcp\Client\ServerConfig;
usePhpMcp\Client\Exception\ConfigurationException;
$jsonConfig = '{
"mcpServers": {
"stdio_files": {
"command": "php",
"args": ["/app/mcp/file_server.php"],
"timeout": 10
},
"http_api": {
"url": "https://api.example.com/mcp/sse",
"transport": "http",
"headers": {"X-API-Key": "secret"}
}
}
}';
$decodedConfig = json_decode($jsonConfig, true)['mcpServers']?? [];
$serverConfigs = [];
foreach ($decodedConfig as $name => $data) {
try {
$serverConfigs[$name] = ServerConfig::fromArray($name, $data);
} catch (ConfigurationException $e) {
echo"Error parsing config for '{$name}': {$e->getMessage()}\n";
}
}
// 现在 $serverConfigs['stdio_files'] 和 $serverConfigs['http_api'] 包含ServerConfig对象。
使用构建器组装Client
实例:
use PhpMcp\Client\Client;
//... 其他用于Config、Logger等的use语句...
$client = Client::make()
->withClientInfo($clientName, $clientVersion) // 必需
->withCapabilities($clientCapabilities) // 可选(提供默认值)
->withServerConfig($stdioConfig) // 必需:服务器配置
->withLogger($myLogger) // 可选
->withCache($myCache, 3600) // 可选(缓存 + 生存时间)
->withEventDispatcher($myDispatcher) // 可选
->withIdGenerator($myIdGenerator) // 可选
->withLoop($myEventLoop) // 可选(默认为Loop::get())
->build();
一旦为特定服务器配置好了Client
实例:
在发出请求之前,必须调用initialize()
或initializeAsync()
。
// 同步(阻塞)
try {
$client->initialize(); // 连接、执行握手操作,等待直到准备就绪
echo"Connection Ready!";
} catch (Throwable $e) {
echo"Initialization failed: ". $e->getMessage();
// 处理错误... 客户端可能处于错误状态
}
// 异步(基于Promise)
$client->initializeAsync()->then(
function(Client $readyClient) { /* 准备就绪 */ },
function(Throwable $error) { /* 处理初始化失败 */ }
);
// 需要运行事件循环 ($client->getLoop()->run())
使用客户端方法,这些方法基于initialize()
建立的单个连接进行操作。
listTools()
、callTool()
、readResource()
等方法会阻塞执行,直到收到响应或超时。它们返回解析后的结果对象(如array<ToolDefinition>
、CallToolResult
),或者抛出异常(TimeoutException
、RequestException
、ConnectionException
等)。try {
if ($client->isReady()) { // 检查状态
$tools = $client->listTools();
$result = $client->callTool('myTool', ['param' => 'value']);
}
} catch (Throwable $e) { /* 处理错误 */ }
listToolsAsync()
、callToolAsync()
、readResourceAsync()
等方法返回一个React\Promise\PromiseInterface
。需要使用Promise方法(then
、catch
、finally
)或React\Async\await
(在Fiber上下文中)来处理结果。并且需要运行事件循环。use functionReact\Promise\all;
if ($client->isReady()) {
$p1 = $client->listToolsAsync();
$p2 = $client->readResourceAsync('config://settings');
all([$p1, $p2])->then(
function(array $results) {
[$tools, $readResult] = $results;
// 处理异步结果...
},
function(Throwable $error) {
// 处理异步错误...
}
);
// $client->getLoop()->run(); // 需要运行循环
}
与服务器交互完成后,始终要断开连接以释放资源(特别是对于stdio
传输)。
// 同步
$client->disconnect(); // 阻塞直到连接关闭或超时
// 异步
$client->disconnectAsync()->then(function() { echo "Disconnected async"; });
// $loop->run();
Client
类提供了与连接的MCP服务器交互的方法。大多数方法都有同步(阻塞)和异步(返回Promise)两种变体。
initialize(): self
:连接到服务器并执行MCP握手操作。阻塞直到准备就绪或抛出异常。返回客户端实例。initializeAsync(): PromiseInterface<Client>
:异步启动连接和握手操作。返回一个Promise,在连接准备就绪时解析为客户端实例,失败时拒绝。disconnect(): void
:优雅地关闭连接。阻塞直到断开连接完成或超时。disconnectAsync(): PromiseInterface<void>
:异步启动优雅断开连接操作。返回一个Promise,在断开连接完成时解析。getStatus(): ConnectionStatus
:返回当前连接状态枚举(Disconnected
、Connecting
、Handshaking
、Ready
、Closing
、Closed
、Error
)。isReady(): bool
:辅助方法,如果状态为Ready
则返回true
。getServerName():?string
:返回服务器名称(成功初始化后可用)。getServerVersion():?string
:返回服务器版本(成功初始化后可用)。getNegotiatedCapabilities():?Capabilities
:返回与服务器协商的功能(成功初始化后可用)。getNegotiatedProtocolVersion():?string
:返回与服务器商定的协议版本(成功初始化后可用)。(这些方法要求客户端先初始化,并且会阻塞)
ping(): void
listTools(bool $useCache = true): array<ToolDefinition>
listResources(bool $useCache = true): array<ResourceDefinition>
listPrompts(bool $useCache = true): array<PromptDefinition>
listResourceTemplates(bool $useCache = true): array<ResourceTemplateDefinition>
readResource(string $uri): ReadResourceResult
subscribeResource(string $uri): void
unsubscribeResource(string $uri): void
setLogLevel(string $level): void
(这些方法要求客户端先初始化,并返回React\Promise\PromiseInterface
)
pingAsync(): PromiseInterface<void>
listToolsAsync(): PromiseInterface<array<ToolDefinition>>
listResourcesAsync(): PromiseInterface<array<ResourceDefinition>>
listPromptsAsync(): PromiseInterface<array<PromptDefinition>>
listResourceTemplatesAsync(): PromiseInterface<array<ResourceTemplateDefinition>>
readResourceAsync(string $uri): PromiseInterface<ReadResourceResult>
subscribeResourceAsync(string $uri): PromiseInterface<void>
unsubscribeResourceAsync(string $uri): PromiseInterface<void>
setLogLevelAsync(string $level): PromiseInterface<void>
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有