dv-team / mcp-server
Basic and easy to use PHP based MCP Server
Installs: 4
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/dv-team/mcp-server
Requires
- php: >= 8.2
- psr/log: >= 1.0
Requires (Dev)
- phpstan/phpstan: >= 0.1
- phpunit/phpunit: ^10.5
- roave/security-advisories: dev-latest
- spatie/yaml-front-matter: ^2.1
README
This project demonstrates a simple PHP-based MCP Server for handling processes that require near-instant responses (as no support for streaming is intended to get a simpler interface). It is designed for flexibility and can be adapted to various environments, such as database schema retrieval, prompt output, filesystem tasks, and more.
High-Level Goals
- Simple PHP and PHP-esque MCP Server for processes that can respond (near) immediately.
- HTTP and STDIN samples for quick testing; no streaming support.
- Versatile usage: Suitable for a wide range of environments, including database, prompt handling, and filesystem operations.
Requirements
- PHP 8.2 or higher
- Composer
Installation
composer install
Usage
HTTP server (main)
php -S 127.0.0.1:8080 public/index.php
Use test.http to exercise the JSON-RPC methods against the running server.
Example servers
HTTP sample that loads prompts/tools from examples/prompts and exposes demo tools:
php -S 127.0.0.1:8080 examples/src/prompts-and-tools-http.php
STDIN sample that logs requests/responses to examples/src/stdio–mcp.log:
php examples/src/prompts-and-tools-cli.php < request.json
Example: Registering Prompts
The server uses YAML front-matter to parse Markdown files and register them as prompts, as shown in examples/src/prompts-and-tools-http.php:
$files = [ __DIR__ . '/../prompts/email--basic-rules.md', __DIR__ . '/../prompts/behaviour--basic-rules.md' ]; $responseHandler = new HttpResponseHandler(); $server = new MCPServer('Prompt provider example', $responseHandler); foreach($files as $file) { $document = YamlFrontMatter::parseFile($file); /** @var string $name */ $name = $document->matter('name'); /** @var string $prompt */ $prompt = $document->matter('prompt'); /** @var string $description */ $description = $document->matter('description'); $server->registerPrompt( name: $name, description: $prompt, arguments: new MCPPromptArguments(), handler: function () use ($description, $document) { return new MCPPromptResult( description: $description, messages: [new PromptResultStringMessage( role: RoleEnum::User, content: $document->body() )] ); } ); }
Example: Registering Tools
Here are three simple tools you might register:
$server->registerTool( name: 'echo_text', description: 'Echoes back the provided text.', inputSchema: new MCPToolInputSchema( properties: new MCPToolProperties( new MCPToolString(name: 'text', description: 'Text to echo', required: true), ) ), isDangerous: false, handler: static function (object $input): MCPToolResult { return new MCPToolResult( content: (object) ['echo' => (string) ($input->text ?? '')], isError: false ); } ); $server->registerTool( name: 'sum_numbers', description: 'Adds two integers together.', inputSchema: new MCPToolInputSchema( properties: new MCPToolProperties( new MCPToolInteger(name: 'a', description: 'First addend', required: true), new MCPToolInteger(name: 'b', description: 'Second addend', required: true), ) ), isDangerous: false, handler: static function (object $input): MCPToolResult { $sum = (int) ($input->a ?? 0) + (int) ($input->b ?? 0); return new MCPToolResult( content: ['sum' => $sum], isError: false ); } ); $server->registerTool( name: 'send_email', description: 'Send an email', inputSchema: new MCPToolInputSchema( properties: new MCPToolProperties( new MCPToolString(name: 'to', description: 'The recipient email address', required: true), new MCPToolString(name: 'cc', description: 'The cc-recipient email address', required: false), new MCPToolString(name: 'from', description: 'The sender email address', required: true), new MCPToolString(name: 'subject', description: 'The subject of the email', required: true), new MCPToolString(name: 'body', description: 'The body of the email', required: true), ), required: [] ), isDangerous: true, handler: static function (object $input): MCPToolResult { $json = json_encode($input, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); error_log($json); return new MCPToolResult( content: ['result' => 'Email queued!'], isError: false ); } );
Example: Attribute-Based Tools
You can mark methods with attributes and let the AttributeToolRegistrar build the MCP tool definitions for you:
use McpSrv\Common\Attributes\MCPDescription; use McpSrv\Common\Attributes\MCPTool; use McpSrv\Common\Tools\AttributeToolRegistrar; use McpSrv\MCPServer; class MathTools { #[MCPTool( name: 'add_numbers', description: 'Adds two integers together.', parametersSchema: [ 'type' => 'object', 'properties' => [], // left empty; types/required flags are inferred from parameter signatures ], returnSchema: [ 'type' => 'object', 'properties' => [ 'sum' => ['type' => 'integer', 'description' => 'Result of the addition', 'required' => true], ], 'required' => ['sum'], ], )] public function add( #[MCPDescription('First addend')] int $a, #[MCPDescription('Second addend')] int $b ): array { return ['sum' => $a + $b]; } } $server = new MCPServer('attribute-sample', $responseHandler); $registrar = new AttributeToolRegistrar(); $registrar->register(new MathTools(), $server);
Descriptions on parameters are copied into the generated schema; when a property is not supplied in parametersSchema, the registrar infers the JSON Schema type and required flag from the method signature.
Notes
- This server is intended for environments where immediate responses are required (no streaming support).
- For broader protocol coverage, including streaming, see
logiscape/mcp-sdk-php.
License
MIT License