本文导读:以原理与示例结合的形式讲解PHP开发者如何基于PHP-MCP框架玩转MCP。将自己开发的PHP应用发布为 MCP Server,验证使用Cursor应用作为客户端接入自己发布的 PHP MCP Server。
2024 年 11 月,Anthropic 公司搞了个挺有意思的新玩意 - Model Context Protocol(模型上下文协议)简称为 MCP 协议。简单来说,它就是给 AI 和各类工具数据之间搭了个标准化的”桥梁”,让开发者不用再为对接问题头疼了。
大模型应用可以使用别人分享的 MCP 服务来完成各种各样的工作内容,MCP 协议在实际的应用场景上非常广泛,本文应用场景为Cursor 快速接入腾讯地图 MCP Server 服务。
接下来我们使用 Cursor 快速接入腾讯位置 MCP Server 服务。
腾讯地图WebService API 是基于HTTPS/HTTP协议构建的标准化地理数据服务接口。该接口支持跨平台调用,开发者可使用任意客户端、服务器端技术及编程语言,遵循API规范发起HTTPS请求,获取地理信息服务(目前支持JSON/JSONP格式的数据返回)。
腾讯位置服务MCP Server(SSE)依赖WebServiceAPI构建,因此需要先创建key,并开启WebServiceAPI功能,取得相关接口调用配额后方可使用, 具体方法可参见: http://lbs.qq.com/service/web
创建应用
添加key
复制Key,在接来下的过程中会使用到
在 Cursor 中配置 MCP Server, 本文以 Cursor 开发工具为例,详细介绍如何配置 MCP Server。若使用其他开发工具(如 VS Code、IntelliJ IDEA 等),可参考相关文档进行类似配置,核心流程基本一致。
“SSE模式配置
{
"mcpServers": {
"TencentAISearch": {
"url": "https://mcp.map.qq.com/sse?key=<您的Key>"
}
}
}
配置成功后,会有绿色的小圆点,代表服务已经配置成功。 img
在对话界面,在输入框下方工具中,点击@Add context
符号,选配置的mcp.json
选项。
更多了解:https://mcp.so/zh/server/Tencent%20Map/tencent-map?tab=content
在的对话框中输入指令:杭州五一期间有哪些必玩的景点?
此时我们就可以享受腾讯地图 MCP Server 服务提供的操作地理位置的能力
从图上可以看到,通过杭州五一期间有哪些必玩的景点?
这样的提示词,Cursor的大模型自行判断需要使用 MCP 中提供的腾讯地图WebService API能力,从而完成了地理位置的推荐。
MCP 主要分为 MCP 服务和 MCP 客户端:
整体架构如下:
“整体的工作流程是这样的:AI 应用中集成 MCP 客户端,通过 MCP 协议向 MCP 服务端发起请求,MCP 服务端可以连接本地/远程的数据源,或者通过 API 访问其他服务,从而完成数据的获取,返回给 AI 应用去使用。
基于 SSE (Server-Sent Events) 的 MCP 服务端通过 HTTP 协议与客户端通信,适用于作为独立服务部署的场景,可以被多个客户端远程调用,具体做法与 stdio 非常类似。客户端通过服务端的 URL 进行远程访问,适用于比较重量级的工具。
composer require php-mcp/server
“使用
@McpTool
注解标记方法,使其可以被 MCP 客户端发现和调用。
将ReactPhpHttpTransportHandler
与ReactPHP集成以创建异步 HTTP+SSE 服务器。SampleMcpElements.php
文件
<?php
usePhpMcp\Server\Attributes\McpPrompt;
usePhpMcp\Server\Attributes\McpResource;
usePhpMcp\Server\Attributes\McpResourceTemplate;
usePhpMcp\Server\Attributes\McpTool;
/**
* A sample class containing methods decorated with MCP attributes for testing discovery.
*/
class SampleMcpElements
{
/**
* A sample tool method.
*
* @param string $name The name to greet.
* @return string The greeting message.
*/
#[McpTool(name: 'tinywanTool', description: '开源技术小栈微信公众号生成简介.')]
publicfunction tinywanTool(string $name): string
{
return'[开源技术小栈]'.$name;
}
/**
* A sample tool method.
*
* @param string $name The name to greet.
* @param int $count The number of times to greet.
* @return string The greeting message.
*/
#[McpTool(name: 'greet_user', description: 'Generates a greeting for a user.')]
publicfunction simpleTool(string $name, int $count = 1): string
{
if ($count === null) {
$count = 1;
}
return implode(' ', array_fill(0, $count, "Hello, {$name}!"));
}
#[McpTool(description: 'Another tool with no explicit name.')]
publicfunction anotherTool(
string $input,
int $count = 1
): array {
return ['processed' => $input, 'count' => $count];
}
/**
* Generates a simple story prompt.
*
* @param string $subject The main subject of the story.
* @param string $genre The genre (e.g., fantasy, sci-fi).
*/
#[McpPrompt(name: 'create_story', description: 'Creates a short story premise.')]
publicfunction storyPrompt(string $subject, string $genre = 'fantasy'): array
{
// In a real scenario, this would return the prompt string
return [
[
'role' => 'user',
'content' => "Write a short {$genre} story about {$subject}.",
],
];
}
#[McpPrompt]
publicfunction simplePrompt(): array
{
return [
[
'role' => 'user',
'content' => 'This is a simple prompt with no arguments.',
],
];
}
#[McpResource(uri: 'config://app/name', name: 'app_name', description: 'The application name.', mimeType: 'text/plain')]
publicfunction getAppName(): string
{
// In a real scenario, this would fetch the config value
return'My MCP App';
}
#[McpResource(uri: 'file://data/users.csv', name: 'users_csv', mimeType: 'text/csv')]
publicfunction getUserData(): string
{
// In a real scenario, this would return file content
return"id,name\n1,Alice\n2,Bob";
}
#[McpResourceTemplate(uriTemplate: 'user://{userId}/profile', name: 'user_profile', mimeType: 'application/json')]
publicfunction getUserProfileTemplate(string $userId): array
{
// In a real scenario, this would fetch user data based on userId
return ['id' => $userId, 'name' => 'User '.$userId, 'email' => $userId.'@example.com'];
}
}
reactphp_http.server.php
服务端文件
<?php
/**
* @desc reactphp_http.server.php
* @author Tinywan(ShaoBo Wan)
*/
declare(strict_types=1);
use PhpMcp\Server\Defaults\StreamLogger;
use PhpMcp\Server\Server;
use PhpMcp\Server\Transports\ReactPhpHttpTransportHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\HttpServer;
use React\Http\Message\Response;
use React\Promise\Promise;
use React\Socket\SocketServer;
use React\Stream\ThroughStream;
// --- Instructions ---
// 1. cd php-mcp/samples/reactphp
// 2. composer install
// 3. Run: php server.php
// 4. Use an MCP client to connect via HTTP SSE:
// - SSE Endpoint: http://127.0.0.1:8080/mcp/sse
// - POST Endpoint will be provided by the server via the 'endpoint' event.
// --- ------------ ---
require_once __DIR__.'/vendor/autoload.php';
require_once __DIR__.'/SampleMcpElements.php';
// --- MCP Server Setup ---
$logger = new StreamLogger(__DIR__.'/react_server.log', 'debug');
$server = Server::make()
->withLogger($logger)
->withBasePath(__DIR__)
->discover();
$transportHandler = new ReactPhpHttpTransportHandler($server);
// --- ReactPHP HTTP Server Setup ---
$postEndpoint = '/mcp/message';
$sseEndpoint = '/mcp/sse';
$listenAddress = '0.0.0.0:8201';
$http = new HttpServer(function (ServerRequestInterface $request) use ($logger, $transportHandler, $postEndpoint, $sseEndpoint): ResponseInterface|Promise {
$path = $request->getUri()->getPath();
$method = $request->getMethod();
$responseHeaders = ['Access-Control-Allow-Origin' => '*'];
$logger->info('Received request', ['method' => $method, 'path' => $path]);
// POST Endpoint Handling
if ($method === 'POST' && str_starts_with($path, $postEndpoint)) {
$queryParams = $request->getQueryParams();
$clientId = $queryParams['clientId'] ?? null;
if (! $clientId || ! is_string($clientId)) {
return new Response(400, $responseHeaders, 'Error: Missing or invalid clientId query parameter');
}
if (! str_contains($request->getHeaderLine('Content-Type'), 'application/json')) {
return new Response(415, $responseHeaders, 'Error: Content-Type must be application/json');
}
$requestBody = (string) $request->getBody();
if (empty($requestBody)) {
return new Response(400, $responseHeaders, 'Error: Empty request body');
}
try {
$transportHandler->handleInput($requestBody, $clientId);
return new Response(202, $responseHeaders); // Accepted
} catch (JsonException $e) {
return new Response(400, $responseHeaders, "Error: Invalid JSON - {$e->getMessage()}");
} catch (Throwable $e) {
return new Response(500, $responseHeaders, 'Error: Internal Server Error');
}
}
// SSE Endpoint Handling
if ($method === 'GET' && $path === $sseEndpoint) {
$clientId = 'client_'.bin2hex(random_bytes(16));
$logger->info('ReactPHP SSE connection opening', ['client_id' => $clientId]);
$stream = new ThroughStream;
$postEndpointWithClientId = $postEndpoint.'?clientId='.urlencode($clientId);
$transportHandler->setClientSseStream($clientId, $stream);
$transportHandler->handleSseConnection($clientId, $postEndpointWithClientId);
$sseHeaders = [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
'X-Accel-Buffering' => 'no',
'Access-Control-Allow-Origin' => '*',
];
return new Response(200, $sseHeaders, $stream);
}
// Fallback 404
return new Response(404, $responseHeaders, 'Not Found');
});
$socket = new SocketServer($listenAddress);
$logger->info("ReactPHP MCP Server listening on {$listenAddress}");
$logger->info("SSE Endpoint: http://{$listenAddress}{$sseEndpoint}");
$logger->info("POST Endpoint: (Sent via SSE 'endpoint' event)");
echo"ReactPHP MCP Server listening on http://{$listenAddress}\n";
$http->listen($socket);
在控制台中执行如下命令
# php reactphp_http.server.php
ReactPHP MCP Server listening on http://0.0.0.0:8201
“服务端将在
http://localhost:8201
启动
在上一小节中我们编写完了 MCP 服务,这些服务到底是否能正常运行呢?在 Cursor 中可以测试一下。
“SSE模式配置
{
"mcpServers": {
"mcp-php-server-http": {
"url": "http://127.0.0.1:8201/mcp/sse"
}
}
}
配置成功后,会有绿色的小圆点,代表服务已经配置成功。
在对话界面,在输入框下方工具中,点击@Add context
符号,选配置的mcp.json
选项。
调用简单工具方法如下
#[McpTool(name: 'greet_user', description: 'Generates a greeting for a user.')]
public function simpleTool(string $name, int $count = 1): string
{
if ($count === null) {
$count = 1;
}
return implode(' ', array_fill(0, $count, "Hello, {$name}!"));
}
“工具:tinywanTool:超高性能可扩展PHP框架webman
作为 AI 开发领域的革命性突破,Model Context Protocol(MCP)重新定义了智能体与工具生态的交互范式。通过标准化协议打通地图服务、代码仓库、数据库等核心工具链,MCP 不仅解决了传统 AI 开发中跨平台对接的碎片化难题,更以”开箱即用”的轻量化集成模式,让开发者能够快速构建具备多模态能力的智能应用。
未来,随着更多工具接入 MCP 生态,开发者只需专注于业务逻辑创新,而复杂的工具链整合将真正成为”看不见的底层能力”——这或许正是 AI 普惠化进程中,最具实际意义的技术跃迁。
PHP-MCP 中的 MCP 支持可以让 PHP 开发者轻松的将自己的应用发布为 MCP Server 或者是作为消费者去集成任意的 MCP Server 实现。
扫码关注腾讯云开发者
领取腾讯云代金券
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. 腾讯云 版权所有