К основному содержанию
правило официальный · GitHub

php-mcp-server — правило для GitHub Copilot: суть и установка

Инструкция для GitHub Copilot по созданию серверов Model Context Protocol на PHP 8.2+. Помогает ассистенту размечать методы атрибутами и собирать проект через официальный SDK.

Установить в GitHub Copilot · Вручную: положить файлы Все способы и платформы ↓
.github/instructions/php-mcp-server.instructions.md
Открыть на GitHub

Что делает php-mcp-server

Правило инструктирует Copilot писать серверы MCP по стандарту официального PHP SDK, созданного совместно с The PHP Foundation. Ассистент ориентируется на структуру проекта с каталогами Tools, Resources и Prompts, а также генерирует корректную конфигурацию composer.json для пакета mcp/sdk.

Код строится вокруг PHP-атрибутов. Copilot автоматически размечает методы через #[McpTool] и #[McpResource], добавляет валидацию параметров через #[Schema] и конфигурирует точку входа через Server::builder(). При необходимости генерация учитывает транспорт StdioTransport и кеширование PSR-16 для быстрого сканирования классов.

Для работы сервера требуется PHP не ниже версии 8.2. Без установленного пакета mcp/sdk сгенерированный код выполняться не будет. Правило применяется ко всем файлам по маске **/*.php.

Какие задачи решает

  • спроектировать файловую структуру MCP-сервера на PHP
  • объявить инструменты через атрибут #[McpTool] со схемой валидации
  • настроить автообнаружение инструментов и ресурсов с PSR-16 кешем
  • реализовать отдачу данных по шаблонам URI через #[McpResourceTemplate]

Примеры запросов агенту

  • Создай инструмент MCP для чтения локальных файлов с проверкой существования пути
  • Напиши входную точку server.php со StdioTransport и сканированием каталога src/Tools
  • Добавь ресурс config://app/settings для отдачи настроек приложения в JSON

Как установить php-mcp-server

Вручную: положить файлы

.github/instructions/php-mcp-server.instructions.md

Поле applyTo задаёт, к каким файлам применять

По шагам, с командами для каждой платформы: AGENTS.md и CLAUDE.md: что это, кто читает и какой выбрать

Вопросы о php-mcp-server

Как установить php-mcp-server в GitHub Copilot?

Добавьте файл правила в свой репозиторий по пути .github/instructions/php-mcp-server.instructions.md. После этого Copilot начнёт учитывать эти инструкции при работе с файлами **/*.php.

Что делает правило php-mcp-server?

Оно задаёт правила генерации PHP-кода для Model Context Protocol. Copilot начинает использовать актуальный синтаксис официального SDK с атрибутами вроде #[McpTool] вместо ручной сборки схем.

Какие зависимости нужны для работы сгенерированного кода?

Понадобится PHP версии 8.2 или новее и пакет mcp/sdk, установленный через Composer. Для ускорения обнаружения классов также поддерживается кеш стандарта PSR-16.

Исходный текст

php-mcp-server.instructions.md · MIT
---
description: 'Best practices for building Model Context Protocol servers in PHP using the official PHP SDK with attribute-based discovery and multiple transport options'
applyTo: '**/*.php'
---

# PHP MCP Server Development Best Practices

This guide provides best practices for building Model Context Protocol (MCP) servers using the official PHP SDK maintained in collaboration with The PHP Foundation.

## Installation and Setup

### Install via Composer

```bash
composer require mcp/sdk
```

### Project Structure

Organize your PHP MCP server project:

```
my-mcp-server/
├── composer.json
├── src/
│   ├── Tools/
│   │   ├── Calculator.php
│   │   └── FileManager.php
│   ├── Resources/
│   │   ├── ConfigProvider.php
│   │   └── DataProvider.php
│   ├── Prompts/
│   │   └── PromptGenerator.php
│   └── Server.php
├── server.php           # Server entry point
└── tests/
    └── ToolsTest.php
```

### Composer Configuration

```json
{
    "name": "your-org/mcp-server",
    "description": "MCP Server for...",
    "type": "project",
    "require": {
        "php": "^8.2",
        "mcp/sdk": "^0.1"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
```

