PHP MCP MCP server

PHP implementation that enables PHP applications to expose methods as tools, prompts, and resources through attribute-based annotations with support for multiple transport handlers.
Back to servers
Setup instructions
Provider
Kyrian Obikwelu
Release date
Mar 02, 2025
Language
PHP
Stats
546 stars

The PHP MCP Server SDK enables your PHP applications to communicate with AI assistants using the Model Context Protocol (MCP) standard. It transforms your backend functionality into standardized tools, resources, and prompts that AI models can interact with directly.

Installation

composer require php-mcp/server

Getting Started

Quick Setup with Stdio Transport

The simplest way to create an MCP server is using attribute-based discovery with the stdio transport:

  1. Define your MCP elements with attributes:
<?php

namespace App;

use PhpMcp\Server\Attributes\McpTool;

class CalculatorElements
{
    /**
     * Adds two numbers together.
     */
    #[McpTool(name: 'add_numbers')]
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
    
    /**
     * Calculates power with validation.
     */
    #[McpTool(name: 'calculate_power')]
    public function power(float $base, int $exponent): float
    {
        return pow($base, $exponent);
    }
}
  1. Create your server script:
#!/usr/bin/env php
<?php

declare(strict_types=1);

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

use PhpMcp\Server\Server;
use PhpMcp\Server\Transports\StdioServerTransport;

try {
    // Build server configuration
    $server = Server::make()
        ->withServerInfo('PHP Calculator Server', '1.0.0') 
        ->build();

    // Discover MCP elements via attributes
    $server->discover(
        basePath: __DIR__,
        scanDirs: ['src']
    );

    // Start listening via stdio transport
    $transport = new StdioServerTransport();
    $server->listen($transport);

} catch (\Throwable $e) {
    fwrite(STDERR, "[CRITICAL ERROR] " . $e->getMessage() . "\n");
    exit(1);
}
  1. Configure your MCP client (e.g., in .cursor/mcp.json):
{
    "mcpServers": {
        "php-calculator": {
            "command": "php",
            "args": ["/absolute/path/to/your/mcp-server.php"]
        }
    }
}

Defining MCP Elements

Using Attributes (Recommended)

The SDK provides attributes to define different MCP elements:

use PhpMcp\Server\Attributes\{McpTool, McpResource, McpResourceTemplate, McpPrompt};

class UserManager
{
    /**
     * Creates a new user account.
     */
    #[McpTool(name: 'create_user')]
    public function createUser(string $email, string $password, string $role = 'user'): array
    {
        // Create user logic
        return ['id' => 123, 'email' => $email, 'role' => $role];
    }

    /**
     * Get user configuration.
     */
    #[McpResource(
        uri: 'config://user/settings',
        mimeType: 'application/json'
    )]
    public function getUserConfig(): array
    {
        return ['theme' => 'dark', 'notifications' => true];
    }

    /**
     * Get user profile by ID.
     */
    #[McpResourceTemplate(
        uriTemplate: 'user://{userId}/profile',
        mimeType: 'application/json'
    )]
    public function getUserProfile(string $userId): array
    {
        return ['id' => $userId, 'name' => 'John Doe'];
    }
}

After defining elements with attributes, discover them:

$server->discover(
    basePath: __DIR__,
    scanDirs: ['src/Handlers', 'src/Services'],  // Directories to scan
    excludeDirs: ['src/Tests'],                  // Directories to skip
    saveToCache: true                            // Cache results (default: true)
);

Manual Registration

You can also register elements programmatically:

use PhpMcp\Schema\ToolAnnotations;

$server = Server::make()
    ->withServerInfo('Manual Registration Server', '1.0.0')
    
    // Register a tool with handler method
    ->withTool(
        [EmailHandler::class, 'sendEmail'],     // Handler: [class, method]
        name: 'send_email',                     // Tool name (optional)
        description: 'Send email to user',      // Description (optional)
        annotations: ToolAnnotations::make(     // Annotations (optional)
            title: 'Send Email Tool'
        )
    )
    
    // Register a closure as tool
    ->withTool(
        function(int $a, int $b): int {         // Handler: Closure
            return $a + $b;
        },
        name: 'add_numbers',
        description: 'Add two numbers together'
    )
    
    ->build();

Transport Options

The SDK supports multiple transports:

Stdio Transport

Best for direct client execution:

use PhpMcp\Server\Transports\StdioServerTransport;

$transport = new StdioServerTransport();
$server->listen($transport);

HTTP Transport

Best for web integration:

use PhpMcp\Server\Transports\HttpServerTransport;

