diff --git a/src/Server/Builder.php b/src/Server/Builder.php index c01dd731..e2cb4e63 100644 --- a/src/Server/Builder.php +++ b/src/Server/Builder.php @@ -60,7 +60,6 @@ use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Psr\SimpleCache\CacheInterface; -use Symfony\Component\Finder\Finder; /** * @phpstan-import-type Handler from ElementReference @@ -686,12 +685,8 @@ public function build(): Server ]; if (null !== $this->discoveryBasePath) { - if (null !== $this->discoverer || class_exists(Finder::class)) { - $discoverer = $this->discoverer ?? $this->createDiscoverer($logger); - $loaders[] = new DiscoveryLoader($this->discoveryBasePath, $this->discoveryScanDirs, $this->discoveryExcludeDirs, $discoverer, $this->discoveryNamePatterns, $logger); - } else { - $logger->warning('File-based discovery requires symfony/finder. Skipping automatic discovery. Run: composer require symfony/finder'); - } + $discoverer = $this->discoverer ?? $this->createDiscoverer($logger); + $loaders[] = new DiscoveryLoader($this->discoveryBasePath, $this->discoveryScanDirs, $this->discoveryExcludeDirs, $discoverer, $this->discoveryNamePatterns, $logger); } $chainLoader = new ChainLoader($loaders); diff --git a/tests/Unit/Server/BuilderTest.php b/tests/Unit/Server/BuilderTest.php index a29c9f4e..fd37d411 100644 --- a/tests/Unit/Server/BuilderTest.php +++ b/tests/Unit/Server/BuilderTest.php @@ -11,11 +11,15 @@ namespace Mcp\Tests\Unit\Server; +use Composer\Autoload\ClassLoader; +use Mcp\Capability\Discovery\Discoverer; +use Mcp\Capability\Discovery\DiscovererInterface; use Mcp\Capability\Registry; use Mcp\Capability\Registry\ElementReference; use Mcp\Capability\Registry\Loader\LoaderInterface; use Mcp\Capability\Registry\ReferenceHandlerInterface; use Mcp\Exception\LogicException; +use Mcp\Exception\RuntimeException; use Mcp\Schema\Content\TextContent; use Mcp\Schema\Extension\Apps\McpApps; use Mcp\Schema\Implementation; @@ -27,11 +31,19 @@ use Mcp\Server\Handler\Request\CallToolHandler; use Mcp\Server\Handler\Request\InitializeHandler; use Mcp\Server\Session\SessionInterface; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; +use Psr\Log\LogLevel; +use Symfony\Component\Finder\Finder; final class BuilderTest extends TestCase { + private const DISCOVERY_DEPENDENCY_ERROR = 'File-based discovery requires symfony/finder. Run: composer require symfony/finder'; + private const FINDER_LOADED_ERROR = 'Symfony Finder was loaded after Composer autoloaders were disabled.'; + private const TEST_SERVER_NAME = 'test'; + private const TEST_SERVER_VERSION = '1.0.0'; + #[TestDox('setReferenceHandler() returns the builder for fluent chaining')] public function testSetReferenceHandlerReturnsSelf(): void { @@ -66,6 +78,48 @@ public function testBuildWithoutCustomReferenceHandler(): void $this->assertInstanceOf(Server::class, $server); } + #[RunInSeparateProcess] + #[TestDox('build() fails when file-based discovery is configured without Symfony Finder')] + public function testBuildFailsWhenDiscoveryDependencyIsMissing(): void + { + Server::builder()->setServerInfo(self::TEST_SERVER_NAME, self::TEST_SERVER_VERSION)->build(); + Server::builder() + ->setServerInfo(self::TEST_SERVER_NAME, self::TEST_SERVER_VERSION) + ->setDiscovery(__DIR__) + ->setDiscoverer($this->createStub(DiscovererInterface::class)) + ->build(); + class_exists(Discoverer::class); + class_exists(RuntimeException::class); + class_exists(LogLevel::class); + + $autoloaders = array_values(array_filter( + spl_autoload_functions(), + static fn (mixed $autoloader): bool => \is_array($autoloader) && $autoloader[0] instanceof ClassLoader, + )); + $this->assertNotEmpty($autoloaders); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage(self::DISCOVERY_DEPENDENCY_ERROR); + + foreach ($autoloaders as $autoloader) { + $autoloader[0]->unregister(); + } + + try { + if (class_exists(Finder::class)) { + throw new LogicException(self::FINDER_LOADED_ERROR); + } + + Server::builder() + ->setServerInfo(self::TEST_SERVER_NAME, self::TEST_SERVER_VERSION) + ->setDiscovery(__DIR__) + ->build(); + } finally { + foreach ($autoloaders as $autoloader) { + $autoloader[0]->register(true); + } + } + } + #[TestDox('Custom ReferenceHandler is used when calling a tool')] public function testCustomReferenceHandlerIsUsedForToolCalls(): void {