## Server Implementation

### Basic Server with Attribute Discovery

Create your server entry point:

```php
#!/usr/bin/env php
<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;

$server = Server::builder()
    ->setServerInfo('My MCP Server', '1.0.0')
    ->setDiscovery(__DIR__, ['.'])
    ->build();

$transport = new StdioTransport();

$server->run($transport);
```

### Server with Caching

Use PSR-16 cache for better performance:

```php
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;

$cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery'));

$server = Server::builder()
    ->setServerInfo('My MCP Server', '1.0.0')
    ->setDiscovery(
        basePath: __DIR__,
        scanDirs: ['.', 'src'],
        excludeDirs: ['vendor', 'tests'],
        cache: $cache
    )
    ->build();
```

### Manual Registration

Register capabilities programmatically:

```php
use App\Tools\Calculator;
use App\Resources\Config;

$server = Server::builder()
    ->setServerInfo('My MCP Server', '1.0.0')
    ->addTool([Calculator::class, 'add'], 'add')
    ->addTool([Calculator::class, 'multiply'], 'multiply')
    ->addResource([Config::class, 'getSettings'], 'config://app/settings')
    ->build();
```

## Tool Development

### Simple Tool with Attribute

```php
<?php

namespace App\Tools;

use Mcp\Capability\Attribute\McpTool;

class Calculator
{
    /**
     * Adds two numbers together.
     * 
     * @param int $a The first number
     * @param int $b The second number
     * @return int The sum of the two numbers
     */
    #[McpTool]
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
}
```

### Tool with Custom Name

```php
use Mcp\Capability\Attribute\McpTool;

class FileManager
{
    /**
     * Reads file content from the filesystem.
     */
    #[McpTool(name: 'read_file')]
    public function readFileContent(string $path): string
    {
        if (!file_exists($path)) {
            throw new \InvalidArgumentException("File not found: {$path}");
        }
        
        return file_get_contents($path);
    }
}
```

### Tool with Validation and Schema

```php
use Mcp\Capability\Attribute\{McpTool, Schema};

class UserManager
{
    #[McpTool(name: 'create_user')]
    public function createUser(
        #[Schema(format: 'email')]
        string $email,
        
        #[Schema(minimum: 18, maximum: 120)]
        int $age,
        
        #[Schema(
            pattern: '^[A-Z][a-z]+$',
            description: 'Capitalized first name'
        )]
        string $firstName
    ): array
    {
        return [
            'id' => uniqid(),
            'email' => $email,
            'age' => $age,
            'firstName' => $firstName
        ];
    }
}
```

### Tool with Complex Return Types

```php
use Mcp\Schema\Content\{TextContent, ImageContent};

class ReportGenerator
{
    #[McpTool]
    public function generateReport(string $type): array
    {
        return [
            new TextContent('Report generated:'),
            TextContent::code($this->generateCode($type), 'php'),
            new TextContent('Summary: All checks passed.')
        ];
    }
    
    #[McpTool]
    public function getChart(string $chartType): ImageContent
    {
        $imageData = $this->generateChartImage($chartType);
        
        return new ImageContent(
            data: base64_encode($imageData),
            mimeType: 'image/png'
        );
    }
}
```

### Tool with Match Expression

```php
#[McpTool(name: 'calculate')]
public function performCalculation(float $a, float $b, string $operation): float
{
    return match($operation) {
        'add' => $a + $b,
        'subtract' => $a - $b,
        'multiply' => $a * $b,
        'divide' => $b != 0 ? $a / $b : 
            throw new \InvalidArgumentException('Division by zero'),
        default => throw new \InvalidArgumentException('Invalid operation')
    };
}
```

## Resource Implementation

### Static Resource

```php
<?php

namespace App\Resources;

use Mcp\Capability\Attribute\McpResource;

class ConfigProvider
{
    /**
     * Provides the current application configuration.
     */
    #[McpResource(
        uri: 'config://app/settings',
        name: 'app_settings',
        mimeType: 'application/json'
    )]
    public function getSettings(): array
    {
        return [
            'version' => '1.0.0',
            'debug' => false,
            'features' => ['auth', 'logging']
        ];
    }
}
```