$transport = new HttpServerTransport(
    host: '127.0.0.1',      
    port: 8080,             
    mcpPathPrefix: 'mcp'    // URL prefix (/mcp/sse, /mcp/message)
);

$server->listen($transport);

Streamable HTTP Transport (Recommended for Production)

use PhpMcp\Server\Transports\StreamableHttpServerTransport;

$transport = new StreamableHttpServerTransport(
    host: '127.0.0.1',      
    port: 8080,
    mcpPathPrefix: 'mcp',
    enableJsonResponse: false,  // Use SSE streaming (default)
    stateless: false            // Enable stateless mode for session-less clients
);

$server->listen($transport);

Schema Enhancement

Use the #[Schema] attribute to enhance parameter validation:

use PhpMcp\Server\Attributes\{McpTool, Schema};

#[McpTool(name: 'validate_user')]
public function validateUser(
    #[Schema(format: 'email')]              // Validate as email format
    string $email,
    
    #[Schema(pattern: '^[A-Z][a-z]+$')]     // Validate pattern
    string $name,
    
    #[Schema(minimum: 18, maximum: 120)]    // Validate range
    int $age
): bool {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

Advanced Configuration

Session Management

// In-memory sessions (default, not persistent)
->withSession('array', 3600)

// Cache-backed sessions (persistent across restarts)  
->withSession('cache', 7200)

// Custom session handler
->withSessionHandler(new MyCustomSessionHandler(), 1800)

Dependency Injection

use Psr\Container\ContainerInterface;

// Use a PSR-11 compatible container for dependencies
$container = new \DI\Container();
$container->set(\PDO::class, new \PDO('mysql:host=localhost;dbname=app', $user, $pass));

$server = Server::make()
    ->withContainer($container)  // Handlers get dependencies auto-injected
    ->build();

Completion Providers

Help users complete parameters with the #[CompletionProvider] attribute:

use PhpMcp\Server\Attributes\{McpPrompt, CompletionProvider};

#[McpPrompt(name: 'content_generator')]
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}"]];
}

Error Handling

Tool handlers can throw exceptions that will be properly formatted as JSON-RPC errors:

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

Production Deployment

For production deployment, use Supervisor to manage the process and Nginx as a reverse proxy:

# /etc/supervisor/conf.d/mcp-server.conf
[program:mcp-server]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/mcp-server/server.php --transport=http --host=127.0.0.1 --port=8080
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/mcp-server.log
# Nginx configuration
server {
    listen 443 ssl http2;
    server_name mcp.yourdomain.com;

    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/mcp.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mcp.yourdomain.com/privkey.pem;
    
    location / {
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # Important for SSE connections
        proxy_buffering off;
        proxy_cache off;
        
        proxy_pass http://127.0.0.1:8080/;
    }
}

How to install this MCP server

For Claude Code

To add this MCP server to Claude Code, run this command in your terminal:

claude mcp add-json "php-calculator" '{"command":"php","args":["/absolute/path/to/your/mcp-server.php"]}'

See the official Claude Code MCP documentation for more details.

For Cursor

There are two ways to add an MCP server to Cursor. The most common way is to add the server globally in the ~/.cursor/mcp.json file so that it is available in all of your projects.

If you only need the server in a single project, you can add it to the project instead by creating or adding it to the .cursor/mcp.json file.

Adding an MCP server to Cursor globally

To add a global MCP server go to Cursor Settings > Tools & Integrations and click "New MCP Server".

When you click that button the ~/.cursor/mcp.json file will be opened and you can add your server like this:

{
    "mcpServers": {
        "php-calculator": {
            "command": "php",
            "args": [
                "/absolute/path/to/your/mcp-server.php"
            ]
        }
    }
}

Adding an MCP server to a project

To add an MCP server to a project you can create a new .cursor/mcp.json file or add it to the existing one. This will look exactly the same as the global MCP server example above.

How to use the MCP server

Once the server is installed, you might need to head back to Settings > MCP and click the refresh button.

The Cursor agent will then be able to see the available tools the added MCP server has available and will call them when it needs to.

You can also explicitly ask the agent to use the tool by mentioning the tool name and describing what the function does.

For Claude Desktop

To add this MCP server to Claude Desktop:

1. Find your configuration file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

2. Add this to your configuration file:

{
    "mcpServers": {
        "php-calculator": {
            "command": "php",
            "args": [
                "/absolute/path/to/your/mcp-server.php"
            ]
        }
    }
}

3. Restart Claude Desktop for the changes to take effect

Want to 10x your AI skills?

Get a free account and learn to code + market your apps using AI (with or without vibes!).

Nah, maybe later