PHP模型上下文协议SDK

一个框架无关的PHP SDK,用于实现模型上下文协议(MCP),使PHP应用能够快速连接AI模型如Claude。

  • by Php-nl
  • PHP
  • 本地部署
  • 1 星标
  • 更新于

使用说明

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