使用说明
phpnl/mcp
一个与框架无关的PHP SDK 模型上下文协议(MCP).在几分钟内将PHP应用程序连接到像Claude这样的AI模型——零运行时依赖关系。
composer require phpnl/mcp
目录
什么是MCP?
模型上下文协议是一个开放标准,允许AI模型与外部工具、数据源和服务进行交互。MCP服务器公开:
- 工具 --AI可以调用的功能(例如查询数据库、发送电子邮件)
- 资源 --AI可以引用的只读数据(例如配置文件、文档)
- 提示 --人工智能可以检索和使用的可重用提示模板
该库在PHP中实现了MCP服务器端。
快速开始
创建 server.php:
<?php
declare(strict_types=1);
require 'vendor/autoload.php';
use Phpnl\Mcp\McpServer;
McpServer::make()
->tool(
name: 'get_user',
description: 'Fetch a user record from the database',
handler: function (int $id): string {
$user = fetchUserById($id); // your own code
return json_encode($user);
},
)
->serve();
将服务器添加到您的Claude桌面 claude_desktop_config.json:
{
"mcpServers": {
"my-app": {
"command": "php",
"args": ["/absolute/path/to/server.php"]
}
}
}
重新启动Claude Desktop——您的工具立即可用。
工具
注册工具
McpServer::make()
->tool(
name: 'send_email',
description: 'Send an email to a recipient',
handler: function (string $to, string $subject, string $body): string {
mail($to, $subject, $body);
return "Email sent to {$to}";
},
)
->serve();
这 handler 是一个 Closure其参数名称和类型提示会自动反映到向AI通告的工具JSON模式中。
类型安全参数
PHP类型直接映射到JSON模式类型:
| PHP类型 | JSON模式类型 |
|---|---|
string |
"string" |
int |
"integer" |
float |
"number" |
bool |
"boolean" |
array |
"array" |
?T / T|null |
["T", "null"] |
任何其他类型的提示都会回退到 "string".
参数说明
使用 #[Description] 属性,为参数添加人类可读的描述。这些都包含在JSON模式中,因此AI可以理解每个参数的用途。
use Phpnl\Mcp\Tool\Description;
McpServer::make()
->tool(
name: 'search_products',
description: 'Search the product catalogue',
handler: function (
#[Description('Search term, e.g. "blue jeans"')] string $query,
#[Description('Maximum number of results to return (1–100)')] int $limit = 10,
): string {
return json_encode(searchProducts($query, $limit));
},
)
->serve();
可选参数
具有默认值的参数是可选的——AI可以省略它们,并自动使用默认值。
handler: function (string $name, string $greeting = 'Hello'): string {
return "{$greeting}, {$name}!";
}
可选参数不包括在 inputSchema.required.
输入验证
在调用处理程序之前,参数会根据生成的JSON模式自动验证。如果缺少必需的参数或类型错误 InvalidParams 错误将返回给客户端——您的处理程序永远不会被调用。
// If the AI omits 'to', the server responds with:
// {"error": {"code": -32602, "message": "Invalid params", "data": "Missing required argument: to"}}
丰富的工具结果
默认情况下,处理程序返回一个明文 string,其作为单个文本内容项发送。返回a ToolResult 以获得更丰富的回应。
use Phpnl\Mcp\Tool\ToolResult;
// Single text item
return ToolResult::text('Here is the summary: ...');
// Image (base64-encoded)
return ToolResult::image(base64_encode($pngBytes), 'image/png');
// Embedded resource
return ToolResult::resource('file://report.json', $json, 'application/json');
// Multiple items chained together
return ToolResult::text('Monthly revenue: €12,400')
->withImage(base64_encode($chartPng), 'image/png')
->withText('Data as of 2024-01-01');
ToolResult 是不可变的。每 with*() 调用附加一个项目并返回一个新实例。
| 工厂/方法 | 内容类型 | 目的 |
|---|---|---|
ToolResult::text(string $text) |
text |
纯文本或标记文本 |
ToolResult::image(string $data, string $mimeType) |
image |
Base64编码图像 |
ToolResult::resource(string $uri, string $text, string $mimeType) |
resource |
嵌入式资源 |
->withText(string $text) |
text |
附加文本项 |
->withImage(string $data, string $mimeType) |
image |
附加图像项 |
->withResource(string $uri, string $text, string $mimeType) |
resource |
附加资源项 |
进度通知
对于长时间运行的工具,注入 ProgressReporter 参数。SDK会自动注入它 不 出现在工具的JSON模式中。
use Phpnl\Mcp\Tool\ProgressReporter;
McpServer::make()
->tool(
name: 'import_csv',
description: 'Import a CSV file into the database',
handler: function (
string $path,
ProgressReporter $progress,
): string {
$rows = array_map('str_getcsv', file($path));
$total = count($rows);
foreach ($rows as $i => $row) {
insertRow($row);
$progress->report($i + 1, $total); // (current, total)
}
return "Imported {$total} rows from {$path}.";
},
)
->serve();
客户端发送 progressToken 在 _meta 要选择参与进度更新,请执行以下操作:
{
"method": "tools/call",
"params": {
"name": "import_csv",
"arguments": {"path": "/tmp/data.csv"},
"_meta": {"progressToken": "import-42"}
}
}
当处理程序运行时,服务器会发送带外通知:
{"jsonrpc": "2.0", "method": "notifications/progress", "params": {"progressToken": "import-42", "progress": 150, "total": 1000}}
当客户端发送“否”时 progressToken,全部 $progress->report() 调用会被默默地忽略——同一处理程序适用于流式和非流式客户端,无需任何代码更改。
资源
资源公开了人工智能可以检索和推理的只读数据。
McpServer::make()
->resource(
uri: 'file://app-config',
name: 'Application Config',
mimeType: 'application/json',
handler: function (): string {
return file_get_contents(__DIR__ . '/config.json');
},
)
->resource(
uri: 'db://schema',
name: 'Database Schema',
mimeType: 'text/plain',
handler: function (): string {
return generateSchemaDescription(); // your own code
},
)
->serve();
资源出现在 resources/list 并与 resources/readThe capabilities.resources 密钥仅在 initialize 当至少一个资源被注册时的响应。
提示
提示是AI可以检索和填充的可重用模板。
McpServer::make()
->prompt(
name: 'code_review',
description: 'Review a piece of code for bugs and style issues',
handler: function (array $args): string {
$language = $args['language'] ?? 'PHP';
$code = $args['code'] ?? '';
return "Please review the following {$language} code for correctness, "
. "potential bugs, and style:\n\n```{$language}\n{$code}\n```";
},
)
->serve();
提示出现在 prompts/list 并与 prompts/get与资源一样, capabilities.prompts 仅当至少注册了一个提示时才进行广告宣传。
中间件
注册中间件以包装每个工具调用——用于日志记录、身份验证、速率限制、缓存等。
McpServer::make()
->middleware(function (string $name, array $args, callable $next): mixed {
// Before: log the call
$start = microtime(true);
error_log("→ tool:{$name} " . json_encode($args));
$result = $next($name, $args);
// After: log the duration
$ms = round((microtime(true) - $start) * 1000);
error_log("← tool:{$name} {$ms}ms");
return $result;
})
->tool('ping', 'Returns pong', fn (): string => 'pong')
->serve();
签字: function(string $name, array $arguments, callable $next): mixed
多个中间件按注册顺序执行——第一个注册的是最外层的包装器。每个中间件必须调用 $next($name, $args) 以继续链,或直接返回一个值以使剩余的中间件和处理程序短路。
// Authentication middleware
->middleware(function (string $name, array $args, callable $next): mixed {
if (! isAuthenticated()) {
throw new \RuntimeException('Unauthorized');
}
return $next($name, $args);
})
// Rate-limiting middleware
->middleware(function (string $name, array $args, callable $next): mixed {
if (isRateLimited($name)) {
throw new \RuntimeException('Rate limit exceeded');
}
return $next($name, $args);
})
异常处理
所有特定于MCP的错误都作为扩展的类型化异常抛出 McpException (它本身延伸 \RuntimeException).JSON-RPC错误代码可通过以下方式获得 getErrorCode().
| 异常类 | 发生时抛出 | 错误代码 |
|---|---|---|
ToolNotFoundException |
工具名称未注册 | ToolNotFound (−32601) |
InvalidToolArgumentsException |
必需参数缺失或类型错误 | InvalidParams (−32602) |
ResourceNotFoundException |
资源URI未注册 | ResourceNotFound (−32002) |
PromptNotFoundException |
提示名称未注册 | PromptNotFound (−32003) |
直接从注册表中捕获异常(例如在测试或CLI工具中):
use Phpnl\Mcp\Exception\McpException;
use Phpnl\Mcp\Exception\ToolNotFoundException;
try {
$result = $toolRegistry->call('missing', []);
} catch (ToolNotFoundException $e) {
echo "No such tool: " . $e->getMessage();
} catch (McpException $e) {
// Any other MCP error
echo "MCP error " . $e->getErrorCode()->value . ": " . $e->getMessage();
}
这 JsonRpcHandler 抓住一切 McpException 子类会自动将其转换为格式良好的JSON-RPC错误响应。意外 \Throwable 异常被捕获并返回为 InternalError (−32603).
运输
STDIN/STDOUT(默认)
默认传输从以下位置读取JSON-RPC消息 STDIN 并将回复写入 STDOUT这是Claude Desktop和其他基于CLI的本地MCP客户端的标准。
McpServer::make() // StdioTransport is used automatically
->tool(...)
->serve();
HTTP+SSE
使用 HttpSseTransport 用于基于web的客户端、浏览器集成或远程部署。
use Phpnl\Mcp\Transport\HttpSseTransport;
$transport = new HttpSseTransport(port: 8080);
McpServer::make($transport)
->tool('hello', 'Returns a greeting', fn (): string => 'Hello from PHP!')
->serve();
php server.php
# MCP server now listening on http://localhost:8080
传输暴露了两个端点:
| 端点 | 方法 | 目的 |
|---|---|---|
/sse |
GET |
客户端连接并接收服务器发送的事件流。第一个事件是 endpoint 带有消息URL的事件 |
/message |
POST |
客户端在此处发送JSON-RPC请求。响应通过SSE流到达。 |
配置
new HttpSseTransport(
host: '0.0.0.0', // Bind address (default: 0.0.0.0)
port: 8080, // TCP port (default: 8080)
ssePath: '/sse', // SSE endpoint path (default: /sse)
messagePath: '/message', // POST endpoint path (default: /message)
baseUrl: null, // Public URL override — set when behind a reverse proxy
// e.g. 'https://api.example.com'
);
跨域资源共享
所有回复包括 Access-Control-Allow-Origin: *.CORS飞行前(OPTIONS)请求是自动处理的,因此基于浏览器的客户端无需额外配置即可工作。
反向代理
在nginx或其他代理后面运行时,设置 baseUrl 因此客户端会收到正确的公共端点URL:
$transport = new HttpSseTransport(
host: '127.0.0.1',
port: 9000,
baseUrl: 'https://mcp.example.com',
);
开发人员CLI
这 phpnl 二进制文件为在开发过程中检查和调试MCP服务器提供了工具。
# List all registered tools and their schemas
./vendor/bin/phpnl inspect server.php
# Stream full JSON-RPC traffic in real time (tools, resources, prompts)
./vendor/bin/phpnl debug server.php
# Call a specific tool and print the result
./vendor/bin/phpnl call server.php get_user --id=42
./vendor/bin/phpnl call server.php send_email --to=alice@example.com --subject=Hello --body=Hi
# Read a resource
./vendor/bin/phpnl read server.php file://app-config
# Retrieve a prompt
./vendor/bin/phpnl prompt server.php code_review --language=PHP --code="echo 'hi';"
参数类型转换遵循工具的JSON模式:
| PHP类型提示 | CLI参数形式 | 示例 |
|---|---|---|
string |
--name=value |
--to=alice@example.com |
int |
--name=N |
--id=42 |
float |
--name=N.N |
--price=9.99 |
bool |
--name=true|false |
--active=true |
API 参考
McpServer
| 方法 | 说明 |
|---|---|
McpServer::make(?TransportInterface $transport) |
创建新服务器(静态工厂) |
->tool(string $name, string $description, Closure $handler): self |
注册工具 |
->resource(string $uri, string $name, string $mimeType, Closure $handler): self |
注册资源 |
->prompt(string $name, string $description, Closure $handler): self |
注册提示 |
->middleware(Closure $fn): self |
添加工具中间件 |
->serve(): void |
启动服务器循环 |
ToolResult
| 方法 | 说明 |
|---|---|
ToolResult::text(string $text) |
使用单个文本项创建结果 |
ToolResult::image(string $data, string $mimeType) |
使用单个图像项创建结果 |
ToolResult::resource(string $uri, string $text, string $mimeType) |
使用单个资源项创建结果 |
->withText(string $text) |
追加文本项,返回新实例 |
->withImage(string $data, string $mimeType) |
追加图像项,返回新实例 |
->withResource(string $uri, string $text, string $mimeType) |
追加资源项,返回新实例 |
->toContent() |
返回原始MCP内容数组 |
ProgressReporter
| 方法 | 说明 |
|---|---|
->report(int|float $progress, int|float|null $total = null) |
发送进度通知。无操作时无操作 progressToken 由客户提供。 |
#[Description]
应用于工具处理程序参数,为JSON模式添加人类可读的描述:
function (#[Description('The user ID')] int $id): string { ... }
支持的MCP方法
| 方法 | 支持 |
|---|---|
initialize |
✅ |
notifications/initialized |
✅ |
ping |
✅ |
tools/list |
✅ |
tools/call |
✅ |
resources/list |
✅ |
resources/read |
✅ |
prompts/list |
✅ |
prompts/get |
✅ |
notifications/progress |
✅ (服务器→ 客户) |
需求
- PHP 8.3 或更高
- 零运行时依赖关系
测试
composer install
composer test # PHPUnit (unit + integration)
composer stan # PHPStan level 8
composer lint # PHP CS Fixer (dry-run)
要自动修复代码样式,请执行以下操作:
vendor/bin/php-cs-fixer fix
许可证
麻省理工学院-用❤️ 通过 Php.nl