### Resource Template with Variables

```php
use Mcp\Capability\Attribute\McpResourceTemplate;

class UserProvider
{
    /**
     * Retrieves user profile information by ID and section.
     */
    #[McpResourceTemplate(
        uriTemplate: 'user://{userId}/profile/{section}',
        name: 'user_profile',
        description: 'User profile data by section',
        mimeType: 'application/json'
    )]
    public function getUserProfile(string $userId, string $section): array
    {
        // Variable order must match URI template order
        return $this->users[$userId][$section] ?? 
            throw new \InvalidArgumentException("Profile section not found");
    }
}
```

### Resource with File Content

```php
use Mcp\Schema\Content\{TextResourceContents, BlobResourceContents};

class FileProvider
{
    #[McpResource(uri: 'file://readme.txt', mimeType: 'text/plain')]
    public function getReadme(): TextResourceContents
    {
        return new TextResourceContents(
            uri: 'file://readme.txt',
            mimeType: 'text/plain',
            text: file_get_contents(__DIR__ . '/README.txt')
        );
    }
    
    #[McpResource(uri: 'file://image.png', mimeType: 'image/png')]
    public function getImage(): BlobResourceContents
    {
        $imageData = file_get_contents(__DIR__ . '/image.png');
        
        return new BlobResourceContents(
            uri: 'file://image.png',
            mimeType: 'image/png',
            blob: base64_encode($imageData)
        );
    }
}
```

## Prompt Implementation

### Basic Prompt

```php
<?php

namespace App\Prompts;

use Mcp\Capability\Attribute\McpPrompt;

class PromptGenerator
{
    /**
     * Generates a code review request prompt.
     */
    #[McpPrompt(name: 'code_review')]
    public function reviewCode(string $language, string $code, string $focus = 'general'): array
    {
        return [
            ['role' => 'assistant', 'content' => 'You are an expert code reviewer.'],
            ['role' => 'user', 'content' => "Review this {$language} code focusing on {$focus}:\n\n```{$language}\n{$code}\n```"]
        ];
    }
}
```

### Prompt with Mixed Content

```php
use Mcp\Schema\Content\{TextContent, ImageContent};
use Mcp\Schema\PromptMessage;
use Mcp\Schema\Enum\Role;

#[McpPrompt]
public function analyzeImage(string $imageUrl, string $question): array
{
    $imageData = file_get_contents($imageUrl);
    
    return [
        new PromptMessage(Role::Assistant, [
            new TextContent('You are an image analysis expert.')
        ]),
        new PromptMessage(Role::User, [
            new TextContent($question),
            new ImageContent(
                data: base64_encode($imageData),
                mimeType: 'image/jpeg'
            )
        ])
    ];
}
```

## Completion Providers

### Value List Completion

```php
use Mcp\Capability\Attribute\{McpPrompt, CompletionProvider};

#[McpPrompt]
public function generateContent(
    #[CompletionProvider(values: ['blog', 'article', 'tutorial', 'guide'])]
    string $contentType,
    
    #[CompletionProvider(values: ['beginner', 'intermediate', 'advanced'])]
    string $difficulty
): array
{
    return [
        ['role' => 'user', 'content' => "Create a {$difficulty} level {$contentType}"]
    ];
}
```

### Enum Completion

```php
enum Priority: string
{
    case LOW = 'low';
    case MEDIUM = 'medium';
    case HIGH = 'high';
}

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
}

#[McpResourceTemplate(uriTemplate: 'tasks/{taskId}')]
public function getTask(
    string $taskId,
    
    #[CompletionProvider(enum: Priority::class)]
    string $priority,
    
    #[CompletionProvider(enum: Status::class)]
    string $status
): array
{
    return $this->tasks[$taskId] ?? [];
}
```

### Custom Completion Provider

```php
use Mcp\Capability\Prompt\Completion\ProviderInterface;

class UserIdCompletionProvider implements ProviderInterface
{
    public function __construct(
        private DatabaseService $db
    ) {}

    public function getCompletions(string $currentValue): array
    {
        return $this->db->searchUserIds($currentValue);
    }
}

#[McpResourceTemplate(uriTemplate: 'user://{userId}/profile')]
public function getUserProfile(
    #[CompletionProvider(provider: UserIdCompletionProvider::class)]
    string $userId
): array
{
    return $this->users[$userId] ?? 
        throw new \InvalidArgumentException('User not found');
}
```

