antidrift / zeromcp
Zero-config MCP runtime for PHP
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Drop a .php file in a folder, get a sandboxed MCP server. Stdio out of the box, zero dependencies.
Getting started
<?php // tools/hello.php — this is a complete MCP server return [ 'description' => 'Say hello to someone', 'input' => ['name' => 'string'], 'execute' => function ($args, $ctx) { return "Hello, {$args['name']}!"; }, ];
php zeromcp.php serve ./tools
That's it. Stdio works immediately. Drop another .php file to add another tool. Delete a file to remove one.
vs. the official SDK
The official PHP SDK (backed by The PHP Foundation) requires Composer, server setup, transport configuration, and explicit tool registration. ZeroMCP is file-based — each tool is its own file, discovered automatically. Pure PHP, no Composer, no extensions.
In benchmarks, ZeroMCP PHP handles 13,333 requests/second over stdio versus the official SDK's 18 — 740x faster. Over HTTP (Slim), ZeroMCP serves 1,561 rps at 11-33 MB versus the official SDK's 17 rps at 31-64 MB. The official SDK takes 54ms per request. ZeroMCP takes 0.53ms. The official SDK corrupted responses on giant strings and slow tools in chaos testing. ZeroMCP survived 22/22 attacks.
The official SDK has no sandbox. ZeroMCP lets tools declare network, filesystem, and exec permissions.
PHP passes all 10 conformance suites.
HTTP / Streamable HTTP
ZeroMCP doesn't own the HTTP layer. You bring your own framework; ZeroMCP gives you a handleRequest method that takes an associative array and returns an array (or null for notifications).
// $response = $server->handleRequest($request);
Slim
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Factory\AppFactory; $app = AppFactory::create(); $app->post('/mcp', function (Request $req, Response $res) use ($server) { $body = json_decode((string) $req->getBody(), true); $response = $server->handleRequest($body); if ($response === null) { return $res->withStatus(204); } $res->getBody()->write(json_encode($response)); return $res->withHeader('Content-Type', 'application/json'); }); $app->run();
Requirements
- PHP CLI (no extensions required)
Sandbox
<?php return [ 'description' => 'Fetch from our API', 'input' => ['url' => 'string'], 'permissions' => [ 'network' => ['api.example.com', '*.internal.dev'], 'fs' => false, 'exec' => false, ], 'execute' => function ($args, $ctx) { // ... }, ];
Directory structure
Tools are discovered recursively. Subdirectory names become namespace prefixes:
tools/
hello.php -> tool "hello"
math/
add.php -> tool "math_add"
Testing
php tests/run.php