muxtorov98/yii2-mcp

Model Context Protocol (MCP) server integration for Yii2 applications.

Maintainers

Package info

github.com/Muxtorov98/yii2-mcp

Type:yii2-extension

pkg:composer/muxtorov98/yii2-mcp

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-04-06 08:53 UTC

This package is auto-updated.

Last update: 2026-04-06 08:55:21 UTC


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-mcp does not choose OpenAI, Claude, Gemini, or any other model
  • yii2-mcp does not send prompts to an LLM by itself
  • the AI client chooses the model

Typical setup:

  • Yii2 + yii2-mcp = MCP server
  • ChatGPT / 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

  1. You create PHP classes and mark them with #[McpTool] or #[McpResource].
  2. The mcp component scans configured folders.
  3. The package builds MCP metadata and input schemas.
  4. AI clients connect to your /mcp endpoint.
  5. 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:

  • path must point to the folder containing your MCP classes
  • namespace must 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:

  • ai component calls the LLM
  • mcp component exposes tools/resources to external AI agents

These are different responsibilities.

Supported MCP Methods

The HTTP JSON-RPC endpoint currently supports:

  • initialize
  • ping
  • tools/list
  • tools/call
  • resources/list
  • resources/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 name
  • description: human-readable description
  • handler: handler class name
  • input: optional separate input DTO class
  • output: optional output DTO class
  • validate: run Yii validation if input extends yii\base\Model
  • structuredContent: include structuredContent in result
  • meta: 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 identifier
  • name: display name
  • description: human-readable description
  • provider: provider class name
  • mimeType: resource MIME type
  • structuredContent: reserved for future use
  • meta: arbitrary metadata

Example:

#[McpResource(
    uri: 'resource://my-app/policies',
    name: 'Policies',
    provider: PoliciesProvider::class,
    mimeType: 'text/markdown'
)]

Validation

Validation is only applied when:

  • validate: true is 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.path points to the correct folder
  • discover.namespace matches 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:

  • handler class name is correct
  • handler has handle($input) method or is invokable
  • dependency injection can build the class

Resource is not returned

Check:

  • uri is unique
  • provider class is valid
  • provider has provide() method or is invokable

Unauthorized response

Check:

  • authEnabled is enabled intentionally
  • request sends Authorization: Bearer ... or X-API-Key: ...
  • provided token exists in apiKeys or bearerTokens

Rate limit exceeded

Check:

  • enableRateLimit is enabled
  • cache component is configured
  • rateLimit and rateWindow values 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