## Transport Options

### Stdio Transport

For command-line integration (default):

```php
use Mcp\Server\Transport\StdioTransport;

$transport = new StdioTransport();
$server->run($transport);
```

### HTTP Transport

For web-based integration:

```php
use Mcp\Server\Transport\StreamableHttpTransport;
use Nyholm\Psr7\Factory\Psr17Factory;

$psr17Factory = new Psr17Factory();

$request = $psr17Factory->createServerRequestFromGlobals();

$transport = new StreamableHttpTransport(
    $request,
    $psr17Factory,  // Response factory
    $psr17Factory   // Stream factory
);

$response = $server->run($transport);

// Send response in your web framework
foreach ($response->getHeaders() as $name => $values) {
    foreach ($values as $value) {
        header("$name: $value", false);
    }
}

http_response_code($response->getStatusCode());
echo $response->getBody();
```

## Session Management

### In-Memory Sessions (Default)

```php
$server = Server::builder()
    ->setServerInfo('My Server', '1.0.0')
    ->setSession(ttl: 7200) // 2 hours
    ->build();
```

### File-Based Sessions

```php
use Mcp\Server\Session\FileSessionStore;

$server = Server::builder()
    ->setServerInfo('My Server', '1.0.0')
    ->setSession(new FileSessionStore(__DIR__ . '/sessions'))
    ->build();
```

### Custom Session Store

```php
use Mcp\Server\Session\InMemorySessionStore;

$server = Server::builder()
    ->setServerInfo('My Server', '1.0.0')
    ->setSession(new InMemorySessionStore(3600))
    ->build();
```

## Error Handling

### Exception Handling in Tools

```php
#[McpTool]
public function divideNumbers(float $a, float $b): float
{
    if ($b === 0.0) {
        throw new \InvalidArgumentException('Division by zero is not allowed');
    }
    
    return $a / $b;
}

#[McpTool]
public function processFile(string $filename): string
{
    if (!file_exists($filename)) {
        throw new \InvalidArgumentException("File not found: {$filename}");
    }
    
    if (!is_readable($filename)) {
        throw new \RuntimeException("File not readable: {$filename}");
    }
    
    return file_get_contents($filename);
}
```

### Custom Error Responses

The SDK automatically converts exceptions into JSON-RPC error responses that MCP clients understand.

## Testing

### PHPUnit Tests for Tools

```php
<?php

namespace Tests;

use PHPUnit\Framework\TestCase;
use App\Tools\Calculator;

class CalculatorTest extends TestCase
{
    private Calculator $calculator;
    
    protected function setUp(): void
    {
        $this->calculator = new Calculator();
    }
    
    public function testAdd(): void
    {
        $result = $this->calculator->add(5, 3);
        $this->assertSame(8, $result);
    }
    
    public function testDivideByZero(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Division by zero');
        
        $this->calculator->divide(10, 0);
    }
}
```

### Testing Server Discovery

```php
public function testServerDiscoversTools(): void
{
    $server = Server::builder()
        ->setServerInfo('Test Server', '1.0.0')
        ->setDiscovery(__DIR__ . '/../src', ['.'])
        ->build();
    
    $capabilities = $server->getCapabilities();
    
    $this->assertArrayHasKey('tools', $capabilities);
    $this->assertNotEmpty($capabilities['tools']);
}
```

## Performance Best Practices

### Use Discovery Caching

Always use caching in production:

```php
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);

$cache = new Psr16Cache(new RedisAdapter($redis));

$server = Server::builder()
    ->setServerInfo('My Server', '1.0.0')
    ->setDiscovery(
        basePath: __DIR__,
        scanDirs: ['src'],
        excludeDirs: ['vendor', 'tests', 'var'],
        cache: $cache
    )
    ->build();
```

### Optimize Scan Directories

Only scan necessary directories:

