返回市场
麦克服孵化器

麦克服孵化器

作者:hyperf2 星标更新:2025-11-23

项目介绍

Hyperf MCP Server

最新稳定版本 总下载量 许可证

基于Hyperf框架实现的Model Context Protocol (MCP)服务器,提供了完整的工具、提示和资源管理能力,支持Redis会话管理和基于注解的配置。基于dtyq/php-mcp核心库构建。

特性

  • 🚀 高性能 基于Hyperf协程框架,支持高并发访问
  • 🔧 注解驱动 使用注解快速定义MCP工具、提示和资源
  • 📦 Redis会话管理 内置Redis会话管理器,支持分布式部署
  • 🎯 类型安全 完整的类型提示和JSON模式验证
  • 🔒 安全性 支持会话过期、元数据存储和访问控制
  • 🏗️ 多服务器架构 支持多个MCP服务器实例,便于功能分组和管理
  • 🎨 灵活配置 支持服务器组管理和动态启用/禁用功能

🚀 快速开始

1. 安装依赖

composer require hyperf/mcp-server-incubator

2. 注册路由

在路由文件(如config/routes.php)中添加MCP路由器:

<?php
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpServer\Router\Router;
use Hyperf\McpServer\Server;

Router::addRoute(['POST', 'GET', 'DELETE'], '/mcp', function () {
    return ApplicationContext::getContainer()->get(Server::class)->handler();
});

注意 配置提供者将被Hyperf自动加载,无需手动注册config/config.php

📝 注解注册

注册MCP工具、提示和资源最简单的方法是使用注解。此方法将根据方法签名自动生成模式并处理注册。

可用注解

#[McpTool] - 注册工具

使用 #[McpTool] 注解:将方法注册为MCP工具:

<?php
declare(strict_types=1);

namespace App\Service;

use Hyperf\McpServer\Collector\Annotations\McpTool;

class CalculatorService
{
    #[McpTool]
    public function calculate(string $operation, int $a, int $b): array
    {
        $result = match ($operation) {
            'add' => $a + $b,
            'subtract' => $a - $b,
            'multiply' => $a * $b,
            'divide' => $a / $b,
            default => null,
        };

        return [
            'operation' => $operation,
            'operands' => [$a, $b],
            'result' => $result,
        ];
    }

    #[McpTool(
        name: 'advanced_calc',
        description: '高级数学计算',
        group: 'math'
    )]
    public function advancedCalculate(string $formula, array $variables = []): float
    {
        // 复杂计算逻辑
        return 42.0;
    }
}

注解参数:

  • name: 工具名称(默认为方法名)
  • description: 工具描述
  • inputSchema 自定义输入模式(当为空时自动生成)
  • group 工具分组,用于组织
  • enabled: 启用工具(默认:true)

#[McpPrompt] - 注册提示

使用 #[McpPrompt] 注解将方法注册为提示模板:

<?php
declare(strict_types=1);

namespace App\Service;

use Dtyq\PhpMcp\Types\Prompts\GetPromptResult;
use Dtyq\PhpMcp\Types\Prompts\PromptMessage;
use Dtyq\PhpMcp\Types\Content\TextContent;
use Dtyq\PhpMcp\Types\Core\ProtocolConstants;
use Hyperf\McpServer\Collector\Annotations\McpPrompt;

class PromptService
{
    #[McpPrompt]
    public function greeting(string $name, string $language = 'chinese'): GetPromptResult
    {
        $greetings = [
            'english' => "Hello, {$name}! Welcome to the Streamable HTTP MCP server!",
            'spanish' => "¡Hola, {$name}! ¡Bienvenido al servidor MCP Streamable HTTP!",
            'french' => "Bonjour, {$name}! Bienvenue sur le serveur MCP Streamable HTTP!",
            'chinese' => "你好,{$name}!欢迎使用 Streamable HTTP MCP 服务器!",
        ];

        $message = new PromptMessage(
            ProtocolConstants::ROLE_USER,
            new TextContent($greetings[$language] ?? $greetings['chinese'])
        );

        return new GetPromptResult('问候提示', [$message]);
    }

    #[McpPrompt(
        name: 'code_review',
        description: '生成代码审查提示',
        group: 'development'
    )]
    public function codeReview(string $code, string $language = 'php'): GetPromptResult
    {
        $prompt = "请审查以下 {$language} 代码:\n\n```{$language}\n{$code}\n```\n\n请提供以下方面的反馈:\n- 代码质量\n- 最佳实践\n- 潜在改进";
        
        $message = new PromptMessage(
            ProtocolConstants::ROLE_USER,
            new TextContent($prompt)
        );

        return new GetPromptResult('代码审查提示', [$message]);
    }
}

注解参数:

  • name 提示名称(默认为方法名)
  • description: 提示描述
  • arguments 自定义参数模式(当为空时自动生成)
  • group 提示分组,用于组织
  • enabled: 启用提示(默认:true)

#[McpResource] - 注册资源

使用 #[McpResource] 注解将方法注册为资源提供者:

<?php
declare(strict_types=1);

namespace App\Service;

use Dtyq\PhpMcp\Types\Resources\TextResourceContents;
use Hyperf\McpServer\Collector\Annotations\McpResource;

class SystemService
{
    #[McpResource]
    public function systemInfo(): TextResourceContents
    {
        $info = [
            'php_version' => PHP_VERSION,
            'os' => PHP_OS,
            'memory_usage' => memory_get_usage(true),
            'timestamp' => date('c'),
            'pid' => getmypid(),
        ];

        return new TextResourceContents(
            'mcp://system/info',
            json_encode($info, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
            'application/json'
        );
    }

    #[McpResource(
        name: 'server_config',
        uri: 'mcp://system/config',
        description: '服务器配置数据',
        mimeType: 'application/json'
    )]
    public function serverConfig(): TextResourceContents
    {
        $config = [
            'environment' => env('APP_ENV', 'production'),
            'debug'  => env('APP_DEBUG', false),
            'timezone' => date_default_timezone_get(),
        ];

        return new TextResourceContents(
            'mcp://system/config',
            json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
            'application/json'
        );
    }
}

注解参数:

  • name: 资源名称(默认为方法名)
  • uri: 资源URI(当为空时自动生成)
  • description: 资源描述
  • mimeType: 资源MIME类型
  • size: 资源大小(以字节为单位)
  • group 资源分组,用于组织
  • enabled: 启用资源(默认:true)
  • isTemplate 是否为模板资源
  • uriTemplate: URI模板参数

自动模式生成

注解系统将根据方法签名自动生成一个JSON模式:

#[McpTool]
public function processUser(
    string $userId,           // 必需的字符串参数
    int $age = 18,           // 可选的整数参数,有默认值
    bool $active = true,     // 可选的布尔参数,有默认值
    array $tags = []         // 可选的数组参数,默认为空数组
): array {
    // 实现代码
}

这将生成以下模式:

{
    "type": "object",
    "properties": {
        "userId": {
            "type": "string",
            "description": "Parameter: userId"
        },
        "age": {
            "type": "integer",
            "description": "Parameter: age",
            "default": 18
        },
        "active": {
            "type": "boolean",
            "description": "Parameter: active",
            "default": true
        },
        "tags": {
            "type": "array",
            "description": "Parameter: tags",
            "items": {"type": "string"},
            "default": []
        }
    },
    "required": ["userId"]
}

支持的类型:

PHP 类型JSON Schema 类型
string"type": "string"
int, integer"type": "integer"
float, double"type": "number"
bool, boolean"type": "boolean"
array"type": "array"

注意:复杂类型(类、接口、联合类型)不支持。自动模式生成仅允许基本PHP类型。

分组注册

可以使用分组来组织注解并加载特定组:

<?php

use Hyperf\Context\ApplicationContext;
use Hyperf\HttpServer\Router\Router;
use Hyperf\McpServer\Server;

// 只注册数学相关工具
Router::addRoute(['POST', 'GET', 'DELETE'], '/mcp/math', function () {
    return ApplicationContext::getContainer()->get(Server::class)->handler('math');
});

// 注册开发工具
Router::addRoute(['POST', 'GET', 'DELETE'], '/mcp/dev', function () {
    return ApplicationContext::getContainer()->get(Server::class)->handler('development');
});

// 注册所有工具(默认分组)
Router::addRoute(['POST', 'GET', 'DELETE'], '/mcp', function () {
    return ApplicationContext::getContainer()->get(Server::class)->handler();
});

完整注解示例

这是一个使用所有三种类型注解的完整服务类:

<?php

namespace App\Service;

use Dtyq\PhpMcp\Types\Prompts\GetPromptResult;
use Dtyq\PhpMcp\Types\Prompts\PromptMessage;
use Dtyq\PhpMcp\Types\Content\TextContent;
use Dtyq\PhpMcp\Types\Core\ProtocolConstants;
use Dtyq\PhpMcp\Types\Resources\TextResourceContents;
use Hyperf\McpServer\Collector\Annotations\McpTool;
use Hyperf\McpServer\Collector\Annotations\McpPrompt;
use Hyperf\McpServer\Collector\Annotations\McpResource;

class McpDemoService
{
    #[McpTool(description: '回显消息')]
    public function echo(string $message): array
    {
        return [
            'echo' => $message,
            'timestamp' => time(),
        ];
    }

    #[McpPrompt(description: '生成欢迎消息')]
    public function welcome(string $username): GetPromptResult
    {
        $message = new PromptMessage(
            ProtocolConstants::ROLE_USER,
            new TextContent("欢迎 {$username} 来到我们的 MCP 服务器!")
        );

        return new GetPromptResult('欢迎消息', [$message]);
    }

    #[McpResource(description: '当前服务器状态')]
    public function status(): TextResourceContents
    {
        $status = [
            'status' => 'healthy',
            'uptime' => time() - $_SERVER['REQUEST_TIME'],
            'memory' => round(memory_get_usage() / 1024 / 1024, 2) . ' MB',
        ];

        return new TextResourceContents(
            'mcp://server/status',
            json_encode($status, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
            'application/json'
        );
    }
}

🔧 高级配置

自定义认证

如果需要自定义认证,可以实现 AuthenticatorInterface

<?php

namespace App\Auth;

use Dtyq\PhpMcp\Shared\Auth\AuthenticatorInterface;
use Dtyq\PhpMcp\Shared\Exceptions\AuthenticationError;
use Dtyq\PhpMcp\Types\Auth\AuthInfo;
use Hyperf\HttpServer\Contract\RequestInterface;

class CustomAuthenticator implements AuthenticatorInterface
{
    public function __construct(
        protected RequestInterface $request,
    ) {
    }

    public function authenticate(): AuthInfo
    {
        $apiKey = $this->request->header('X-API-Key');
        
        // 实现您的认证逻辑
        if (!$this->validateApiKey($apiKey)) {
            throw new AuthenticationError('认证失败');
        }
        
        return AuthInfo::create(
            subject: 'user-123',
            scopes: ['read', 'write'],
            metadata: ['api_key' => $apiKey]
        );
    }
    
    private function validateApiKey(string $apiKey): bool
    {
        // 您的 API 密钥验证逻辑
        return $apiKey === 'your-secret-api-key';
    }
}

然后在配置中绑定:

// config/autoload/dependencies.php
return [
    Dtyq\PhpMcp\Shared\Auth\AuthenticatorInterface::class => App\Auth\CustomAuthenticator::class,
];

动态传输元数据管理

您可以监听 HttpTransportAuthenticatedEvent 事件来动态注册工具、资源和提示:

<?php

namespace App\Listener;

use App\Service\UserToolService;
use Dtyq\PhpMcp\Server\Transports\Http\Event\HttpTransportAuthenticatedEvent;
use Dtyq\PhpMcp\Types\Tools\Tool;
use Dtyq\PhpMcp\Types\Resources\Resource;
use Dtyq\PhpMcp\Types\Prompts\Prompt;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Psr\Container\ContainerInterface;

#[Listener]
class DynamicMcpResourcesListener implements ListenerInterface
{
    public function __construct(
        protected ContainerInterface $container,
    ) {
    }

    public function listen(): array
    {
        return [
            HttpTransportAuthenticatedEvent::class,
        ];
    }

    public function process(object $event): void
    {
        if (!$event instanceof HttpTransportAuthenticatedEvent) {
            return;
        }

        $transportMetadata = $event->getTransportMetadata();
        $authInfo = $event->getAuthInfo();

        // 获取认证用户信息
        $user = $authInfo->getMetadata('user');
        $permissions = $authInfo->getMetadata('permissions', []);

        // 动态注册工具
        $this->registerDynamicTools($transportMetadata, $user, $permissions);
        
        // 动态注册资源
        $this->registerDynamicResources($transportMetadata, $user, $permissions);
        
        // 动态注册提示
        $this->registerDynamicPrompts($transportMetadata, $user, $permissions);
    }

    private function registerDynamicTools($transportMetadata, $user, array $permissions): void
    {
        $toolManager = $transportMetadata->getToolManager();
        
        // 根据用户权限注册不同的工具
        if (in_array('user_management', $permissions)) {
            $userTool = new Tool('get_user_info', [
                'type' => 'object',
                'properties' => [
                    'user_id' => ['type' => 'integer'],
                ],
                'required' => ['user_id'],
            ], '获取用户信息');
            
            $toolManager->register($userTool, function(array $args) use ($user) {
                // 实现工具逻辑
                return $this->container->get(UserToolService::class)->getUserInfo($args['user_id'], $user);
            });
        }

        if (in_array('admin', $permissions)) {
            $adminTool = new Tool('admin_operation', [
                'type' => 'object',
                'properties' => [
                    'action' => ['type' => 'string'],
                    'target' => ['type' => 'string'],
                ],
                'required' => ['action'],
            ], '执行管理员操作');
            
            $toolManager->register($adminTool, function(array $args) {
                // 管理员专用工具逻辑
                return ['result' => "Admin action: {$args['action']}"];
            });
        }
    }

    private function registerDynamicResources($transportMetadata, $user, array $permissions): void
    {
        $resourceManager = $transportMetadata->getResourceManager();
        
        // 根据权限注册资源
        if (in_array('read_users', $permissions)) {
            $usersResource = new Resource('users', 'application/json', '用户列表');
            $resourceManager->register($usersResource, function() use ($user) {
                // 返回用户有权限访问的用户列表
                return json_encode(['users' => ['Alice', 'Bob']]);
            });
        }

        if (in_array('read_reports', $permissions)) {
            $reportsResource = new Resource('reports', 'application/json', '报告数据');
            $resourceManager->register($reportsResource, function() {
                return json_encode(['reports' => ['report1', 'report2']]);
            });
        }
    }

    private function registerDynamicPrompts($transportMetadata, $user, array $permissions): void
    {
        $promptManager = $transportMetadata->getPromptManager();
        
        // 根据用户角色注册提示模板
        if (in_array('content_creator', $permissions)) {
            $contentPrompt = new Prompt('create_content', [
                'type' => 'object',
                'properties' => [
                    'topic' => ['type' => 'string'],
                    'style' => ['type' => 'string'],
                ],
                'required