定制 rasuvaeff/yii3-mcp 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

rasuvaeff/yii3-mcp

Composer 安装命令:

composer require rasuvaeff/yii3-mcp

包简介

MCP server integration for Yii3: PSR-15 Streamable HTTP endpoint, DI tool registry, and stdio transport over the official mcp/sdk

README 文档

README

Stable Version Total Downloads Build Static analysis Psalm level PHP License

Model Context Protocol server integration for Yii3 over the official mcp/sdk (PHP Foundation + Symfony): expose your application's domain operations as MCP tools/resources for AI agents (Claude Code, Claude Desktop, …) through a PSR-15 Streamable HTTP endpoint, with tools resolved through the Yii3 DI container.

Using an AI coding assistant? llms.txt contains a compact API reference you can share with the model. Contributors: see AGENTS.md.

Requirements

Requirement Version
PHP 8.3 – 8.5
mcp/sdk ~0.6.0 (experimental until 1.0 — hence the tilde pin)
MCP protocol 2025-06-18 (via SDK)
ext-fileinfo required by the SDK

Installation

composer require rasuvaeff/yii3-mcp

Usage

1. Declare a tool

Tools are ordinary Yii3 services. Capability methods are annotated with the SDK's own attributes — this package invents no protocol structures:

use Mcp\Capability\Attribute\McpTool;

final readonly class OrderTools
{
    public function __construct(private OrderRepository $orders) {}

    /**
     * Returns the current status of an order.
     */
    #[McpTool(name: 'order.status')]
    public function status(string $orderId): string
    {
        return $this->orders->get($orderId)->status->value;
    }
}

Input schemas are generated by the SDK from the method signature and DocBlock. #[McpResource], #[McpResourceTemplate] and #[McpPrompt] methods work the same way — all four SDK capability attributes are recognized.

To gate a capability class (feature flag, environment check), implement ConditionalToolInterface — the instance is resolved through the container at build time and skipped when shouldRegister() returns false:

final readonly class BetaTools implements ConditionalToolInterface
{
    public function __construct(private FeatureFlags $flags) {}

    public function shouldRegister(): bool
    {
        return $this->flags->isEnabled('mcp-beta-tools');
    }

    #[McpTool(name: 'beta.op')]
    public function betaOp(): string { ... }
}

2. Register it

// config/params.php
return [
    'rasuvaeff/yii3-mcp' => [
        'server_name' => 'my-app',
        'server_version' => '1.0.0',
        'tools' => [OrderTools::class],
        'endpoint_secret' => getenv('MCP_SECRET'),
    ],
];

Handlers are registered as [class, method] references — the SDK resolves the instance through the Yii3 container on call, so constructor dependencies are injected the normal way.

3. Route the endpoint

// config/routes.php
Route::methods(['POST', 'GET', 'DELETE', 'OPTIONS'], '/mcp')
    ->middleware(SharedSecretMiddleware::class)
    ->action(McpAction::class),

An MCP client connects with the secret header:

{
    "mcpServers": {
        "my-app": {
            "type": "http",
            "url": "https://example.com/mcp",
            "headers": { "X-Mcp-Secret": "..." }
        }
    }
}

stdio for local development

// add McpServeCommand to your console commands
./yii mcp:serve

Claude Code config: claude mcp add my-app -- ./yii mcp:serve.

Sessions (important for PHP-FPM)

The MCP Streamable HTTP session spans several HTTP requests (initialize first, then tools/call with the returned Mcp-Session-Id). The SDK's default in-memory store would lose the session between FPM workers, so this package defaults to a file-based store (sys_get_temp_dir(), override via session.dir param). For multi-host setups rebind the interface:

// config/common/di/mcp.php
use Mcp\Server\Session\Psr16SessionStore;
use Mcp\Server\Session\SessionStoreInterface;

return [
    SessionStoreInterface::class => static fn (CacheInterface $cache) =>
        new Psr16SessionStore($cache),
];

Prompts from Markdown files

Prompts are content, not code — keep them in a directory and every *.md file becomes an MCP prompt (edited without a deployment, versioned like any other file):

'rasuvaeff/yii3-mcp' => [
    'prompts_path' => __DIR__ . '/../resources/prompts',
],
---
name: code-review          # defaults to the file name
title: Code review assistant
description: Reviews a diff with a given focus
arguments:
  - name: diff
    description: The diff to review
    required: true
  - focus                  # simple form: optional argument
---
Review the following diff focusing on {{focus}}:

{{diff}}

Declared {{argument}} placeholders are substituted from the request (missing ones become empty strings); undeclared placeholders are left intact. Malformed frontmatter, an unreadable file or a duplicate prompt name fail the server build with Prompts\Exception\InvalidPromptFileException — never a silently missing prompt.

The file format is intentionally compatible with — and inspired by — vjik/my-prompts-mcp by Sergei Predvoditelev: the same prompt file works in a personal stdio prompt manager and on an application server.

OpenAPI bridge: expose an existing REST API

If the application already maintains an OpenAPI document, allow-listed operations can be bridged as MCP tools with zero duplication — names come from operationId, descriptions from summary/description, input schemas from parameters/request body. Calls are executed as real HTTP requests against the API, passing its full middleware stack (validation, rate limiting, auth) — unlike hand-written tools that invoke handlers directly.

// config/params.php
'rasuvaeff/yii3-mcp' => [
    'openapi' => [
        // file path OR http(s) URL — e.g. the app's own spec endpoint,
        // always current; fetched with the same `headers` (auth included)
        'spec_path' => 'https://api.example.com/rest/json-url',
        'base_url' => 'https://api.example.com',
        'operations' => ['getBlogTags', 'getPage'],   // allow-list, empty = nothing
        'headers' => ['Authorization' => 'Bearer ' . getenv('MCP_API_TOKEN')],
        'safe_methods_only' => true,   // read-only bridge: non-GET in the list => build error
    ],
],

The DI wiring requires PSR-18/PSR-17 services (ClientInterface, RequestFactoryInterface, StreamFactoryInterface) in the container. Request bodies are passed as a single body tool argument; an operationId missing from the document throws at server build time (fail-fast).

For custom scenarios use the pieces directly: SpecIndex + HttpOperationExecutor + OpenApiServerConfigurator (a ServerConfiguratorInterface — the generic extension point accepted by McpServerFactory::create(tools, configurators)).

Components

Class Role
McpServerFactory list of tool FQCNs → configured SDK Server (reads #[McpTool]/#[McpResource] attributes, wires the DI container and session store)
McpAction PSR-15 handler running the SDK StreamableHttpTransport for the current request
SharedSecretMiddleware fail-closed hash_equals() guard; an empty secret rejects every request with an explanatory 503 — an unprotected endpoint must be an explicit decision
McpServeCommand mcp:serve — stdio transport for local MCP clients
Exception\InvalidToolClassException configured tool class missing or without capability attributes (fail-fast)
ConditionalToolInterface capability class opts out of registration at build time (shouldRegister())
Testing\McpTester in-process test client: initialize/listTools/callTool/readResource
Prompts\MarkdownPromptsConfigurator a directory of *.md files as MCP prompts (vjik/my-prompts-mcp-compatible format)
ServerConfiguratorInterface generic extension point for contributing capabilities to the builder
OpenApi\OpenApiServerConfigurator bridges allow-listed OpenAPI operations as tools (HTTP execution)
OpenApi\Exception\* InvalidSpecException, UnknownOperationException, OperationFailedException

Security

  • The endpoint is trusted-only. MCP tools execute application code; treat the endpoint like an admin API. Ship it behind SharedSecretMiddleware (an empty secret rejects every request with an explanatory 503) or an explicit network ACL.
  • Tool errors are returned as MCP error envelopes by the SDK — internals are not leaked as 500 traces.
  • The core registers no tools by default; every exposed operation is an explicit entry in params['rasuvaeff/yii3-mcp']['tools'].
  • OAuth from the MCP authorization spec is deliberately out of scope until it stabilizes; shared-secret/ACL only.

Examples

See examples/ for a runnable script.

Script Shows Needs server?
http-handshake.php Full in-process MCP cycle: initialize + tools/call no

Testing your tools

Testing\McpTester drives the real Streamable HTTP code path in-process — no HTTP server, no stdio process:

$tester = new McpTester($server, $psr17, $psr17, $psr17);

$result = $tester->callTool('order.status', ['orderId' => '42']);
$this->assertSame('paid', $result['content'][0]['text']);

$tester->listTools();                 // tool definitions
$tester->readResource('app://x');     // resource contents
$tester->request('prompts/list');     // any raw JSON-RPC method

For interactive debugging use the official MCP Inspector:

npx @modelcontextprotocol/inspector
# transport: Streamable HTTP, URL: https://your-app/rest/mcp,
# header: X-Mcp-Secret: <secret>

Development

No PHP/Composer on the host — run in Docker via the composer:2 image:

docker run --rm -v "$PWD":/app -w /app composer:2 composer build

Or with Make: make build, make cs-fix, make psalm, make test.

License

BSD-3-Clause. See LICENSE.md.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 4
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: BSD-3-Clause
  • 更新时间: 2026-07-04

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固