PHP MCP 服务器 v1.0.0 发布!
简介
PHP MCP Server:Model Context Protocol(MCP)服务器的核心PHP实现。
Model Context Protocol(MCP)是一种开放标准,最初由Anthropic开发,旨在规范AI助手和工具连接外部数据源、API及其他系统的方式。可以将其想象为AI领域的USB-C接口,是一种统一、一致的提供上下文的方式。
php-mcp/server
是一个PHP库,使用它能够轻松构建符合MCP标准的服务器。其核心目标是让你能够通过最少的工作,主要利用PHP 8的属性,将现有PHP应用程序的特定方法,作为MCP工具、资源或提示公开。
该软件包目前支持2024年11月5日版本的Model Context Protocol,并且与各种符合该协议版本的MCP客户端兼容,如Claude Desktop、Cursor、Windsurf等。
关键特性
- 基于属性的定义:使用简单的PHP 8属性(
#[McpTool]
、#[McpResource]
、#[McpPrompt]
、#[McpResourceTemplate]
、#[McpTemplate]
)定义MCP元素(工具、资源、提示、模板)。 - 自动元数据推断:利用方法名称、参数名称、PHP类型提示(用于架构)和文档块(用于架构和描述)自动生成MCP定义,最大限度减少样板代码。
- 符合PSR标准:与标准PHP接口无缝集成:
PSR-3
(LoggerInterface):可使用自己的日志记录器(例如Monolog)。PSR-11
(ContainerInterface):使用自己喜欢的依赖注入容器(例如Laravel、Symfony、PHP-DI),在调用MCP元素时解析应用程序类及其依赖项。PSR-16
(SimpleCacheInterface):提供缓存实现(例如Symfony Cache、Laravel Cache),用于缓存发现的元素和传输状态。
- 灵活的配置:以合理的默认设置开始,但允许为日志记录、缓存、依赖注入容器和详细的MCP配置(
ConfigurationRepositoryInterface
)提供自己的实现。 - 多种传输方式:支持
stdio
(用于命令行客户端),并包含开箱即用的构建http+sse
(HTTP + Server-Sent Events)传输的组件(需要与HTTP服务器集成)。 - 自动发现:扫描项目内指定的目录,查找使用MCP属性注释的类和方法。
- 框架无关:设计为在普通PHP项目中或集成到任何PHP框架中都能同样良好地工作。
要求
- PHP >= 8.1
- Composer
安装
可以通过Composer安装该软件包:
代码语言:javascript代码运行次数:0运行复制composer require php-mcp/server
“注意:对于Laravel应用程序,建议使用专门的
php-mcp/laravel-server
软件包。它基于这个核心库构建,提供了针对Laravel框架的有用集成、配置选项和Artisan命令。
一个简单的stdio
服务器
以下是一个最小示例,展示如何使用stdio
传输将一个简单的PHP类方法作为MCP工具公开。
- 创建MCP元素类:创建一个文件,例如
src/MyMcpStuff.php
:
<?php
namespaceApp;
usePhpMcp\Server\Attributes\McpTool;
class MyMcpStuff
{
/**
* 一个用于将两个数字相加的简单工具。
*
* @param int $a 第一个数字。
* @param int $b 第二个数字。
* @return int 两个数字的和。
*/
#[McpTool(name: 'adder')]
publicfunction addNumbers(int $a, int $b): int
{
return $a + $b;
}
}
- 创建服务器脚本:在项目根目录中创建一个脚本,例如
mcp-server.php
:
<?php
declare(strict_types=1);
usePhpMcp\Server\Server;
// 确保包含项目的自动加载器
require_once__DIR__ . '/vendor/autoload.php';
// 如果MCP元素在特定命名空间中,确保该命名空间也被自动加载(例如通过composer.json)
// 可选:配置日志记录(默认记录到STDERR)
// $logger = new MyPsrLoggerImplementation(...);
$server = Server::make()
// 可选:->withLogger($logger)
// 可选:->withCache(new MyPsrCacheImplementation(...))
// 可选:->withContainer(new MyPsrContainerImplementation(...))
->withBasePath(__DIR__) // 开始扫描属性的目录
->withScanDirectories(['src']) // 要扫描的特定子目录(相对于basePath)
->discover(); // 查找所有#[Mcp*]属性
// 使用stdio传输运行服务器
$exitCode = $server->run('stdio');
exit($exitCode);
- 配置MCP客户端:配置MCP客户端(如Cursor、Claude Desktop等)以使用
stdio
传输进行连接。这通常涉及指定运行服务器脚本的命令。例如,在Cursor的.cursor/mcp.json
中:
{
"mcpServers": {
"my-php-server": {
"command": "php",
"args": [
"/path/to/your/project/mcp-server.php"
]
}
}
}
将/path/to/your/project/mcp-server.php
替换为脚本的实际绝对路径。现在,当连接客户端时,它应该能够发现adder
工具。
核心概念
通过php-mcp/server
公开功能的主要方式是使用特定属性装饰PHP方法。服务器会自动发现这些属性,并将它们转换为相应的MCP定义。
#[McpTool]
:将一个方法标记为MCP工具。工具代表客户端可以调用的操作或函数,通常带有参数。该属性接受以下参数:name
(可选):暴露给客户端的工具名称。默认为方法名称(例如,addNumbers
变为addNumbers
)。description
(可选):工具的描述。默认为方法的文档块摘要。- 方法的参数(包括名称、类型提示和默认值)定义了工具的输入架构。方法的返回类型提示定义了输出架构。文档块中的
@param
和@return
描述用于参数/输出描述。
- 返回值格式化 :方法返回的值决定了发送回客户端的内容。该库会自动格式化常见类型:
null
:返回空内容(如果返回类型提示为void
)或带有(null)
的TextContent
。string
、int
、float
、bool
:自动包装在PhpMcp\Server\JsonRpc\Contents\TextContent
中。array
、object
:自动JSON编码(漂亮打印)并包装在TextContent
中。PhpMcp\Server\JsonRpc\Contents\Content
对象:如果返回Content
(例如TextContent
、ImageContent
、AudioContent
、ResourceContent
)的实例或Content
对象数组,它们将直接使用。这使你可以完全控制输出格式。例如:return TextContent::code('echo \'Hello\';', 'php');
- 异常:如果方法抛出异常,将返回一个包含错误消息和类型的
TextContent
。方法的返回类型提示(文档块中的@return
标签)用于生成工具的输出架构,但实际格式化取决于运行时返回的值。
#[McpResource]
:将一个方法标记为代表特定的、静态的MCP资源实例。资源代表由URI标识的内容或数据片段。当客户端对指定的URI执行resources/read
时,通常会调用此方法。该属性接受以下参数:uri
(必需):此资源实例的唯一URI(例如,config://app/settings
、file:///data/status.txt
)。必须符合RFC 3986。name
(可选):人类可读的名称。默认值从方法名称推断。description
(可选):描述。默认值为文档块摘要。mimeType
(可选):资源的MIME类型(例如,text/plain
、application/json
)。size
(可选):资源大小(以字节为单位),如果已知且固定。annotations
(可选):MCP注释数组(例如,['audience' => ['user']]
)。方法应返回资源的内容。
#[McpResourceTemplate]
:将一个方法标记为可以基于模板URI生成资源实例的方法。这对于URI包含可变部分(如用户ID或文档ID)的资源很有用。当客户端执行与模板匹配的resources/read
时,将调用此方法。该属性接受以下参数:uriTemplate
(必需):URI模板字符串,符合RFC 6570(例如,user://{userId}/profile
、document://{docId}?format={fmt}
)。name
、description
、mimeType
、annotations
(可选):与#[McpResource]
类似,但用于描述模板本身。方法参数必须与uriTemplate
中定义的变量匹配。方法应返回解析后的资源实例的内容。
#[McpPrompt]
:将一个方法标记为MCP提示生成器。提示是预定义的模板或函数,可根据输入参数生成对话消息(如用户或助手的回复)。该属性接受以下参数:name
(可选):提示名称。默认值为方法名称。description
(可选):描述。默认值为文档块摘要。方法参数定义了提示的输入参数。方法应返回提示内容,通常是符合MCP消息结构的数组。
Server流接口
PhpMcp\Server\Server
类是配置和运行MCP服务器的主要入口点。它提供了一个流畅接口(方法链式调用)来设置依赖项和参数。
Server::make(): self
:静态工厂方法,用于创建一个新的服务器实例。->withLogger(LoggerInterface $logger): self
:提供一个符合PSR-3标准的日志记录器实现。默认情况下,使用一个基本的StreamLogger
,将日志写入STDERR
,日志级别为LogLevel::INFO
。->withCache(CacheInterface $cache): self
:提供一个符合PSR-16标准的缓存实现。用于缓存发现的MCP元素以及可能的传输状态。默认情况下,使用一个简单的内存ArrayCache
。->withContainer(ContainerInterface $container): self
:提供一个符合PSR-11标准的依赖注入容器。当需要调用包含#[Mcp*]
属性的方法时,这个容器将用于实例化相应的类。默认情况下,使用一个非常基本的BasicContainer
,它只能解析显式设置的服务。->withConfig(ConfigurationRepositoryInterface $config): self
:提供一个自定义配置存储库。这允许覆盖所有默认设置,包括启用的功能、协议版本、缓存键/生存时间等。默认情况下,使用ArrayConfigurationRepository
,并带有预定义的默认值。->withBasePath(string $path): self
:设置发现过程中目录扫描的绝对基本路径。默认值为当前工作目录(getcwd()
)。->withScanDirectories(array $dirs): self
:指定一个相对于basePath
的目录路径数组,服务器将在这些目录中查找注释方法。默认值为['.']
(即基本路径本身)。- ->discover(bool
->run(?string $transport = null): int
:使用指定的传输启动服务器的主处理循环。- 如果
$transport
为'stdio'
(或在CLI中运行时为null
),它将使用StdioTransportHandler
通过标准输入/输出进行通信。 - 如果
$transport
为'http'
,它将抛出一个异常,因为HTTP传输需要集成到现有的HTTP服务器循环中(请参阅传输部分)。返回退出代码(与stdio
相关)。
- 如果
发现
当调用->discover()
时,服务器会在->withScanDirectories()
指定的目录(相对于->withBasePath()
)中查找所有文件。对于每个文件,它会尝试解析类定义,反射该类的公共非静态方法,并检查这些方法是否有#[McpTool]
、#[McpResource]
、#[McpPrompt]
或#[McpResourceTemplate]
属性。
如果找到一个属性,它会从属性实例、方法签名(名称、参数、类型提示)和方法的文档块中提取元数据。然后,它会创建一个相应的Definition
对象(例如,ToolDefinition
、ResourceDefinition
),并将它们注册到注册表中。最后,收集到的定义会被序列化并存储在通过->withCache()
提供的缓存中(使用配置的缓存键和生存时间),以加快后续服务器启动的速度。
依赖注入
当MCP客户端调用一个工具或读取一个映射到你带有属性的方法的资源/提示时:
Processor
从Registry
中识别目标类和方法。- 它使用通过
->withContainer()
提供的PSR-11容器来检索目标类的实例(例如,$container->get(MyMcpStuff::class)
)。这意味着你的类构造函数可以注入在容器中配置的任何依赖项(数据库连接、服务等)。 - 处理器然后根据客户端请求和方法签名准备参数。
- 最后,它在检索到的类实例上调用目标方法。
使用默认的BasicContainer
仅适用于非常简单的情况,即你带有属性的方法所在的类没有构造函数依赖项。对于任何实际应用程序,你都应该提供自己的PSR-11容器实例,例如->withContainer(MyFrameworkContainer::getInstance())
。
配置
服务器的行为可以通过实现PhpMcp\Server\Contracts\ConfigurationRepositoryInterface
的配置存储库进行自定义。你可以使用->withConfig()
来提供这个存储库。如果没有提供,则使用默认的PhpMcp\Server\Defaults\ArrayConfigurationRepository
。
关键配置值(使用点表示法)包括:
mcp.server.name
:(字符串)用于握手的服务器名称。mcp.server.version
:(字符串)用于握手的服务器版本。mcp.protocol_versions
:(数组)支持的协议版本(例如,['2024-11-05']
)。mcp.pagination_limit
:(整数)列出元素的默认限制。mcp.capabilities.tools.enabled
:(布尔值)启用/禁用工具功能。mcp.capabilities.resources.enabled
:(布尔值)启用/禁用资源功能。mcp.capabilities.resources.subscribe
:(布尔值)启用/禁用资源订阅。mcp.capabilities.prompts.enabled
:(布尔值)启用/禁用提示功能。mcp.capabilities.logging.enabled
:(布尔值)启用/禁用logging/setLevel
方法。mcp.cache.ttl
:(整数)缓存生存时间(以秒为单位)。mcp.cache.prefix
:(字符串)与MCP相关的缓存前缀。mcp.discovery.base_path
:(字符串)发现的基本路径(会被withBasePath
覆盖)。mcp.discovery.directories
:(数组)要扫描的目录(会被withScanDirectories
覆盖)。mcp.runtime.log_level
:(字符串)默认日志级别(由默认日志记录器使用)。
你可以创建自己的接口实现,或者将填充了覆盖值的ArrayConfiguration