```php
$server = Server::builder()
    ->setDiscovery(
        basePath: __DIR__,
        scanDirs: ['src/Tools', 'src/Resources'],  // Specific dirs
        excludeDirs: ['vendor', 'tests', 'var', 'cache']
    )
    ->build();
```

### Use OPcache

Enable OPcache in production for better PHP performance:

```ini
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
```

## Framework Integration

### Laravel Integration

```php
// app/Console/Commands/McpServer.php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;

class McpServer extends Command
{
    protected $signature = 'mcp:serve';
    protected $description = 'Start MCP server';
    
    public function handle()
    {
        $server = Server::builder()
            ->setServerInfo('Laravel MCP Server', '1.0.0')
            ->setDiscovery(app_path(), ['Tools', 'Resources'])
            ->build();
        
        $transport = new StdioTransport();
        $server->run($transport);
    }
}
```

### Symfony Integration

```php
// Use symfony/mcp-bundle for native integration
composer require symfony/mcp-bundle
```

## Deployment

### Docker Deployment

```dockerfile
FROM php:8.2-cli

# Install extensions
RUN docker-php-ext-install pdo pdo_mysql

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Set working directory
WORKDIR /app

# Copy application
COPY . /app

# Install dependencies
RUN composer install --no-dev --optimize-autoloader

# Make server executable
RUN chmod +x /app/server.php

CMD ["php", "/app/server.php"]
```

### Systemd Service

```ini
[Unit]
Description=MCP PHP Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/mcp-server
ExecStart=/usr/bin/php /var/www/mcp-server/server.php
Restart=always

[Install]
WantedBy=multi-user.target
```

## Configuration for MCP Clients

### Claude Desktop Configuration

```json
{
  "mcpServers": {
    "php-server": {
      "command": "php",
      "args": ["/absolute/path/to/server.php"]
    }
  }
}
```

### MCP Inspector Testing

```bash
npx @modelcontextprotocol/inspector php /path/to/server.php
```

## Additional Resources

- [Official PHP SDK Repository](https://github.com/modelcontextprotocol/php-sdk)
- [MCP Elements Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/mcp-elements.md)
- [Server Builder Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/server-builder.md)
- [Transport Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/transports.md)
- [Examples](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/examples.md)
- [MCP Specification](https://spec.modelcontextprotocol.io/)
- [Model Context Protocol](https://modelcontextprotocol.io/)

© github · MIT · github/awesome-copilot

Похожие правила IDE

Все правила IDE по теме «Настройка агента: память, контекст, субагенты» →

Из того же набора861

Весь набор →
playwright-typescript Правило задаёт Copilot стиль написания тестов Playwright: ролевые локаторы, автоожидание, единая структура файлов. Пригодится, если тесты пишет агент, а не человек. ★ 40 418звёзд репо playwright-python Задаёт стандарты генерации тестов на Playwright Python в Pytest. Помогает Copilot писать надёжные проверки через expect и ролевые локаторы без хардкодных пауз. ★ 40 418звёзд репо playwright-dotnet Правило задаёт структуру и практики для тестов Playwright на C#. Пригодится, когда Copilot пишет или дорабатывает UI-тесты. ★ 40 418звёзд репо php-symfony Правило задаёт Copilot стиль разработки на Symfony: структура проекта, DI, Doctrine, Twig и тесты. Пригодится, если не хочется объяснять агенту конвенции в каждом чате. ★ 40 418звёзд репо performance-optimization Правило помогает находить причины проблем с Core Web Vitals и выбирать исправления для веб-приложения. Пригодится при работе над загрузкой, рендерингом и сдвигами макета. ★ 40 418звёзд репо pcf-tooling Правило подсказывает Copilot, что для сборки, отладки и деплоя код-компонентов нужен Microsoft Power Platform CLI. ★ 40 418звёзд репо pcf-sample-components Направляет GitHub Copilot при сборке, инициализации и тестировании готовых компонентов Power Apps Component Framework из официального репозитория примеров Microsoft. ★ 40 418звёзд репо pcf-react-platform-libraries Правило помогает создавать React-компоненты PCF с общими библиотеками React и Fluent на платформе Power Apps. Оно пригодится при разработке виртуальных контролов для canvas и model-driven apps. ★ 40 418звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой