Add tool name validation for SEP-986 (#140)

This commit is contained in:
Christopher Hertel 2025-11-13 21:42:30 +01:00 • committed by GitHub
commit 7a8fbe9b20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 96 additions and 9 deletions

View file

@ -18,6 +18,7 @@ use Mcp\Capability\Registry\ReferenceRegistryInterface;
use Mcp\Capability\Registry\ResourceReference;
use Mcp\Capability\Registry\ResourceTemplateReference;
use Mcp\Capability\Registry\ToolReference;
use Mcp\Capability\Tool\NameValidator;
use Mcp\Event\PromptListChangedEvent;
use Mcp\Event\ResourceListChangedEvent;
use Mcp\Event\ResourceTemplateListChangedEvent;
@ -71,6 +72,7 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
public function __construct(
private readonly ?EventDispatcherInterface $eventDispatcher = null,
private readonly LoggerInterface $logger = new NullLogger(),
private readonly NameValidator $nameValidator = new NameValidator(),
) {
}
@ -100,12 +102,18 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
if ($existing && !$isManual && $existing->isManual) {
$this->logger->debug(
"Ignoring discovered tool '{$toolName}' as it conflicts with a manually registered one.",
\sprintf('Ignoring discovered tool "%s" as it conflicts with a manually registered one.', $toolName),
);
return;
}
if (!$this->nameValidator->isValid($toolName)) {
$this->logger->warning(
\sprintf('Tool name "%s" is invalid. Tool names should only contain letters (a-z, A-Z), numbers, dots, hyphens, underscores, and forward slashes.', $toolName),
);
}
$this->tools[$toolName] = new ToolReference($tool, $handler, $isManual);
$this->eventDispatcher?->dispatch(new ToolListChangedEvent());
@ -118,7 +126,7 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
if ($existing && !$isManual && $existing->isManual) {
$this->logger->debug(
"Ignoring discovered resource '{$uri}' as it conflicts with a manually registered one.",
\sprintf('Ignoring discovered resource "%s" as it conflicts with a manually registered one.', $uri),
);
return;
@ -140,7 +148,7 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
if ($existing && !$isManual && $existing->isManual) {
$this->logger->debug(
"Ignoring discovered template '{$uriTemplate}' as it conflicts with a manually registered one.",
\sprintf('Ignoring discovered template "%s" as it conflicts with a manually registered one.', $uriTemplate),
);
return;
@ -167,7 +175,7 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
if ($existing && !$isManual && $existing->isManual) {
$this->logger->debug(
"Ignoring discovered prompt '{$promptName}' as it conflicts with a manually registered one.",
\sprintf('Ignoring discovered prompt "%s" as it conflicts with a manually registered one.', $promptName),
);
return;

View file

@ -0,0 +1,20 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Capability\Tool;
final class NameValidator
{
public function isValid(string $name): bool
{
return 1 === preg_match('/^[a-zA-Z0-9._\/-]{1,64}$/', $name);
}
}