From 05338f4a43f9dfb39a1bb2826e2535641762fdbb Mon Sep 17 00:00:00 2001 From: "isabel.harms" Date: Fri, 21 Aug 2026 11:52:28 +0200 Subject: [PATCH] Adapt SDK to enerchart permission system ReferenceHandler: Set Claims of Primitive class instance CompletionCompleteHandler: Set Claims of CompletionProvider instance Discoverer: Filter Primitives by McpPermissions Attribute --- src/Capability/Discovery/Discoverer.php | 53 +++++++++++++++++++ src/Capability/Registry/ReferenceHandler.php | 7 +++ .../Request/CompletionCompleteHandler.php | 9 ++++ 3 files changed, 69 insertions(+) diff --git a/src/Capability/Discovery/Discoverer.php b/src/Capability/Discovery/Discoverer.php index dab6590c..6b94094e 100644 --- a/src/Capability/Discovery/Discoverer.php +++ b/src/Capability/Discovery/Discoverer.php @@ -30,6 +30,8 @@ use Mcp\Schema\ResourceDefinition; use Mcp\Schema\ResourceTemplate; use Mcp\Schema\Tool; +use McpClient\Entity\ClientConfig; +use McpServer\Attributes\McpPermissions; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\Finder\Finder; @@ -53,6 +55,7 @@ public function __construct( private readonly LoggerInterface $logger = new NullLogger(), private ?DocBlockParser $docBlockParser = null, private ?SchemaGeneratorInterface $schemaGenerator = null, + private readonly ?ClientConfig $clientConfig = null, ) { if (!class_exists(Finder::class)) { throw new RuntimeException('File-based discovery requires symfony/finder. Run: composer require symfony/finder'); @@ -175,6 +178,9 @@ private function processFile(SplFileInfo $file, array &$discoveredCount, array & if (!$processedViaClassAttribute) { foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if ($this->clientConfig && !$this->matchesClaims($method)) { + continue; + } if ( $method->getDeclaringClass()->getName() !== $reflectionClass->getName() || $method->isStatic() || $method->isAbstract() || $method->isConstructor() || $method->isDestructor() || '__invoke' === $method->getName() @@ -452,4 +458,51 @@ private function getClassFromFile(SplFileInfo $file): ?string return null; } + + /** + * @param \ReflectionMethod $method + * @return bool + */ + private function matchesClaims(\ReflectionMethod $method): bool + { + $attrs = $method->getAttributes(McpPermissions::class); + + if (empty($attrs)) { + return false; + } + + /** @var McpPermissions $permissionAttrs */ + $permissionAttrs = $attrs[0]->newInstance(); + + foreach (get_object_vars($permissionAttrs) as $key => $allowedValues) { + //skip attributes that aren't specified + if ($allowedValues === null) { + continue; + } + + switch ($key) { + case 'permissions': + $claimValue = $this->clientConfig->getPermissions(); + if (empty(array_intersect($allowedValues, $claimValue))) { + return false; + } + break; + case 'language': + $claimValue = $this->clientConfig->getLanguage()->getLanguageCode(); + if (!in_array($claimValue, $allowedValues, false)) { + return false; + } + break; + case 'clientOrigin': + $claimValue = $this->clientConfig->getClientOrigin(); + if (!in_array($claimValue, $allowedValues, false)) { + return false; + } + break; + default: + return false; + } + } + return true; + } } diff --git a/src/Capability/Registry/ReferenceHandler.php b/src/Capability/Registry/ReferenceHandler.php index 99e58442..13cc7190 100644 --- a/src/Capability/Registry/ReferenceHandler.php +++ b/src/Capability/Registry/ReferenceHandler.php @@ -16,6 +16,9 @@ use Mcp\Server\ClientGateway; use Mcp\Server\RequestContext; use Mcp\Server\Session\SessionInterface; +use McpClient\Entity\ClientConfig; +use McpServer\Primitives\AbstractClaimsAwarePrimitive; +use McpServer\Session\SessionClaimsService; use Psr\Container\ContainerInterface; /** @@ -72,6 +75,10 @@ public function handle(ElementReference $reference, array $arguments): mixed [$className, $methodName] = $reference->handler; $reflection = new \ReflectionMethod($className, $methodName); $instance = $this->getClassInstance($className); + $claims = $session? $this->container->get(SessionClaimsService::class)->getClaims($session):null; + if ($claims instanceof ClientConfig && $instance instanceof AbstractClaimsAwarePrimitive) { + ($instance)->setClaims($claims); + } $arguments = $this->prepareArguments($reflection, $arguments); return \call_user_func([$instance, $methodName], ...$arguments); diff --git a/src/Server/Handler/Request/CompletionCompleteHandler.php b/src/Server/Handler/Request/CompletionCompleteHandler.php index a1eab00b..76ae6a90 100644 --- a/src/Server/Handler/Request/CompletionCompleteHandler.php +++ b/src/Server/Handler/Request/CompletionCompleteHandler.php @@ -24,6 +24,8 @@ use Mcp\Schema\ResourceReference; use Mcp\Schema\Result\CompletionCompleteResult; use Mcp\Server\Session\SessionInterface; +use McpServer\Primitives\AbstractClaimsAwarePrimitive; +use McpServer\Session\SessionClaimsService; use Psr\Container\ContainerInterface; /** @@ -77,6 +79,13 @@ public function handle(Request $request, SessionInterface $session): Response|Er if (!$provider instanceof ProviderInterface) { return Error::forInternalError('Invalid completion provider type', $request->getId()); } + if ($provider instanceof AbstractClaimsAwarePrimitive) { + $claimsService = $this->container->get(SessionClaimsService::class); + $claims = $claimsService->getClaims($session); + if ($claims) { + $provider->setClaims($claims); + } + } $completions = $provider->getCompletions($value); $total = \count($completions);