vluzrmos / mcp
MCP Server implementation for PHP 5.6+
Requires
- php: >=5.6
- ext-curl: *
- ext-json: *
- vluzrmos/jsonrpc: 0.0.1
Requires (Dev)
- phpunit/phpunit: ^5.7
This package is auto-updated.
Last update: 2026-03-14 13:30:52 UTC
README
A Model Context Protocol (MCP) server implementation for PHP 5.6+, built on top of a JSON-RPC 2.0 foundation (vluzrmos/jsonrpc). It enables AI models to interact with PHP-based tools through the standardized MCP protocol.
Table of Contents
- Requirements
- Installation
- Quick Start
- Architecture
- Creating Tools
- Creating Resources
- Creating Prompts
- Content Types
- Sessions & Login
- HTTP & Server-Sent Events
- Capabilities
- Pagination
- API Reference
- Testing
- Docker
- License
Requirements
- PHP >= 5.6
jsonextensioncurlextension
Installation
composer require vluzrmos/mcp
Quick Start
Minimal MCP Server
<?php require 'vendor/autoload.php'; use Vluzrmos\MCP\Server; use Vluzrmos\MCP\ServerInfo; use Vluzrmos\MCP\Capability; use Vluzrmos\MCP\Tools\ToolManager; use Vluzrmos\MCP\Tools\AbstractTool; use Vluzrmos\MCP\Tools\ToolResult; use Vluzrmos\MCP\Utils\JsonSchema; use Vluzrmos\MCP\Methods\ServerInitialize; use Vluzrmos\MCP\Methods\ToolsList; use Vluzrmos\MCP\Methods\ToolsCall; use Vluzrmos\MCP\Methods\ResourcesList; use Vluzrmos\MCP\Methods\ResourcesRead; use Vluzrmos\MCP\Methods\PromptsList; use Vluzrmos\MCP\Methods\PromptsGet; use Vluzrmos\MCP\Methods\Ping; use Vluzrmos\MCP\Methods\NotificationsInitialized; use Vluzrmos\MCP\Resources\ResourceManager; use Vluzrmos\MCP\Prompts\PromptManager; // 1. Create managers and register tools, resources, prompts $toolManager = new ToolManager(); $toolManager->registerTool(new MyTool()); $resourceManager = new ResourceManager(); $resourceManager->registerResource(new MyResource()); $promptManager = new PromptManager(); $promptManager->registerPrompt(new MyPrompt()); // 2. Create the server $server = new Server(new ServerInfo('My MCP Server', '1.0.0')); $server->addCapability(Capability::tools()); $server->addCapability(Capability::resources()); $server->addCapability(Capability::prompts()); // 3. Register MCP methods $server->addMethod(new ServerInitialize($server)); $server->addMethod(new NotificationsInitialized()); $server->addMethod(new Ping()); $server->addMethod(new ToolsList($toolManager)); $server->addMethod(new ToolsCall($toolManager)); $server->addMethod(new ResourcesList($resourceManager)); $server->addMethod(new ResourcesRead($resourceManager)); $server->addMethod(new PromptsList($promptManager)); $server->addMethod(new PromptsGet($promptManager)); // 4. Process request $input = file_get_contents('php://input'); $response = $server->handle($input); header('Content-Type: application/json'); echo json_encode($response);
CLI Usage
$server = new Server(new ServerInfo('CLI Server', '1.0.0')); // ... register methods ... // Send a JSON-RPC request directly $response = $server->handle('{"jsonrpc":"2.0","method":"initialize","id":1}'); echo json_encode($response, JSON_PRETTY_PRINT);
Architecture
Server
├── JsonRpcServer (vluzrmos/jsonrpc)
├── ServerInfo
├── SessionManagerInterface → SessionManager / LaravelSessionManager → Session
├── Capability → CapabilityOptions
└── Methods:
├── ServerInitialize ← Server
├── ToolsList ← ToolManager
├── ToolsCall ← ToolManager → Tool → ToolResult → Content
├── ResourcesList ← ResourceManager
├── ResourcesRead ← ResourceManager → Resource → Contents
├── PromptsList ← PromptManager
├── PromptsGet ← PromptManager → Prompt → PromptMessage → Content
├── Ping
└── NotificationsInitialized
HTTP Layer:
├── Response ← HeaderBag
├── JsonResponse ← Response
├── BinaryFileResponse ← Response
└── Stream (SSE) ← Response ← Payload
Content Types:
├── Text ← Content
├── Image ← Base64Encoded ← Content
├── Audio ← Base64Encoded ← Content
├── ResourceLink ← Content
├── EmbeddedResource ← ResourceLink
└── Annotations
Resources:
├── Resource (interface) ← Concerns
├── AbstractResource ← Resource
└── ResourceManager (registry)
Prompts:
├── Prompt (interface) ← Concerns
├── AbstractPrompt ← Prompt
├── PromptArgument
├── PromptMessage
└── PromptManager (registry)
Core Components
| Component | Description |
|---|---|
Server |
Main MCP server — wraps JSON-RPC |
Tools/ |
Tool system with AbstractTool base class and ToolManager |
Resources/ |
Resource system with AbstractResource base class and ResourceManager |
Prompts/ |
Prompt system with AbstractPrompt, PromptArgument, PromptMessage and PromptManager |
Methods/ |
JSON-RPC method implementations for the MCP protocol |
Content/ |
Content types (Text, Image, Audio, ResourceLink, etc.) |
Http/ |
HTTP layer with JSON responses and Server-Sent Events |
Session / SessionManagerInterface |
MCP session management (swappable implementations) |
Creating Tools
Tools are the main extension point of the MCP server. Extend AbstractTool to create one:
use Vluzrmos\MCP\Tools\AbstractTool; use Vluzrmos\MCP\Tools\ToolResult; use Vluzrmos\MCP\Utils\JsonSchema; class SearchTool extends AbstractTool { public function getName() { return 'search'; } public function getTitle() { return 'Search Tool'; } public function getDescription() { return 'Performs a search with the given query.'; } public function getInputSchema() { return new JsonSchema('object', [ 'query' => ['type' => 'string', 'description' => 'Search term'], 'limit' => ['type' => 'integer', 'description' => 'Max number of results'] ], ['query']); // 'query' is required } public function getOutputSchema() { return new JsonSchema('object', [ 'results' => ['type' => 'array'], 'total' => ['type' => 'integer'] ]); } public function execute(array $arguments) { $query = $arguments['query']; $limit = isset($arguments['limit']) ? $arguments['limit'] : 10; // Your logic here... $results = performSearch($query, $limit); return ToolResult::structured([ 'results' => $results, 'total' => count($results) ]); } }
Registering Tools
$toolManager = new ToolManager(); $toolManager->registerTool(new SearchTool()); $toolManager->registerTool(new AnotherTool()); // Check registered tools $toolManager->hasTool('search'); // true $toolManager->listTools(); // ['search', 'another'] $toolManager->executeTool('search', ['query' => 'php']); // execute directly
Tool Return Types
ToolsCall automatically converts the tool's return value:
| Return | Behavior |
|---|---|
string, int, float |
Wrapped in ToolResult::content(new Text(...)) |
null |
ToolResult::content(new Text('')) |
ToolResult |
Returned as-is |
array / object with outputSchema |
ToolResult::structured(...) |
array / object without outputSchema |
ToolResult::content(new Text(json_encode(...))) |
ToolResult Factory Methods
use Vluzrmos\MCP\Tools\ToolResult; use Vluzrmos\MCP\Content\Text; // Simple content ToolResult::content(new Text('Operation result')); // Structured content (JSON) ToolResult::structured(['key' => 'value', 'data' => [1, 2, 3]]); // Error ToolResult::error('Failed to process the request');
Error Handling in Tools
use Vluzrmos\MCP\Exceptions\ToolExecutionException; class ErrorProneToool extends AbstractTool { public function execute(array $arguments) { // ToolExecutionException: message is sent to the client throw new ToolExecutionException('Invalid parameter X'); // Any other exception: generic message "Tool execution failed." throw new \RuntimeException('Internal error'); } }
Creating Resources
Resources expose data that AI models can read. Extend AbstractResource to create one:
use Vluzrmos\MCP\Resources\AbstractResource; class ServerInfoResource extends AbstractResource { public function getUri() { return 'info://server'; } public function getName() { return 'Server Info'; } public function getDescription() { return 'Returns information about the current server environment.'; } public function getMimeType() { return 'application/json'; } public function read() { return [ [ 'uri' => $this->getUri(), 'mimeType' => $this->getMimeType(), 'text' => json_encode([ 'php_version' => PHP_VERSION, 'server_time' => date('Y-m-d\TH:i:sP'), 'timezone' => date_default_timezone_get(), ]) ] ]; } }
Registering Resources
use Vluzrmos\MCP\Resources\ResourceManager; $resourceManager = new ResourceManager(); $resourceManager->registerResource(new ServerInfoResource()); // Check registered resources $resourceManager->hasResource('info://server'); // true $resourceManager->listResources(); // ['info://server'] $resourceManager->readResource('info://server'); // reads content directly
Resource Interface
The Resource interface requires:
| Method | Returns | Description |
|---|---|---|
getUri() |
string |
Unique URI identifying the resource |
getName() |
string |
Human-readable name |
getDescription() |
string|null |
Optional description |
getMimeType() |
string|null |
Optional MIME type |
read() |
array |
Array of content objects with uri, mimeType, and text or blob |
Wiring Resources to the Server
use Vluzrmos\MCP\Capability; use Vluzrmos\MCP\Methods\ResourcesList; use Vluzrmos\MCP\Methods\ResourcesRead; $server->addCapability(Capability::resources()); $server->addMethod(new ResourcesList($resourceManager)); $server->addMethod(new ResourcesRead($resourceManager));
Creating Prompts
Prompts define reusable prompt templates that AI models can discover and use. Extend AbstractPrompt to create one:
use Vluzrmos\MCP\Prompts\AbstractPrompt; use Vluzrmos\MCP\Prompts\PromptArgument; use Vluzrmos\MCP\Prompts\PromptMessage; use Vluzrmos\MCP\Content\Text; class CodeReviewPrompt extends AbstractPrompt { public function getName() { return 'code_review'; } public function getDescription() { return 'Generates a code review prompt for the given code snippet.'; } public function getArguments() { return [ new PromptArgument('code', 'The code snippet to review.', true), new PromptArgument('language', 'The programming language.', false) ]; } public function render(array $arguments) { $code = isset($arguments['code']) ? $arguments['code'] : ''; $language = isset($arguments['language']) ? $arguments['language'] : 'unknown'; return [ PromptMessage::user(new Text( "Please review the following {$language} code:\n\n```{$language}\n{$code}\n```" )), PromptMessage::assistant(new Text( "I'll analyze the {$language} code you provided." )) ]; } }
Registering Prompts
use Vluzrmos\MCP\Prompts\PromptManager; $promptManager = new PromptManager(); $promptManager->registerPrompt(new CodeReviewPrompt()); // Check registered prompts $promptManager->hasPrompt('code_review'); // true $promptManager->listPrompts(); // ['code_review'] $promptManager->renderPrompt('code_review', [ // render directly 'code' => '$x = 1;', 'language' => 'php' ]);
PromptArgument
use Vluzrmos\MCP\Prompts\PromptArgument; $arg = new PromptArgument( 'code', // name (required) 'The code to review', // description (optional) true // required (default: false) ); $arg->getName(); // 'code' $arg->getDescription(); // 'The code to review' $arg->isRequired(); // true
PromptMessage
PromptMessage wraps a role (user or assistant) and a Content object:
use Vluzrmos\MCP\Prompts\PromptMessage; use Vluzrmos\MCP\Content\Text; $userMsg = PromptMessage::user(new Text('Hello')); $assistantMsg = PromptMessage::assistant(new Text('Hi there!')); $userMsg->getRole(); // 'user' $userMsg->getContent(); // Text instance
Wiring Prompts to the Server
use Vluzrmos\MCP\Capability; use Vluzrmos\MCP\Methods\PromptsList; use Vluzrmos\MCP\Methods\PromptsGet; $server->addCapability(Capability::prompts()); $server->addMethod(new PromptsList($promptManager)); $server->addMethod(new PromptsGet($promptManager));
Content Types
All types extend Vluzrmos\MCP\Content\Content and implement JsonSerializable.
Text
use Vluzrmos\MCP\Content\Text; $text = new Text('Hello, world!'); $text->getText(); // 'Hello, world!'
Image
use Vluzrmos\MCP\Content\Image; $image = new Image(base64_encode($imageData), 'image/png'); $image = new Image($base64, 'image/jpeg');
Audio
use Vluzrmos\MCP\Content\Audio; $audio = new Audio(base64_encode($audioData), 'audio/wav'); $audio = new Audio($base64, 'audio/mp3');
ResourceLink
use Vluzrmos\MCP\Content\ResourceLink; $link = new ResourceLink( 'file:///path/to/document.pdf', 'Document', 'A PDF document', 'application/pdf' );
EmbeddedResource
use Vluzrmos\MCP\Content\EmbeddedResource; $resource = new EmbeddedResource( 'file:///data/config.json', 'Configuration', 'Configuration file', 'application/json' );
Annotations
Annotations can be attached to any content:
use Vluzrmos\MCP\Content\Text; use Vluzrmos\MCP\Content\Annotations; $annotations = new Annotations(); $annotations->setAudience(['user', 'assistant']); $annotations->setPriority(0.8); $annotations->setLastModified('2025-01-01T00:00:00Z'); $text = new Text('Annotated content', $annotations);
Sessions & Login
The MCP protocol uses sessions to maintain state across multiple requests. A session is created during the initialize handshake and tracked via the Mcp-Session-Id HTTP header.
How It Works
- Client sends
initialize— theServerInitializemethod creates a newSessionwith a UUID v4 identifier and stores it inSessionManager. - Server responds — the response includes the
Mcp-Session-Idheader with the session ID. - Client sends subsequent requests — includes the
Mcp-Session-Idheader so the server can resume the session. - Server resolves the session — looks up the session by ID, updates its last activity timestamp, and restores context.
Session Lifecycle Diagram
Client Server
│ │
├─── initialize ──────────────────────────►│ → Creates Session (UUID v4)
│ │ → Stores in SessionManager
│◄────── response ────────────────────────┤ ← Mcp-Session-Id: <uuid>
│ + Mcp-Session-Id header │
│ │
├─── tools/list ──────────────────────────►│ → Resolves Session from header
│ Mcp-Session-Id: <uuid> │ → session.touch()
│◄────── response ────────────────────────┤
│ │
├─── tools/call ──────────────────────────►│ → Same session context
│ Mcp-Session-Id: <uuid> │
│◄────── response ────────────────────────┤
│ │
Automatic Session Creation
When a client sends the initialize method, a session is created automatically:
$server = new Server(); $server->addMethod(new ServerInitialize($server)); // Client sends initialize with optional clientInfo $server->handle(json_encode([ 'jsonrpc' => '2.0', 'method' => 'initialize', 'id' => 1, 'params' => [ 'clientInfo' => ['name' => 'MyClient', 'version' => '1.0'] ] ])); // Session is now available $session = $server->getCurrentSession(); $session->getId(); // UUID v4, e.g. "550e8400-e29b-41d4-a716-446655440000" $session->getClientInfo(); // ['name' => 'MyClient', 'version' => '1.0']
Full HTTP Server with Session Handling
This is the recommended pattern for an HTTP MCP server with session persistence:
use Vluzrmos\MCP\Server; use Vluzrmos\MCP\Http\Headers\HeaderBag; use Vluzrmos\MCP\Http\JsonResponse; $server = new Server(); // ... register methods ... $headers = HeaderBag::fromServer(); $input = file_get_contents('php://input'); // 1. Resolve existing session from incoming header $sessionId = $headers->get('Mcp-Session-Id'); if ($sessionId) { $session = $server->getSessionManager()->get($sessionId); if ($session) { $session->touch(); // Update last activity $server->setCurrentSession($session); // Restore session context } } // 2. Handle the JSON-RPC request (may create a new session on "initialize") $mcpResponse = $server->handle($input); // 3. Include session ID in the response headers $currentSession = $server->getCurrentSession(); $currentSessionId = $currentSession ? $currentSession->getId() : $sessionId; $responseHeaders = new HeaderBag([ 'MCP-Protocol-Version' => $server->getProtocolVersion(), ]); if ($currentSessionId) { $responseHeaders->set('Mcp-Session-Id', $currentSessionId); } // 4. Send the response $response = new JsonResponse($mcpResponse, 200, $responseHeaders); $response->send();
Manual Session Management
You can interact with the SessionManager directly:
use Vluzrmos\MCP\SessionManager\SessionManager; $manager = new SessionManager(); // Create a session $session = $manager->create(['name' => 'MyClient']); // Look up and verify $manager->has($session->getId()); // true $manager->get($session->getId()); // Session instance $manager->count(); // 1 $manager->all(); // ['id' => Session, ...] // Update activity timestamp $session->touch(); // Destroy a session $manager->destroy($session->getId());
Session Object API
$session->getId(); // string — UUID v4 $session->getClientInfo(); // array — client info from initialize $session->setClientInfo([...]); // self — update client info $session->getCreatedAt(); // float — microtime of creation $session->getLastActivityAt(); // float — microtime of last touch() $session->touch(); // self — update lastActivityAt to now $session->toArray(); // array — full session data Session::restore($array); // static — restore a Session from an array
SessionManagerInterface
The session management is driven by the SessionManagerInterface, allowing you to swap the default in-memory implementation for any persistence backend:
interface SessionManagerInterface { public function create(array $clientInfo = []); // Session public function get($id); // Session|null public function has($id); // bool public function destroy($id); // bool public function all(); // array<string, Session> public function count(); // int }
The default SessionManager implements this interface and stores sessions in memory — ideal for single-process servers or testing.
Custom Session Manager
You can create your own implementation for any persistence backend:
use Vluzrmos\MCP\SessionManager\SessionManagerInterface; use Vluzrmos\MCP\Session; class RedisSessionManager implements SessionManagerInterface { public function create(array $clientInfo = []) { $session = new Session($this->generateId(), $clientInfo); $this->redis->set('mcp:' . $session->getId(), json_encode($session->toArray())); return $session; } public function get($id) { $data = $this->redis->get('mcp:' . $id); return $data ? Session::restore(json_decode($data, true)) : null; } // ... implement remaining methods } $server = new Server(null, null, new RedisSessionManager($redis));
Laravel Integration
LaravelSessionManager works with any Laravel store that provides get(), put(), forget(), and has() — such as the Cache Repository or Session Store available in Laravel 5.1+.
use Vluzrmos\MCP\SessionManager\LaravelSessionManager; use Vluzrmos\MCP\Server; // Using Laravel Cache (Redis, file, database, memcached, etc.) $store = app('cache')->store('redis'); $sessionManager = new LaravelSessionManager($store); $server = new Server(null, null, $sessionManager);
// Using Laravel Session Store $store = app('session')->driver(); $sessionManager = new LaravelSessionManager($store); $server = new Server(null, null, $sessionManager);
// Custom key prefix $sessionManager = new LaravelSessionManager($store, 'my_app_mcp:');
The LaravelSessionManager also provides a save() method to persist session changes (e.g. after calling touch()):
$session = $sessionManager->get($sessionId); $session->touch(); $sessionManager->save($session);
Note: The default
SessionManagerstores sessions in memory. In a production environment with multiple PHP processes (e.g. php-fpm), useLaravelSessionManagerwith a shared store, or implement your ownSessionManagerInterfacewith persistence (database, Redis, file-based, etc.) so sessions survive across requests.
HTTP & Server-Sent Events
JsonResponse
use Vluzrmos\MCP\Http\JsonResponse; $response = new JsonResponse(['result' => 'ok']); $response->send();
BinaryFileResponse
use Vluzrmos\MCP\Http\BinaryFileResponse; $response = new BinaryFileResponse('/path/to/file.pdf'); $response->send();
Server-Sent Events (Streaming)
use Vluzrmos\MCP\ServerSentEvents\Stream; use Vluzrmos\MCP\ServerSentEvents\Payload; $stream = new Stream(); $stream->stream(function ($stream) use ($response) { // Send an SSE event $stream->payload(new Payload( $response, // data 'message', // event type '1', // id 5000 // retry (ms) )); // Send notifications $stream->payload(new Payload(null, 'ping')); });
Content Negotiation
use Vluzrmos\MCP\Http\Headers\Accept; use Vluzrmos\MCP\Http\Headers\HeaderBag; $headers = HeaderBag::fromServer(); $accept = Accept::make($headers->get('Accept', 'application/json')); $preferred = $accept->getPreferredType([ 'application/json', 'text/event-stream' ]); if ($preferred === 'text/event-stream') { // Streaming SSE } else { // JSON response }
Capabilities
Capabilities tell the client which features the server supports.
use Vluzrmos\MCP\Capability; use Vluzrmos\MCP\CapabilityOptions; // Simple capabilities $server->addCapability(Capability::tools()); $server->addCapability(Capability::resources()); $server->addCapability(Capability::prompts()); $server->addCapability(Capability::logging()); $server->addCapability(Capability::completions()); // With options $options = new CapabilityOptions(); $options->listChanged(true); // supports list change notifications $options->subscribe(true); // supports change subscriptions $server->addCapability(Capability::tools($options));
Pagination
Use Cursor to implement pagination in responses:
use Vluzrmos\MCP\Pagination\Cursor; // Create cursor $cursor = Cursor::pagination(1, 20); // page 1, 20 per page // Custom cursor $cursor = new Cursor(['offset' => 0, 'limit' => 50]); // Opaque serialization (base64) $encoded = json_encode($cursor); // base64 string $restored = Cursor::fromBase64String($encoded); // Add to ToolResult $result = ToolResult::structured($data); $result->setNextCursor($cursor);
API Reference
Server
$server = new Server( ServerInfo $serverInfo = null, // default: new ServerInfo('MCP Server', '1.0.0') JsonRpcServer $jsonRpcServer = null, // default: new JsonRpcServer() SessionManagerInterface $sessionManager = null // default: new SessionManager() ); $server->setProtocolVersion($version); // default: '2025-06-18' $server->setInstructions($text); $server->addCapability(Capability $cap); $server->addMethod(Method $method); $server->addMethodHandler($name, $callable); $server->handle($jsonRpcInput); // JSON string or array (batch) $server->getSessionManager(); $server->getCurrentSession(); $server->setCurrentSession(Session $s);
ServerInfo
$info = new ServerInfo($name, $version, array $extraInfos = []); $info->setExtraInfo('homepage', 'https://example.com'); // Serializes as: {"name": "...", "version": "...", "x-homepage": "https://..."}
JsonSchema
$schema = new JsonSchema($type, $properties = null, array $required = null); $schema = new JsonSchema('object', [ 'name' => ['type' => 'string', 'description' => 'User name'], 'age' => ['type' => 'integer'] ], ['name']); $schema->setTitle('Title'); $schema->setDescription('Description'); $schema->property('email', ['type' => 'string', 'format' => 'email']);
JSON-RPC Methods
| Class | MCP Method | Description |
|---|---|---|
ServerInitialize |
initialize |
Initializes MCP connection and creates session |
ToolsList |
tools/list |
Lists available tools |
ToolsCall |
tools/call |
Executes a tool |
ResourcesList |
resources/list |
Lists available resources |
ResourcesRead |
resources/read |
Reads a resource by URI |
PromptsList |
prompts/list |
Lists available prompts |
PromptsGet |
prompts/get |
Renders a prompt with arguments |
Ping |
ping |
Health check |
NotificationsInitialized |
notifications/initialized |
Post-initialization acknowledgement |
NotificationsCancelled |
notifications/cancelled |
Handles request cancellation |
Registering Custom Methods
// Via Method interface use Vluzrmos\JsonRPC\Concerns\Method; use Vluzrmos\JsonRPC\Request; class MyMethod implements Method { public function getName() { return 'my/method'; } public function execute(Request $request) { $params = $request->getParams(); return ['result' => 'ok']; } } $server->addMethod(new MyMethod()); // Via closure $server->addMethodHandler('my/handler', function (Request $request) { return ['status' => 'ok']; });
Testing
The project uses PHPUnit 5.7.
# Install dependencies composer install # Install dependencies: Via Docker docker run -it --rm -v .:/app -w /app vluzrmos/mcp-php composer install # Run all tests vendor/bin/phpunit # Via Docker docker run -it --rm -v .:/app -w /app vluzrmos/mcp-php vendor/bin/phpunit
Test Environment Variables
| Variable | Default | Description |
|---|---|---|
RUN_INTEGRATION_TESTS |
0 |
Enable integration tests |
TEST_DEBUG |
0 |
Enable debug output in tests |
Docker
Build image
docker build -t vluzrmos/mcp-php .
Run tests
docker run -it --rm -v .:/app -w /app vluzrmos/mcp-php vendor/bin/phpunit
Development server
# Via Docker docker run -it --rm -p 8000:8000 -v .:/app -w /app vluzrmos/mcp-php \ php -S 0.0.0.0:8000 -t examples/server # Or locally php -S 0.0.0.0:8000 -t examples/server
Basic example (CLI)
docker run -it --rm -v .:/app -w /app vluzrmos/mcp-php php examples/basic.php
License
MIT