1. Go to this page and download the library: Download llm/mcp-server library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
llm / mcp-server example snippets
cp\Server\Server;
use Mcp\Server\Configuration;
use Mcp\Server\Registry;
use Mcp\Server\Protocol;
use Mcp\Server\Defaults\CallableHandler;
use Mcp\Server\Defaults\ToolExecutor;
use Mcp\Server\Session\SessionManager;
use Mcp\Server\Session\ArraySessionHandler;
use Mcp\Server\Dispatcher;
use Mcp\Server\Dispatcher\RoutesFactory;
use Mcp\Server\Session\SubscriptionManager;
use Mcp\Server\Transports\StdioServerTransport;
use PhpMcp\Schema\Tool;
use PhpMcp\Schema\Implementation;
use PhpMcp\Schema\ServerCapabilities;
use PhpMcp\Schema\Content\TextContent;
use Psr\Log\NullLogger;
use React\EventLoop\Loop;
// Create server configuration
$serverInfo = new Implementation('my-server', '1.0.0');
$capabilities = new ServerCapabilities(tools: true);
$configuration = new Configuration($serverInfo, $capabilities);
// Set up core components
$loop = Loop::get();
$logger = new NullLogger();
$registry = new Registry($logger);
$sessionHandler = new ArraySessionHandler();
$sessionManager = new SessionManager($sessionHandler, $logger, $loop);
$subscriptionManager = new SubscriptionManager($logger);
$toolExecutor = new ToolExecutor($registry, $logger);
// Create dispatcher
$routesFactory = new RoutesFactory(
$configuration,
$registry,
$subscriptionManager,
$toolExecutor,
);
$dispatcher = new Dispatcher($logger, $routesFactory);
// Create protocol handler
$protocol = new Protocol(
$configuration,
$registry,
$sessionManager,
$dispatcher,
$subscriptionManager,
$logger,
);
// Register a simple tool
$greetTool = Tool::make(
'greet',
'Greets a person by name',
['name' => ['type' => 'string', 'description' => 'Name to greet']],
);
$greetHandler = new CallableHandler(function($args) {
return [TextContent::make("Hello, " . ($args['name'] ?? 'World') . "!")];
});
$registry->registerTool($greetTool, $greetHandler);
// Create and start server
$server = new Server($protocol, $sessionManager, $logger);
$transport = new StdioServerTransport();
$server->listen($transport);
use Mcp\Server\Transports\StdioServerTransport;
$transport = new StdioServerTransport();
$server->listen($transport);
use Mcp\Server\Transports\HttpServer;
use Mcp\Server\Transports\HttpServerTransport;
use React\EventLoop\Loop;
$loop = Loop::get();
$httpServer = new HttpServer($loop, '127.0.0.1', 8080, '/mcp');
$transport = new HttpServerTransport($httpServer);
$server->listen($transport);
use Mcp\Server\Transports\StreamableHttpServerTransport;
use Mcp\Server\Defaults\InMemoryEventStore;
$eventStore = new InMemoryEventStore();
$transport = new StreamableHttpServerTransport(
httpServer: $httpServer,
enableJsonResponse: true,
stateless: false,
eventStore: $eventStore
);
$server->listen($transport);
use PhpMcp\Schema\Content\ImageContent;
use PhpMcp\Schema\Content\BlobResourceContents;
$imageProcessorHandler = new CallableHandler(function($args) {
$imagePath = $args['image_path'];
// Process image (example)
$imageData = file_get_contents($imagePath);
$base64Image = base64_encode($imageData);
return [
TextContent::make("Processed image: $imagePath"),
ImageContent::make($base64Image, 'image/jpeg'),
];
});
use Mcp\Server\Exception\ValidationException;
$validatedToolHandler = new CallableHandler(function($args) {
if (!isset($args[' 'xtContent::make('Success!')];
});
use Mcp\Server\Contracts\SessionHandlerInterface;
class DatabaseSessionHandler implements SessionHandlerInterface
{
public function __construct(private PDO $pdo) {}
public function read(string $id): string|false
{
$stmt = $this->pdo->prepare('SELECT data FROM sessions WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetchColumn() ?: false;
}
public function write(string $id, string $data): bool
{
$stmt = $this->pdo->prepare(
'INSERT INTO sessions (id, data, updated_at) VALUES (?, ?, NOW())
ON DUPLICATE KEY UPDATE data = VALUES(data), updated_at = NOW()'
);
return $stmt->execute([$id, $data]);
}
public function destroy(string $id): bool
{
$stmt = $this->pdo->prepare('DELETE FROM sessions WHERE id = ?');
return $stmt->execute([$id]);
}
public function gc(int $maxLifetime): array
{
$stmt = $this->pdo->prepare(
'DELETE FROM sessions WHERE updated_at < DATE_SUB(NOW(), INTERVAL ? SECOND)'
);
$stmt->execute([$maxLifetime]);
return []; // Return deleted session IDs if needed
}
}
use Mcp\Server\Session\CacheSessionHandler;
use Mcp\Server\Defaults\FileCache;
$cache = new FileCache('/tmp/mcp_sessions.cache');
$sessionHandler = new CacheSessionHandler($cache, ttl: 7200);
$sessionManager = new SessionManager($sessionHandler, $logger, $loop);
use Mcp\Server\Transports\Middleware\AuthenticationMiddleware;
$authMiddleware = new AuthenticationMiddleware(
authenticator: function($request, $authHeader) {
// Bearer token validation
if (!str_starts_with($authHeader, 'Bearer ')) {
return false;
}
$token = substr($authHeader, 7);
return validateToken($token); // Your validation logic
},
protectedPaths: ['/mcp']
);
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class LoggingMiddleware implements MiddlewareInterface
{
public function __construct(private LoggerInterface $logger) {}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$start = microtime(true);
$this->logger->info('Request started', [
'method' => $request->getMethod(),
'path' => $request->getUri()->getPath()
]);
$response = $handler->handle($request);
$duration = microtime(true) - $start;
$this->logger->info('Request completed', [
'status' => $response->getStatusCode(),
'duration' => round($duration * 1000, 2) . 'ms'
]);
return $response;
}
}
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Mcp\Server\Dispatcher\Paginator;
// Advanced logging
$logger = new Logger('mcp-server');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::INFO));
// Custom pagination
$paginator = new Paginator(paginationLimit: 100, logger: $logger);
// Server configuration with all features
$serverInfo = new Implementation(
name: 'advanced-mcp-server',
version: '2.0.0'
);
$capabilities = new ServerCapabilities(
tools: true,
resources: true,
resourcesSubscribe: true,
resourcesListChanged: true,
prompts: true,
promptsListChanged: true,
logging: true,
completions: true
);
$configuration = new Configuration(
serverInfo: $serverInfo,
capabilities: $capabilities,
instructions: 'This server provides comprehensive MCP functionality with advanced features.'
);
// Enhanced routes factory
$routesFactory = new RoutesFactory(
configuration: $configuration,
registry: $registry,
subscriptionManager: $subscriptionManager,
toolExecutor: $toolExecutor,
pagination: $paginator,
logger: $logger
);
use Mcp\Server\Defaults\InMemoryEventStore;
class RedisEventStore implements EventStoreInterface
{
public function __construct(private Redis $redis) {}
public function storeEvent(string $streamId, string $message): string
{
$eventId = $streamId . '_' . time() . '_' . uniqid();
$this->redis->hSet("stream:$streamId", $eventId, $message);
return $eventId;
}
public function replayEventsAfter(string $lastEventId, callable $sendCallback): void
{
// Extract stream ID from event ID
$streamId = explode('_', $lastEventId)[0];
$events = $this->redis->hGetAll("stream:$streamId");
$found = false;
foreach ($events as $eventId => $message) {
if ($eventId === $lastEventId) {
$found = true;
continue;
}
if ($found) {
$sendCallback($eventId, $message);
}
}
}
}
use Mcp\Server\Defaults\FileCache;
use Mcp\Server\Contracts\HandlerInterface;
final readonly class CachedResourceHandler implements HandlerInterface
{
public function __construct(
private HandlerInterface $delegate,
private CacheInterface $cache,
private int $ttl = 3600,
) {}
public function handle(array $arguments, Context $context): mixed
{
$cacheKey = 'resource:' . md5(serialize($arguments));
$cached = $this->cache->get($cacheKey);
if ($cached !== null) {
return $cached;
}
$result = $this->delegate->handle($arguments, $context);
$this->cache->set($cacheKey, $result, $this->ttl);
return $result;
}
}
// Periodic memory cleanup
$server = new Server($protocol, $sessionManager, $logger);
$loop->addPeriodicTimer(60, function() use ($logger) {
$memoryUsage = memory_get_usage(true);
$peakMemory = memory_get_peak_usage(true);
$logger->info('Memory status', [
'current_mb' => round($memoryUsage / 1024 / 1024, 2),
'peak_mb' => round($peakMemory / 1024 / 1024, 2),
]);
// Force garbage collection if memory usage is high
if ($memoryUsage > 100 * 1024 * 1024) { // 100MB
gc_collect_cycles();
}
});
final readonly class HandlerFactory
{
public function __construct(
private ContainerInterface $container,
private LoggerInterface $logger,
) {}
public function createHandler(string $handlerClass): HandlerInterface
{
return new CallableHandler(function($args) use ($handlerClass) {
$instance = $this->container->get($handlerClass);
return $instance->execute($args);
});
}
}
// Usage
$toolHandler = $handlerFactory->createHandler(CalculatorService::class);
$registry->registerTool($calculatorTool, $toolHandler);
use Mcp\Server\Contracts\HandlerInterface;
use Psr\Log\LoggerInterface;
final readonly class LoggingHandlerDecorator implements HandlerInterface
{
public function __construct(
private HandlerInterface $handler,
private LoggerInterface $logger,
) {}
public function handle(array $arguments, Context $context): mixed
{
$start = microtime(true);
$this->logger->debug('Handler execution started', ['arguments' => $arguments]);
try {
$result = $this->handler->handle($arguments, $context);
$duration = microtime(true) - $start;
$this->logger->info('Handler execution completed', ['duration' => $duration]);
return $result;
} catch (\Throwable $e) {
$this->logger->error('Handler execution failed', ['exception' => $e->getMessage()]);
throw $e;
}
}
}
use Spiral\McpServer\SchemaMapperInterface;
final readonly class ClassHandler implements HandlerInterface
{
public function __construct(
private FactoryInterface $factory,
private SchemaMapperInterface $schemaMapper,
private \ReflectionClass $class,
private ?string $schemaClass = null,
) {}
public function handle(
array $arguments,
Context $context,
): mixed {
/** @var callable $tool */
$tool = $this->factory->make($this->class->getName());
if ($this->schemaClass === null) {
return $tool();
}
// Map raw arguments to strongly-typed DTO
$object = $this->schemaMapper->toObject(
json: \json_encode($arguments),
class: $this->schemaClass,
);
return $tool($object);
}
}
interface SchemaMapperInterface
{
/**
* Generate JSON schema from PHP class
* @param class-string $class
*/
public function toJsonSchema(string $class): array;
/**
* Map JSON to strongly-typed PHP object
* @template T of object
* @param class-string<T>|null $class
* @return T
*/
public function toObject(string $json, ?string $class = null): object;
}
use CuyZ\Valinor\Mapper\TreeMapper;
use Spiral\JsonSchemaGenerator\Generator as JsonSchemaGenerator;
final readonly class SchemaMapper implements SchemaMapperInterface
{
public function __construct(
private JsonSchemaGenerator $generator,
private TreeMapper $mapper,
) {}
public function toJsonSchema(string $class): array
{
if (\json_validate($class)) {
return \json_decode($class, associative: true);
}
if (\class_exists($class)) {
return $this->generator->generate($class)->jsonSerialize();
}
throw new \InvalidArgumentException("Invalid class or JSON schema: {$class}");
}
public function toObject(string $json, ?string $class = null): object
{
if ($class === null) {
return \json_decode($json, associative: false);
}
return $this->mapper->map($class, \json_decode($json, associative: true));
}
}
// DTO class
final readonly class CalculatorRequest
{
public function __construct(
public string $operation,
public float $a,
public float $b,
) {}
}
// Tool implementation
final readonly class Calculator
{
public function __invoke(CalculatorRequest $request): float
{
return match($request->operation) {
'add' => $request->a + $request->b,
'subtract' => $request->a - $request->b,
'multiply' => $request->a * $request->b,
'divide' => $request->b !== 0.0 ? $request->a / $request->b
: throw new InvalidArgumentException('Division by zero'),
default => throw new InvalidArgumentException('Unknown operation')
};
}
}
// Registration with automatic schema generation
$schema = $schemaMapper->toJsonSchema(CalculatorRequest::class);
$tool = Tool::make('calculator', 'Performs calculations', $schema);
$handler = new ClassHandler(
$factory,
$schemaMapper,
new \ReflectionClass(Calculator::class),
CalculatorRequest::class,
);
$registry->registerTool($tool, $handler);
bash
git clone https://github.com/your-org/php-mcp-server.git
cd php-mcp-server
composer install
composer test
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.