muxtorov98 / yii2-mcp
Model Context Protocol (MCP) server integration for Yii2 applications.
Requires
- php: ^8.1
- ext-json: *
- yiisoft/yii2: ^2.0
README
MCP server integration for Yii2 applications on PHP 8.1+.
This package exposes Yii2 classes as MCP tools and MCP resources so AI agents can discover and call them through a standard JSON-RPC endpoint.
Package: muxtorov98/yii2-mcp
What This Package Does
- exposes Yii2 classes as MCP
tools - exposes read-only content as MCP
resources - generates JSON Schema from PHP classes and Yii models
- supports Yii2 validation for tool input
- provides an HTTP MCP endpoint for AI clients
What This Package Does Not Do
This package is an MCP server. It is not an LLM provider SDK.
That means:
yii2-mcpdoes not choose OpenAI, Claude, Gemini, or any other modelyii2-mcpdoes not send prompts to an LLM by itself- the AI client chooses the model
Typical setup:
Yii2 + yii2-mcp= MCP serverChatGPT / Claude Desktop / Cursor / other agent= MCP client- model selection = inside the client
If you also want Yii2 itself to call an LLM, add a separate AI client component such as OpenAiClient, AnthropicClient, or your own adapter.
Requirements
- PHP
^8.1 - Yii2
^2.0 ext-json
Install
composer require muxtorov98/yii2-mcp
How It Works
- You create PHP classes and mark them with
#[McpTool]or#[McpResource]. - The
mcpcomponent scans configured folders. - The package builds MCP metadata and input schemas.
- AI clients connect to your
/mcpendpoint. - The client lists tools/resources and calls them when needed.
Minimal Working Setup
1. Configure the mcp component
Example for common/config/main.php or another shared config:
<?php return [ 'components' => [ 'mcp' => [ 'class' => Muxtorov98\YiiMcp\McpServer::class, 'discover' => [ [ 'path' => '@common/mcp', 'namespace' => 'common\\mcp', ], ], 'serverInfo' => [ 'name' => 'my-yii2-mcp', 'title' => 'My Yii2 MCP Server', 'version' => '0.1.0', ], ], ], ];
Notes:
pathmust point to the folder containing your MCP classesnamespacemust match those PHP classes- for web apps, the package bootstrap automatically registers
mcp/index
Production-ready config example
If you want to run this package in a more serious environment, enable authentication, rate limiting, cache, and logging explicitly:
<?php return [ 'components' => [ 'cache' => [ 'class' => yii\caching\FileCache::class, ], 'mcp' => [ 'class' => Muxtorov98\YiiMcp\McpServer::class, 'discover' => [ [ 'path' => '@common/mcp', 'namespace' => 'common\\mcp', ], ], 'serverInfo' => [ 'name' => 'my-yii2-mcp', 'title' => 'My Yii2 MCP Server', 'version' => '1.0.0', ], 'authEnabled' => true, 'apiKeys' => [ getenv('MCP_API_KEY'), ], 'bearerTokens' => [ getenv('MCP_BEARER_TOKEN'), ], 'enableRateLimit' => true, 'rateLimit' => 60, 'rateWindow' => 60, 'cacheComponent' => 'cache', 'enableRegistryCache' => true, 'registryCacheKey' => 'my_app_mcp_registry', 'registryCacheTtl' => 3600, 'logRequests' => true, 'logResults' => false, 'exposeInternalErrors' => YII_DEBUG, ], ], ];
This package now includes:
- API key or Bearer token auth
- cache-backed rate limiting
- registry/schema caching through Yii cache
- request and error logging
- safer internal error masking for production
Optional custom controller mapping
If you want your own controller namespace or route, map the package controller explicitly in your app config:
<?php return [ 'controllerMap' => [ 'mcp' => [ 'class' => Muxtorov98\YiiMcp\Controller\McpController::class, ], ], ];
If you want a project-specific controller, extend the package controller:
File: frontend/controllers/McpController.php
<?php namespace frontend\controllers; final class McpController extends \Muxtorov98\YiiMcp\Controller\McpController { }
Then map it:
<?php return [ 'controllerMap' => [ 'mcp' => [ 'class' => frontend\controllers\McpController::class, ], ], ];
2. Create the folder for MCP classes
Example:
common/mcp/
3. Create a tool input class
File: common/mcp/SearchBooksInput.php
<?php namespace common\mcp; use Muxtorov98\YiiMcp\Attributes\McpTool; use yii\base\Model; #[McpTool( name: 'search_books', description: 'Search books by title', handler: SearchBooksHandler::class, validate: true )] final class SearchBooksInput extends Model { public ?string $query = null; public ?int $limit = 10; public function rules(): array { return [ [['query'], 'required'], [['query'], 'string', 'min' => 2], [['limit'], 'integer', 'min' => 1, 'max' => 50], ]; } }
4. Create the tool handler
File: common/mcp/SearchBooksHandler.php
<?php namespace common\mcp; final class SearchBooksHandler { public function handle(SearchBooksInput $input): array { $rows = [ ['id' => 1, 'title' => 'Domain-Driven Design'], ['id' => 2, 'title' => 'Patterns of Enterprise Application Architecture'], ['id' => 3, 'title' => 'Clean Architecture'], ]; $query = mb_strtolower($input->query ?? ''); $filtered = array_values(array_filter( $rows, static fn (array $row): bool => str_contains(mb_strtolower($row['title']), $query) )); return array_slice($filtered, 0, $input->limit ?? 10); } }
5. Create a resource
File: common/mcp/ApiDocsResource.php
<?php namespace common\mcp; use Muxtorov98\YiiMcp\Attributes\McpResource; use Muxtorov98\YiiMcp\Results\ResourceContents; #[McpResource( uri: 'resource://my-app/api-docs', name: 'API Docs', description: 'Internal API documentation', provider: ApiDocsProvider::class, mimeType: 'text/markdown' )] final class ApiDocsResource { }
File: common/mcp/ApiDocsProvider.php
<?php namespace common\mcp; use Muxtorov98\YiiMcp\Results\ResourceContents; final class ApiDocsProvider { public function provide(): ResourceContents { return new ResourceContents( uri: 'resource://my-app/api-docs', mimeType: 'text/markdown', text: "# API Docs\n\nThis resource is exposed through MCP." ); } }
6. Start your Yii2 web app
Your MCP endpoint will be available at:
/mcp/index
If you use pretty URLs and route rules, you can map it to /mcp.
Example pretty URL rule:
<?php return [ 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ 'POST mcp' => 'mcp/index', ], ], ], ];
AI Model Selection
This is the part most people mix up.
You do not set the AI model inside yii2-mcp.
You set the model inside the MCP client:
- ChatGPT app or OpenAI-based client
- Claude Desktop
- Cursor
- custom MCP client
Example logic:
- client connects to
https://your-domain.com/mcp/index - client reads
tools/list - client decides when to call
search_books - client uses its own selected model to reason over the results
If You Also Want Yii2 To Call OpenAI
That is separate from MCP. Add your own AI component.
Example config:
<?php return [ 'components' => [ 'ai' => [ 'class' => app\components\ai\OpenAiClient::class, 'apiKey' => getenv('OPENAI_API_KEY'), 'model' => 'gpt-4.1-mini', ], 'mcp' => [ 'class' => Muxtorov98\YiiMcp\McpServer::class, 'discover' => [ [ 'path' => '@common/mcp', 'namespace' => 'common\\mcp', ], ], ], ], ];
In this setup:
aicomponent calls the LLMmcpcomponent exposes tools/resources to external AI agents
These are different responsibilities.
Supported MCP Methods
The HTTP JSON-RPC endpoint currently supports:
initializepingtools/listtools/callresources/listresources/read
JSON-RPC Examples
Initialize
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}
List Tools
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
Call Tool
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "search_books",
"arguments": {
"query": "design",
"limit": 2
}
}
}
List Resources
{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/list",
"params": {}
}
Read Resource
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/read",
"params": {
"uri": "resource://my-app/api-docs"
}
}
Testing With curl
List tools
curl -X POST http://localhost/mcp/index \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "id":1, "method":"tools/list", "params":{} }'
Call a tool
curl -X POST http://localhost/mcp/index \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "id":2, "method":"tools/call", "params":{ "name":"search_books", "arguments":{ "query":"design", "limit":2 } } }'
Tool Declaration Rules
#[McpTool(...)] options:
name: MCP tool namedescription: human-readable descriptionhandler: handler class nameinput: optional separate input DTO classoutput: optional output DTO classvalidate: run Yii validation if input extendsyii\base\ModelstructuredContent: includestructuredContentin resultmeta: arbitrary metadata
Example:
#[McpTool(
name: 'create_order',
description: 'Create a new order',
handler: CreateOrderHandler::class,
validate: true
)]
Resource Declaration Rules
#[McpResource(...)] options:
uri: unique resource identifiername: display namedescription: human-readable descriptionprovider: provider class namemimeType: resource MIME typestructuredContent: reserved for future usemeta: arbitrary metadata
Example:
#[McpResource(
uri: 'resource://my-app/policies',
name: 'Policies',
provider: PoliciesProvider::class,
mimeType: 'text/markdown'
)]
Validation
Validation is only applied when:
validate: trueis set on the tool- the input class extends
yii\base\Model
Example:
public function rules(): array { return [ [['email'], 'required'], [['email'], 'email'], [['age'], 'integer', 'min' => 18], ]; }
If validation fails, the MCP response returns an error payload.
Input Hydration
Tool input is hydrated by:
- public property assignment
setXxx()setter methods
Example:
$arguments = [ 'query' => 'design', ];
This can be mapped to:
$input->query$input->setQuery('design')
Response Shape
Tool responses are normalized into JSON-friendly arrays.
Regular tool result:
{
"content": [
{
"type": "text",
"text": "{\n \"id\": 1\n}"
}
],
"structuredContent": {
"id": 1
},
"isError": false
}
Resource result:
{
"contents": [
{
"uri": "resource://my-app/api-docs",
"mimeType": "text/markdown",
"text": "# API Docs"
}
]
}
Folder Layout Suggestion
common/
mcp/
SearchBooksInput.php
SearchBooksHandler.php
ApiDocsResource.php
ApiDocsProvider.php
Current Scope
This package currently provides:
- HTTP-based MCP endpoint
- attribute-based tool/resource discovery
- schema generation
- validation support
- basic tool/resource invocation
This package does not yet provide:
- stdio transport
- OAuth/authentication layer
- advanced MCP session management
- streaming responses
- provider SDK integration
- automatic OpenAPI or Hydra conversion
Troubleshooting
Tools do not appear in tools/list
Check:
discover.pathpoints to the correct folderdiscover.namespacematches the PHP namespace- classes are autoloadable
- class has
#[McpTool]
Validation is not running
Check:
- tool has
validate: true - input class extends
yii\base\Model rules()is defined
Handler is not called
Check:
handlerclass name is correct- handler has
handle($input)method or is invokable - dependency injection can build the class
Resource is not returned
Check:
uriis unique- provider class is valid
- provider has
provide()method or is invokable
Unauthorized response
Check:
authEnabledis enabled intentionally- request sends
Authorization: Bearer ...orX-API-Key: ... - provided token exists in
apiKeysorbearerTokens
Rate limit exceeded
Check:
enableRateLimitis enabled- cache component is configured
rateLimitandrateWindowvalues fit your traffic
Development Notes
This package is currently an MVP skeleton. It is suitable as a starting point for a real Yii2 MCP package and can be extended with:
- authentication
- provider integrations
- stdio bridge
- richer schema customization
- MCP protocol upgrades