vielhuber / simplemcp
Simple php mcp server with auto-discovery and totp auth.
Requires
- php: >=8.3
- vlucas/phpdotenv: *
README
🐘 simplemcp 🐘
simplemcp is a simple php mcp server. it auto-discovers tool classes via reflection, loads them from a directory you point it at, and authenticates requests via a static bearer token or rotating totp codes (rfc 6238).
installation
composer require vielhuber/simplemcp
configuration
php -r '$b="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";$r=random_bytes(20);$bits="";for($i=0;$i<20;$i++){$bits.=str_pad(decbin(ord($r[$i])),8,"0",STR_PAD_LEFT);}$s="";for($i=0;$i+5<=strlen($bits);$i+=5){$s.=$b[bindec(substr($bits,$i,5))];}echo "MCP_TOKEN=".$s.PHP_EOL;' > .env
authentication
static mode
Authorization: Bearer <MCP_TOKEN>
totp mode
MCP_TOKEN is a base32-encoded shared secret (rfc 6238). the bearer is a fresh 6-digit totp code (30-second window, ±1 step tolerance). the server implements the algorithm natively, no extra library needed. on the client (python/fastmcp):
$authorization_token = (static fn($h) => (static fn($o) => str_pad(((ord($h[$o]) & 0x7f) << 24 | (ord($h[$o+1]) & 0xff) << 16 | (ord($h[$o+2]) & 0xff) << 8 | ord($h[$o+3]) & 0xff) % 1_000_000, 6, '0', STR_PAD_LEFT))(ord($h[19]) & 0xf))( (static fn($key) => hash_hmac('sha1', pack('N*', 0) . pack('N*', (int) floor(time() / 30)), $key, true))( (static fn($bits) => implode('', array_map(fn($i) => chr(bindec(substr($bits, $i, 8))), range(0, strlen($bits) - 8, 8))))( implode('', array_map(fn($c) => str_pad(decbin(strpos('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $c)), 5, '0', STR_PAD_LEFT), str_split(strtoupper($_ENV['MCP_TOKEN'])))) ) ));
usage
configure the constructor at the bottom of mcp-server.php:
require __DIR__ . '/vendor/autoload.php'; use vielhuber\simplemcp\simplemcp; new simplemcp( name: 'my-mcp-server', log: 'mcp-server.log', discovery: '.', auth: 'static', // 'static'|'totp' env: '.env' );
adding tools
note: simplemcp uses the same
#[McpTool]and#[Schema]attribute syntax asphp-mcp/server. existing tool classes can be migrated by replacinguse PhpMcp\Server\Attributes\McpTool;withuse vielhuber\simplemcp\Attributes\McpTool;(and the same forSchema).
use vielhuber\simplemcp\Attributes\McpTool; use vielhuber\simplemcp\Attributes\Schema; class MyTools { /** * Returns the sum of two numbers. * * @return int */ #[McpTool(name: 'add', description: 'Returns the sum of two numbers.')] public function add(int $a, int $b): int { return $a + $b; } /** * Greets a person by name. * * @return string */ #[McpTool] public function greet( #[Schema(type: 'string', description: 'The name to greet.')] string $name ): string { return "Hello, {$name}!"; } }
mcp server
http mode (recommended for remote servers)
{
"mcpServers": {
"simplemcp": {
"url": "https://example.com/mcp-server.php",
"headers": {
"Authorization": "Bearer <MCP_TOKEN>"
}
}
}
}
stdio mode (local, via php cli, no auth needed)
{
"mcpServers": {
"simplemcp": {
"command": "/usr/bin/php",
"args": ["/path/to/project/mcp-server.php"]
}
}
}
apache configuration
# forward Authorization header RewriteEngine on RewriteBase / CGIPassAuth On RewriteCond %{HTTP:Authorization} ^(.*) RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] # block public access to .env <Files ".env"> Require all denied </Files>