*/ final class Builder { private ?Implementation $serverInfo = null; private RegistryInterface $registry; private ?LoggerInterface $logger = null; private ?CacheInterface $discoveryCache = null; private ?EventDispatcherInterface $eventDispatcher = null; private ?ContainerInterface $container = null; private ?SessionFactoryInterface $sessionFactory = null; private ?SessionStoreInterface $sessionStore = null; private int $sessionTtl = 3600; private int $paginationLimit = 50; private ?string $instructions = null; private ?ProtocolVersion $protocolVersion = null; /** * @var array> */ private array $requestHandlers = []; /** * @var array */ private array $notificationHandlers = []; /** * @var array{ * handler: Handler, * name: ?string, * description: ?string, * annotations: ?ToolAnnotations, * icons: ?Icon[], * meta: ?array * }[] */ private array $tools = []; /** * @var array{ * handler: Handler, * uri: string, * name: ?string, * description: ?string, * mimeType: ?string, * size: int|null, * annotations: ?Annotations, * icons: ?Icon[], * meta: ?array * }[] */ private array $resources = []; /** * @var array{ * handler: Handler, * uriTemplate: string, * name: ?string, * description: ?string, * mimeType: ?string, * annotations: ?Annotations, * meta: ?array * }[] */ private array $resourceTemplates = []; /** * @var array{ * handler: Handler, * name: ?string, * description: ?string, * icons: ?Icon[], * meta: ?array * }[] */ private array $prompts = []; private ?string $discoveryBasePath = null; /** * @var string[] */ private array $discoveryScanDirs = []; /** * @var array|string[] */ private array $discoveryExcludeDirs = []; private ?ServerCapabilities $serverCapabilities = null; /** * @var LoaderInterface[] */ private array $loaders = []; /** * Sets the server's identity. Required. * * @param ?Icon[] $icons */ public function setServerInfo( string $name, string $version, ?string $description = null, ?array $icons = null, ?string $websiteUrl = null, ): self { $this->serverInfo = new Implementation(trim($name), trim($version), $description, $icons, $websiteUrl); return $this; } /** * Configures the server's pagination limit. */ public function setPaginationLimit(int $paginationLimit): self { $this->paginationLimit = $paginationLimit; return $this; } /** * Configures the instructions describing how to use the server and its features. * * This can be used by clients to improve the LLM's understanding of available tools, resources, * etc. It can be thought of like a "hint" to the model. For example, this information MAY * be added to the system prompt. */ public function setInstructions(?string $instructions): self { $this->instructions = $instructions; return $this; } /** * Explicitly set server capabilities. If set, this overrides automatic detection. */ public function setCapabilities(ServerCapabilities $serverCapabilities): self { $this->serverCapabilities = $serverCapabilities; return $this; } /** * Register a single custom method handler. * * @param RequestHandlerInterface $handler */ public function addRequestHandler(RequestHandlerInterface $handler): self { $this->requestHandlers[] = $handler; return $this; } /** * Register multiple custom method handlers. * * @param iterable> $handlers */ public function addRequestHandlers(iterable $handlers): self { foreach ($handlers as $handler) { $this->requestHandlers[] = $handler; } return $this; } /** * Register a single custom notification handler. */ public function addNotificationHandler(NotificationHandlerInterface $handler): self { $this->notificationHandlers[] = $handler; return $this; } /** * Register multiple custom notification handlers. * * @param iterable $handlers */ public function addNotificationHandlers(iterable $handlers): self { foreach ($handlers as $handler) { $this->notificationHandlers[] = $handler; } return $this; } public function setRegistry(RegistryInterface $registry): self { $this->registry = $registry; return $this; } /** * Provides a PSR-3 logger instance. Defaults to NullLogger. */ public function setLogger(LoggerInterface $logger): self { $this->logger = $logger; return $this; } public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): self { $this->eventDispatcher = $eventDispatcher; return $this; } /** * Provides a PSR-11 DI container, primarily for resolving user-defined handler classes. * Defaults to a basic internal container. */ public function setContainer(ContainerInterface $container): self { $this->container = $container; return $this; } public function setSession( SessionStoreInterface $sessionStore, SessionFactoryInterface $sessionFactory = new SessionFactory(), int $ttl = 3600, ): self { $this->sessionFactory = $sessionFactory; $this->sessionStore = $sessionStore; $this->sessionTtl = $ttl; return $this; } /** * @param string[] $scanDirs * @param string[] $excludeDirs */ public function setDiscovery( string $basePath, array $scanDirs = ['.', 'src'], array $excludeDirs = [], ?CacheInterface $cache = null, ): self { $this->discoveryBasePath = $basePath; $this->discoveryScanDirs = $scanDirs; $this->discoveryExcludeDirs = $excludeDirs; $this->discoveryCache = $cache; return $this; } public function setProtocolVersion(ProtocolVersion $protocolVersion): self { $this->protocolVersion = $protocolVersion; return $this; } /** * Manually registers a tool handler. * * @param Handler $handler * @param array|null $inputSchema * @param ?Icon[] $icons * @param array|null $meta */ public function addTool( callable|array|string $handler, ?string $name = null, ?string $description = null, ?ToolAnnotations $annotations = null, ?array $inputSchema = null, ?array $icons = null, ?array $meta = null, ): self { $this->tools[] = compact( 'handler', 'name', 'description', 'annotations', 'inputSchema', 'icons', 'meta', ); return $this; } /** * Manually registers a resource handler. * * @param Handler $handler * @param ?Icon[] $icons * @param array|null $meta */ public function addResource( \Closure|array|string $handler, string $uri, ?string $name = null, ?string $description = null, ?string $mimeType = null, ?int $size = null, ?Annotations $annotations = null, ?array $icons = null, ?array $meta = null, ): self { $this->resources[] = compact( 'handler', 'uri', 'name', 'description', 'mimeType', 'size', 'annotations', 'icons', 'meta', ); return $this; } /** * Manually registers a resource template handler. * * @param Handler $handler * @param array|null $meta */ public function addResourceTemplate( \Closure|array|string $handler, string $uriTemplate, ?string $name = null, ?string $description = null, ?string $mimeType = null, ?Annotations $annotations = null, ?array $meta = null, ): self { $this->resourceTemplates[] = compact( 'handler', 'uriTemplate', 'name', 'description', 'mimeType', 'annotations', 'meta', ); return $this; } /** * Manually registers a prompt handler. * * @param Handler $handler * @param ?Icon[] $icons * @param array|null $meta */ public function addPrompt( \Closure|array|string $handler, ?string $name = null, ?string $description = null, ?array $icons = null, ?array $meta = null, ): self { $this->prompts[] = compact('handler', 'name', 'description', 'icons', 'meta'); return $this; } /** * Register a single custom loader. */ public function addLoader(LoaderInterface $loader): self { $this->loaders[] = $loader; return $this; } /** * @param iterable $loaders */ public function addLoaders(iterable $loaders): self { foreach ($loaders as $loader) { $this->loaders = [$loader]; } return $this; } /** * Builds the fully configured Server instance. */ public function build(): Server { $logger = $this->logger ?? new NullLogger(); $container = $this->container ?? new Container(); $registry = $this->registry ?? new Registry($this->eventDispatcher, $logger); $loaders = [ ...$this->loaders, new ArrayLoader($this->tools, $this->resources, $this->resourceTemplates, $this->prompts, $logger), ]; if (null !== $this->discoveryBasePath) { $loaders[] = new DiscoveryLoader($this->discoveryBasePath, $this->discoveryScanDirs, $this->discoveryExcludeDirs, $logger, $this->discoveryCache); } foreach ($loaders as $loader) { $loader->load($registry); } $sessionTtl = $this->sessionTtl ?? 3600; $sessionFactory = $this->sessionFactory ?? new SessionFactory(); $sessionStore = $this->sessionStore ?? new InMemorySessionStore($sessionTtl); $messageFactory = MessageFactory::make(); $capabilities = $this->serverCapabilities ?? new ServerCapabilities( tools: $registry->hasTools(), toolsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface, resources: $registry->hasResources() || $registry->hasResourceTemplates(), resourcesSubscribe: false, resourcesListChanged: $this->eventDispatcher instanceof EventDispatcherInterface, prompts: $registry->hasPrompts(), promptsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface, logging: false, completions: true, ); $serverInfo = $this->serverInfo ?? new Implementation(); $configuration = new Configuration($serverInfo, $capabilities, $this->paginationLimit, $this->instructions, $this->protocolVersion); $referenceHandler = new ReferenceHandler($container); $requestHandlers = array_merge($this->requestHandlers, [ new Handler\Request\CallToolHandler($registry, $referenceHandler, $logger), new Handler\Request\CompletionCompleteHandler($registry, $container), new Handler\Request\GetPromptHandler($registry, $referenceHandler, $logger), new Handler\Request\InitializeHandler($configuration), new Handler\Request\ListPromptsHandler($registry, $this->paginationLimit), new Handler\Request\ListResourcesHandler($registry, $this->paginationLimit), new Handler\Request\ListResourceTemplatesHandler($registry, $this->paginationLimit), new Handler\Request\ListToolsHandler($registry, $this->paginationLimit), new Handler\Request\PingHandler(), new Handler\Request\ReadResourceHandler($registry, $referenceHandler, $logger), ]); $notificationHandlers = array_merge($this->notificationHandlers, [ new Handler\Notification\InitializedHandler(), ]); $protocol = new Protocol( requestHandlers: $requestHandlers, notificationHandlers: $notificationHandlers, messageFactory: $messageFactory, sessionFactory: $sessionFactory, sessionStore: $sessionStore, logger: $logger, ); return new Server($protocol, $logger); } }