From 89850e046957866b221724b7c3d5a5536aa70209 Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Thu, 11 Dec 2025 19:58:19 +0530 Subject: [PATCH] Simplify tool, resource, and prompt discovery logic for MCP Signed-off-by: Pushpak Chhajed --- rector.php | 2 - src/Mcp/Boost.php | 147 +++++++++++++++++++++------------------------- 2 files changed, 67 insertions(+), 82 deletions(-) diff --git a/rector.php b/rector.php index b9c1690f..5f495026 100644 --- a/rector.php +++ b/rector.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; -use Rector\CodingStyle\Rector\FunctionLike\FunctionLikeToFirstClassCallableRector; use Rector\Config\RectorConfig; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector; @@ -17,7 +16,6 @@ ReadOnlyPropertyRector::class, EncapsedStringsToSprintfRector::class, DisallowedEmptyRuleFixerRector::class, - FunctionLikeToFirstClassCallableRector::class, ]) ->withPreparedSets( deadCode: true, diff --git a/src/Mcp/Boost.php b/src/Mcp/Boost.php index 5270518d..46e4820e 100644 --- a/src/Mcp/Boost.php +++ b/src/Mcp/Boost.php @@ -4,10 +4,27 @@ namespace Laravel\Boost\Mcp; -use DirectoryIterator; use Laravel\Boost\Mcp\Methods\CallToolWithExecutor; -use Laravel\Boost\Mcp\Resources\ApplicationInfo; +use Laravel\Boost\Mcp\Tools\ApplicationInfo; +use Laravel\Boost\Mcp\Tools\BrowserLogs; +use Laravel\Boost\Mcp\Tools\DatabaseConnections; +use Laravel\Boost\Mcp\Tools\DatabaseQuery; +use Laravel\Boost\Mcp\Tools\DatabaseSchema; +use Laravel\Boost\Mcp\Tools\GetAbsoluteUrl; +use Laravel\Boost\Mcp\Tools\GetConfig; +use Laravel\Boost\Mcp\Tools\LastError; +use Laravel\Boost\Mcp\Tools\ListArtisanCommands; +use Laravel\Boost\Mcp\Tools\ListAvailableConfigKeys; +use Laravel\Boost\Mcp\Tools\ListAvailableEnvVars; +use Laravel\Boost\Mcp\Tools\ListRoutes; +use Laravel\Boost\Mcp\Tools\ReadLogEntries; +use Laravel\Boost\Mcp\Tools\ReportFeedback; +use Laravel\Boost\Mcp\Tools\SearchDocs; +use Laravel\Boost\Mcp\Tools\Tinker; use Laravel\Mcp\Server; +use Laravel\Mcp\Server\Prompt; +use Laravel\Mcp\Server\Resource; +use Laravel\Mcp\Server\Tool; class Boost extends Server { @@ -34,120 +51,90 @@ class Boost extends Server /** * The tools registered with this MCP server. * - * @var array> + * @var array> */ protected array $tools = []; /** * The resources registered with this MCP server. * - * @var array> + * @var array> */ - protected array $resources = [ - ApplicationInfo::class, - ]; + protected array $resources = []; /** * The prompts registered with this MCP server. * - * @var array> + * @var array> */ protected array $prompts = []; protected function boot(): void { - collect($this->discoverTools())->each(fn (string $tool): string => $this->tools[] = $tool); - collect($this->discoverResources())->each(fn (string $resource): string => $this->resources[] = $resource); - collect($this->discoverPrompts())->each(fn (string $prompt): string => $this->prompts[] = $prompt); + $this->tools = $this->discoverTools(); + $this->resources = $this->discoverResources(); + $this->prompts = $this->discoverPrompts(); // Override the tools/call method to use our ToolExecutor $this->methods['tools/call'] = CallToolWithExecutor::class; } /** - * @return array> + * @param array $availablePrimitives + * @return array + */ + private function discoverPrimitives(array $availablePrimitives, string $type): array + { + return collect($availablePrimitives) + ->diff(config("boost.mcp.{$type}.exclude", [])) + ->merge( + collect(config("boost.mcp.{$type}.include", [])) + ->filter(fn (string $class): bool => class_exists($class)) + ) + ->values() + ->all(); + } + + /** + * @return array> */ protected function discoverTools(): array { - $tools = []; - - $excludedTools = config('boost.mcp.tools.exclude', []); - $toolDir = new DirectoryIterator(__DIR__.DIRECTORY_SEPARATOR.'Tools'); - - foreach ($toolDir as $toolFile) { - if ($toolFile->isFile() && $toolFile->getExtension() === 'php') { - $fqdn = 'Laravel\\Boost\\Mcp\\Tools\\'.$toolFile->getBasename('.php'); - if (class_exists($fqdn) && ! in_array($fqdn, $excludedTools, true)) { - $tools[] = $fqdn; - } - } - } - - $extraTools = config('boost.mcp.tools.include', []); - foreach ($extraTools as $toolClass) { - if (class_exists($toolClass)) { - $tools[] = $toolClass; - } - } - - return $tools; + return $this->discoverPrimitives([ + ApplicationInfo::class, + BrowserLogs::class, + DatabaseConnections::class, + DatabaseQuery::class, + DatabaseSchema::class, + GetAbsoluteUrl::class, + GetConfig::class, + LastError::class, + ListArtisanCommands::class, + ListAvailableConfigKeys::class, + ListAvailableEnvVars::class, + ListRoutes::class, + ReadLogEntries::class, + ReportFeedback::class, + SearchDocs::class, + Tinker::class, + ], 'tools'); } /** - * @return array> + * @return array> */ protected function discoverResources(): array { - $resources = []; - - $excludedResources = config('boost.mcp.resources.exclude', []); - $resourceDir = new DirectoryIterator(__DIR__.DIRECTORY_SEPARATOR.'Resources'); - - foreach ($resourceDir as $resourceFile) { - if ($resourceFile->isFile() && $resourceFile->getExtension() === 'php') { - $fqdn = 'Laravel\\Boost\\Mcp\\Resources\\'.$resourceFile->getBasename('.php'); - if (class_exists($fqdn) && ! in_array($fqdn, $excludedResources, true) && $fqdn !== ApplicationInfo::class) { - $resources[] = $fqdn; - } - } - } - - $extraResources = config('boost.mcp.resources.include', []); - foreach ($extraResources as $resourceClass) { - if (class_exists($resourceClass)) { - $resources[] = $resourceClass; - } - } - - return $resources; + return $this->discoverPrimitives([ + Resources\ApplicationInfo::class, + ], 'resources'); } /** - * @return array> + * @return array> */ protected function discoverPrompts(): array { - $prompts = []; - - $excludedPrompts = config('boost.mcp.prompts.exclude', []); - $promptDir = new DirectoryIterator(__DIR__.DIRECTORY_SEPARATOR.'Prompts'); - - foreach ($promptDir as $promptFile) { - if ($promptFile->isFile() && $promptFile->getExtension() === 'php') { - $fqdn = 'Laravel\\Boost\\Mcp\\Prompts\\'.$promptFile->getBasename('.php'); - if (class_exists($fqdn) && ! in_array($fqdn, $excludedPrompts, true)) { - $prompts[] = $fqdn; - } - } - } - - $extraPrompts = config('boost.mcp.prompts.include', []); - foreach ($extraPrompts as $promptClass) { - if (class_exists($promptClass)) { - $prompts[] = $promptClass; - } - } - - return $prompts; + return $this->discoverPrimitives([], 'prompts'); } }