monkeyscloud / monkeyslegion-mcp
Production-grade MCP (Model Context Protocol) server and client for PHP 8.4 with attribute-based tool registration, stdio + Streamable HTTP transports, and JSON-RPC 2.0 engine.
1.0.0
2026-05-23 03:20 UTC
Requires
- php: ^8.4
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/log: ^3.0
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
- ext-curl: Required for the MCP HTTP client transport
- monkeyscloud/monkeyslegion-mlc: For MLC configuration file support
- monkeyscloud/monkeyslegion-router: For PSR-15 McpMiddleware integration
This package is auto-updated.
Last update: 2026-05-23 05:29:52 UTC
README
Production-grade Model Context Protocol (MCP) server and client for PHP 8.4+.
MonkeysLegion ships native MCP support — the first PHP framework to do so.
Features
- JSON-RPC 2.0 engine with full batch support
- Three MCP primitives: Tools, Resources, and Prompts
- Attribute-based discovery:
#[McpTool],#[McpResource],#[McpPrompt],#[McpParam] - Two transports: Stdio (local AI tools) and Streamable HTTP (remote)
- PSR-15 Middleware: Drop-in HTTP integration for any PSR-15 framework
- DI Provider: Bootstrap the full MCP stack from MLC configuration
- MCP Client: HTTP and Stdio clients for connecting to external MCP servers
- Schema generation from PHP method signatures via reflection
- Schema validation for tool arguments (types, enums, ranges)
- Protocol negotiation:
2024-11-05and2025-03-26 - Session management via
MCP-Session-Idheader - PSR-3/PSR-7/PSR-15 integration
Installation
composer require monkeyscloud/monkeyslegion-mcp
Requirements
- PHP 8.4+
psr/log ^3.0psr/http-message ^2.0psr/http-server-handler ^1.0psr/http-server-middleware ^1.0ext-curl(for HTTP client transport, optional)
Quick Start
1. Create an MCP Server
<?php use MonkeysLegion\Mcp\Server\McpServer; use MonkeysLegion\Mcp\Protocol\ServerInfo; $server = new McpServer(new ServerInfo('MyApp', '1.0.0'));
2. Register Tools
$server->addTool( name: 'calculate', description: 'Perform arithmetic calculations', inputSchema: [ 'type' => 'object', 'properties' => [ 'operation' => ['type' => 'string', 'enum' => ['add', 'sub', 'mul', 'div']], 'a' => ['type' => 'number'], 'b' => ['type' => 'number'], ], 'required' => ['operation', 'a', 'b'], ], handler: function (array $args): float { return match ($args['operation']) { 'add' => $args['a'] + $args['b'], 'sub' => $args['a'] - $args['b'], 'mul' => $args['a'] * $args['b'], 'div' => $args['b'] != 0 ? $args['a'] / $args['b'] : throw new \DivisionByZeroError(), }; }, );
3. Attribute-Based Registration (Recommended)
<?php use MonkeysLegion\Mcp\Attributes\McpTool; use MonkeysLegion\Mcp\Attributes\McpParam; use MonkeysLegion\Mcp\Attributes\McpResource; use MonkeysLegion\Mcp\Attributes\McpPrompt; use MonkeysLegion\Mcp\Prompt\PromptDefinition; class AppTools { /** * Search the database for records matching a query. */ #[McpTool(name: 'search', description: 'Search records')] public function search( #[McpParam(description: 'Search query string')] string $query, #[McpParam(description: 'Max results to return', required: false)] int $limit = 10, ): array { // Your search logic here return ['results' => [], 'total' => 0]; } #[McpResource(uri: 'app://config', name: 'config', mimeType: 'application/json')] public function getConfig(array $params = []): string { return json_encode(['env' => 'production', 'debug' => false]); } #[McpPrompt(name: 'code_review', description: 'Review code for best practices')] public function codeReview(array $params = []): PromptDefinition { return new PromptDefinition( name: 'code_review', description: 'Review code for best practices', arguments: [ 'language' => ['description' => 'Programming language', 'required' => true], ], messages: [ ['role' => 'system', 'content' => ['type' => 'text', 'text' => 'You are a {language} expert.']], ['role' => 'user', 'content' => ['type' => 'text', 'text' => 'Review this code for best practices.']], ], ); } }
Scan and register:
use MonkeysLegion\Mcp\Discovery\AttributeScanner; AttributeScanner::scan($server, [AppTools::class]); // or scan an entire directory: AttributeScanner::scanDirectory($server, __DIR__ . '/app/Mcp', 'App\\Mcp');
4. Choose a Transport
Stdio (Local AI Tools — Claude Desktop, etc.)
use MonkeysLegion\Mcp\Transport\StdioTransport; $transport = new StdioTransport(); $transport->start($server);
Streamable HTTP (Remote)
use MonkeysLegion\Mcp\Transport\StreamableHttpTransport; $transport = new StreamableHttpTransport(); // In a route handler or controller: $transport->handleRequest($server);
Or use it directly in a controller:
// POST /mcp $body = file_get_contents('php://input'); $headers = ['MCP-Protocol-Version' => $_SERVER['HTTP_MCP_PROTOCOL_VERSION'] ?? '']; $response = $server->handleJson($body, $headers); foreach ($server->responseHeaders() as $name => $value) { header("{$name}: {$value}"); } echo $response;
PSR-15 Middleware (Recommended for Frameworks)
use MonkeysLegion\Mcp\Middleware\McpMiddleware; // Mount on your middleware stack — handles POST, GET, DELETE on /mcp $middleware = new McpMiddleware($server, '/mcp'); // Or use the Provider for zero-config setup: use MonkeysLegion\Mcp\Provider\McpProvider; $provider = new McpProvider($config['mcp'] ?? []); $middleware = $provider->middleware(); // Auto-wired with server + discovery $server = $provider->server(); // Singleton, pre-scanned $transport = $provider->transport(); // stdio or http based on config
5. Resources & Templates
use MonkeysLegion\Mcp\Resource\ResourceTemplate; // Static resource $server->addResource( name: 'readme', uri: 'file:///README.md', content: file_get_contents('README.md'), mimeType: 'text/markdown', ); // Dynamic resource template $server->addResourceTemplate( new ResourceTemplate('db://users/{id}', 'user', 'application/json', 'Fetch a user'), fn(array $params) => json_encode(User::find($params['id'])), );
MCP Client
HTTP Client
use MonkeysLegion\Mcp\Client\McpClient; use MonkeysLegion\Mcp\Client\ClientConfig; $client = new McpClient(new ClientConfig( serverUrl: 'https://example.com/mcp', timeout: 30, )); // Initialize session $client->initialize(); // List and call tools $tools = $client->listTools(); $result = $client->callTool('search', ['query' => 'PHP 8.4']); // Resources and Prompts $resources = $client->listResources(); $content = $client->readResource('file:///README.md'); $prompts = $client->listPrompts(); $prompt = $client->getPrompt('code_review', ['language' => 'PHP']);
Stdio Client (Subprocess)
use MonkeysLegion\Mcp\Client\StdioClient; $client = new StdioClient('php mcp-server.php'); $client->start(); $client->initialize(); $tools = $client->listTools(); $result = $client->callTool('calculate', ['operation' => 'add', 'a' => 3, 'b' => 5]); $client->stop();
MLC Configuration
# config/mcp.mlc mcp: server: name: "MyApp-MCP" version: "1.0.0" transport: "stdio" # stdio | streamable-http endpoint: "/mcp" # HTTP endpoint (streamable-http only) protocol_version: "2025-03-26" scan: directories: - path: "app/Mcp" namespace: "App\\Mcp" logging: enabled: true level: "debug"
Claude Desktop Integration
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"my-app": {
"command": "php",
"args": ["/path/to/your/mcp-server.php"]
}
}
}
Create mcp-server.php:
<?php require __DIR__ . '/vendor/autoload.php'; use MonkeysLegion\Mcp\Server\McpServer; use MonkeysLegion\Mcp\Protocol\ServerInfo; use MonkeysLegion\Mcp\Discovery\AttributeScanner; use MonkeysLegion\Mcp\Transport\StdioTransport; $server = new McpServer(new ServerInfo('MyApp', '1.0.0')); // Register your tools AttributeScanner::scanDirectory($server, __DIR__ . '/app/Mcp', 'App\\Mcp'); // Start stdio transport (new StdioTransport())->start($server);
Protocol Support
| Feature | 2024-11-05 | 2025-03-26 |
|---|---|---|
| Tools | ✅ | ✅ |
| Resources | ✅ | ✅ |
| Prompts | ❌ | ✅ |
| Streamable HTTP | ❌ | ✅ |
| Session management | ❌ | ✅ |
| Stdio transport | ✅ | ✅ |
Testing
composer test
196 tests, 354 assertions covering:
- JSON-RPC 2.0 engine (messages, responses, errors)
- All MCP handlers (initialize, ping, tools, resources, prompts)
- Schema generation and validation
- Attribute scanning and discovery
- PSR-15 middleware (routing, headers, POST/GET/DELETE)
- DI provider (factories, singletons, config, discovery)
- Stdio transport with in-memory streams
- Full server integration
License
MIT — see LICENSE.