Building a Model Context Protocol (MCP) Server with PHP 8 Attributes
This guide explains how to use the php-mcp/server library to create a standards‑compliant Model Context Protocol (MCP) server in PHP 8, covering installation, key features, attribute‑based definitions, configuration, transport options, discovery, and code examples for a simple stdio server.
Overview
The php-mcp/server package is a PHP 8 library for building Model Context Protocol (MCP) servers. It lets you expose PHP methods as MCP tools, resources, or prompts using native attributes.
Key Features
Attribute‑based definitions : Use PHP 8 attributes such as #[McpTool], #[McpResource], #[McpPrompt], #[McpResourceTemplate] and #[McpTemplate] to declare MCP elements.
Automatic metadata inference : Method names, parameter types and doc‑blocks are parsed to generate MCP schemas.
PSR compliance : Works with PSR‑3 (logger), PSR‑11 (container) and PSR‑16 (cache) interfaces.
Flexible configuration : Replace logger, cache, container or configuration repository.
Multiple transports : Supports stdio for CLI and http+sse for HTTP Server‑Sent Events.
Automatic discovery : Scans configured directories for classes and methods annotated with MCP attributes.
Framework‑agnostic : Works in plain PHP projects or any framework.
Requirements
PHP ≥ 8.1
Composer
Installation
composer require php-mcp/serverFor Laravel projects, use the php-mcp/laravel-server package, which builds on this core library.
Simple stdio Server Example
1. Define a class with MCP attributes:
<?php
namespace App;
use PhpMcp\\Server\\Attributes\\McpTool;
class MyMcpStuff {
/**
* A simple tool that adds two numbers.
* @param int $a First number.
* @param int $b Second number.
* @return int Sum of the numbers.
*/
#[McpTool(name: 'adder')]
public function addNumbers(int $a, int $b): int {
return $a + $b;
}
}2. Create a server script (e.g., mcp-server.php) and configure it:
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use PhpMcp\\Server\\Server;
$server = Server::make()
->withBasePath(__DIR__)
->withScanDirectories(['src'])
->discover();
$exitCode = $server->run('stdio');
exit($exitCode);3. Configure the client (e.g., Cursor) to launch the script via .cursor/mcp.json:
{
"mcpServers": {
"my-php-server": {
"command": "php",
"args": ["/absolute/path/to/mcp-server.php"]
}
}
}When the client connects, the adder tool becomes available.
Core Concepts
#[McpTool] : Marks a method as an executable tool. Optional name and description parameters override defaults derived from the method signature and doc‑block.
#[McpResource] : Declares a static resource identified by a RFC 3986 URI.
#[McpResourceTemplate] : Declares a URI template (RFC 6570) for resources with variable parts.
#[McpPrompt] : Generates prompt content based on method parameters.
Server Fluent Interface
Server::make()– create a server instance. ->withLogger(LoggerInterface $logger) – inject a PSR‑3 logger (default: StreamLogger to STDERR). ->withCache(CacheInterface $cache) – inject a PSR‑16 cache (default: in‑memory ArrayCache). ->withContainer(ContainerInterface $container) – inject a PSR‑11 container for dependency resolution. ->withConfig(ConfigurationRepositoryInterface $config) – supply a custom configuration repository. ->withBasePath(string $path) – set the base directory for discovery (default: getcwd()). ->withScanDirectories(array $dirs) – list sub‑directories to scan (default: ['.']). ->discover(bool $clearCacheFirst = true) – scan for attributes, build definitions and cache them. ->run(?string $transport = null) – start the main loop. 'stdio' uses standard input/output; 'http' requires integration with an HTTP server.
Discovery Process
The discover() method walks the directories set by withScanDirectories(), reflects each class, and extracts metadata from MCP attributes, method signatures and doc‑blocks. It creates definition objects (e.g., ToolDefinition, ResourceDefinition) and registers them in a cache for faster subsequent starts.
Dependency Injection
When a client invokes a tool or reads a resource, the server uses the provided PSR‑11 container to instantiate the target class, allowing constructor injection of services such as database connections. The default BasicContainer works only for classes without dependencies; a real application should supply a framework‑specific container.
Configuration
Implement PhpMcp\\Server\\Contracts\\ConfigurationRepositoryInterface or use the default ArrayConfigurationRepository. Important keys (dot notation) include: mcp.server.name – server name for handshake. mcp.server.version – server version. mcp.protocol_versions – supported protocol versions (e.g., ['2024-11-05']). mcp.pagination_limit – default limit for listing elements. mcp.capabilities.*.enabled – toggle tools, resources, prompts, logging, etc. mcp.cache.ttl and mcp.cache.prefix – cache lifetime and prefix. mcp.discovery.base_path and mcp.discovery.directories – override discovery paths. mcp.runtime.log_level – default log level for the built‑in logger.
You can replace these settings by providing your own configuration repository implementation.
Return Value Formatting
nullor void → empty response.
Scalar types ( string, int, float, bool) → wrapped in TextContent. array or object → JSON‑encoded and wrapped in TextContent.
Instances of PhpMcp\\Server\\JsonRpc\\Contents\\Content (e.g., TextContent, ImageContent) are sent directly, allowing full control over output format.
Exceptions are caught and returned as TextContent containing the error message and type.
This summary provides the essential steps and concepts for building, configuring, and running an MCP‑compatible server in PHP using the php-mcp/server library.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
