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.
composer require php-mcp/server
The simplest way to create an MCP server is using attribute-based discovery with the stdio transport:
<?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);
}
}
#!/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);
}
.cursor/mcp.json
):{
"mcpServers": {
"php-calculator": {
"command": "php",
"args": ["/absolute/path/to/your/mcp-server.php"]
}
}
}
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)
);
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();
The SDK supports multiple transports:
Best for direct client execution:
use PhpMcp\Server\Transports\StdioServerTransport;
$transport = new StdioServerTransport();
$server->listen($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);
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);
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;
}
// 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)
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();
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}"]];
}
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;
}
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/;
}
}
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.
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.
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"
]
}
}
}
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.
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.
To add this MCP server to Claude Desktop:
1. Find your configuration file:
~/Library/Application Support/Claude/claude_desktop_config.json
%APPDATA%\Claude\claude_desktop_config.json
~/.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