前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PHP开发玩转MCP〡腾讯地图SSE接入Cursor客户端应用

PHP开发玩转MCP〡腾讯地图SSE接入Cursor客户端应用

作者头像
Tinywan
发布于 2025-05-06 07:34:20
发布于 2025-05-06 07:34:20
28100
代码可运行
举报
文章被收录于专栏:开源技术小栈开源技术小栈
运行总次数:0
代码可运行

本文导读:以原理与示例结合的形式讲解PHP开发者如何基于PHP-MCP框架玩转MCP。将自己开发的PHP应用发布为 MCP Server,验证使用Cursor应用作为客户端接入自己发布的 PHP MCP Server。

MCP 介绍

2024 年 11 月,Anthropic 公司搞了个挺有意思的新玩意 - Model Context Protocol(模型上下文协议)简称为 MCP 协议。简单来说,它就是给 AI 和各类工具数据之间搭了个标准化的”桥梁”,让开发者不用再为对接问题头疼了。

大模型应用可以使用别人分享的 MCP 服务来完成各种各样的工作内容,MCP 协议在实际的应用场景上非常广泛,本文应用场景为Cursor 快速接入腾讯地图 MCP Server 服务。

在Cursor 中体验 MCP

接下来我们使用 Cursor 快速接入腾讯位置 MCP Server 服务。

服务简介

腾讯地图WebService API 是基于HTTPS/HTTP协议构建的标准化地理数据服务接口。该接口支持跨平台调用,开发者可使用任意客户端、服务器端技术及编程语言,遵循API规范发起HTTPS请求,获取地理信息服务(目前支持JSON/JSONP格式的数据返回)。

创建APIKey

腾讯位置服务MCP Server(SSE)依赖WebServiceAPI构建,因此需要先创建key,并开启WebServiceAPI功能,取得相关接口调用配额后方可使用, 具体方法可参见: http://lbs.qq.com/service/web

创建应用

添加key

复制Key,在接来下的过程中会使用到

配置 MCP Server 服务

在 Cursor 中配置 MCP Server, 本文以 Cursor 开发工具为例,详细介绍如何配置 MCP Server。若使用其他开发工具(如 VS Code、IntelliJ IDEA 等),可参考相关文档进行类似配置,核心流程基本一致。

“SSE模式配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "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 应用

在的对话框中输入指令:杭州五一期间有哪些必玩的景点?

此时我们就可以享受腾讯地图 MCP Server 服务提供的操作地理位置的能力

从图上可以看到,通过杭州五一期间有哪些必玩的景点?这样的提示词,Cursor的大模型自行判断需要使用 MCP 中提供的腾讯地图WebService API能力,从而完成了地理位置的推荐。

MCP 的架构

MCP 主要分为 MCP 服务和 MCP 客户端:

  • 客户端:一般指的是大模型应用,比如 Cursor、通过Langchain 等框架开发的 AI 应用
  • 服务端:连接各种数据源的服务和工具

整体架构如下:

“整体的工作流程是这样的:AI 应用中集成 MCP 客户端,通过 MCP 协议向 MCP 服务端发起请求,MCP 服务端可以连接本地/远程的数据源,或者通过 API 访问其他服务,从而完成数据的获取,返回给 AI 应用去使用。

在 PHP 中使用 Mcp Server

服务端实现

PHP MCP 服务器 v1.0.0 发布!

基于 SSE 的 MCP 服务端实现

基于 SSE (Server-Sent Events) 的 MCP 服务端通过 HTTP 协议与客户端通信,适用于作为独立服务部署的场景,可以被多个客户端远程调用,具体做法与 stdio 非常类似。客户端通过服务端的 URL 进行远程访问,适用于比较重量级的工具。

安装依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
composer require php-mcp/server

实现 MCP 工具

“使用@McpTool 注解标记方法,使其可以被 MCP 客户端发现和调用。

ReactPhpHttpTransportHandler与ReactPHP集成以创建异步 HTTP+SSE 服务器。SampleMcpElements.php文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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'];
    }
}

配置 MCP 服务端

reactphp_http.server.php服务端文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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);

运行服务端

在控制台中执行如下命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# php reactphp_http.server.php 
ReactPHP MCP Server listening on http://0.0.0.0:8201

“服务端将在 http://localhost:8201 启动

在 Cursor 中测试 mcp 服务

在上一小节中我们编写完了 MCP 服务,这些服务到底是否能正常运行呢?在 Cursor 中可以测试一下。

“SSE模式配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "mcpServers": {
      "mcp-php-server-http": {
         "url": "http://127.0.0.1:8201/mcp/sse"
      }
  }
}

配置成功后,会有绿色的小圆点,代表服务已经配置成功。

在对话界面,在输入框下方工具中,点击@Add context符号,选配置的mcp.json选项。

调用简单工具方法如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[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 实现。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MCP 介绍
  • 在Cursor 中体验 MCP
    • 服务简介
    • 创建APIKey
    • 配置 MCP Server 服务
    • 地图 MCP 应用
  • MCP 的架构
  • 在 PHP 中使用 Mcp Server
    • 服务端实现
    • 基于 SSE 的 MCP 服务端实现
    • 安装依赖
    • 实现 MCP 工具
    • 配置 MCP 服务端
    • 运行服务端
    • 在 Cursor 中测试 mcp 服务
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档