|
25 | 25 | use Mcp\Exception\PromptNotFoundException; |
26 | 26 | use Mcp\Exception\ResourceNotFoundException; |
27 | 27 | use Mcp\Exception\ToolNotFoundException; |
| 28 | +use Mcp\Schema\Notification\ResourceUpdatedNotification; |
28 | 29 | use Mcp\Schema\Page; |
29 | 30 | use Mcp\Schema\Prompt; |
30 | 31 | use Mcp\Schema\Resource; |
31 | 32 | use Mcp\Schema\ResourceTemplate; |
32 | 33 | use Mcp\Schema\Tool; |
| 34 | +use Mcp\Server\Protocol; |
| 35 | +use Mcp\Server\Session\SessionFactoryInterface; |
| 36 | +use Mcp\Server\Session\SessionInterface; |
| 37 | +use Mcp\Server\Session\SessionStoreInterface; |
33 | 38 | use Psr\EventDispatcher\EventDispatcherInterface; |
34 | 39 | use Psr\Log\LoggerInterface; |
35 | 40 | use Psr\Log\NullLogger; |
| 41 | +use Psr\SimpleCache\InvalidArgumentException; |
36 | 42 |
|
37 | 43 | /** |
38 | 44 | * Registry implementation that manages MCP element registration and access. |
@@ -64,6 +70,8 @@ final class Registry implements RegistryInterface |
64 | 70 | public function __construct( |
65 | 71 | private readonly ?EventDispatcherInterface $eventDispatcher = null, |
66 | 72 | private readonly LoggerInterface $logger = new NullLogger(), |
| 73 | + private readonly ?SessionStoreInterface $sessionStore = null, |
| 74 | + private readonly ?SessionFactoryInterface $sessionFactory = null, |
67 | 75 | private readonly NameValidator $nameValidator = new NameValidator(), |
68 | 76 | ) { |
69 | 77 | } |
@@ -391,6 +399,64 @@ public function setDiscoveryState(DiscoveryState $state): void |
391 | 399 | } |
392 | 400 | } |
393 | 401 |
|
| 402 | + /** |
| 403 | + * @throws InvalidArgumentException |
| 404 | + */ |
| 405 | + public function subscribe(SessionInterface $session, string $uri): void |
| 406 | + { |
| 407 | + $subscriptions = $session->get('resource_subscriptions', []); |
| 408 | + $subscriptions[$uri] = true; |
| 409 | + $session->set('resource_subscriptions', $subscriptions); |
| 410 | + $session->save(); |
| 411 | + } |
| 412 | + |
| 413 | + /** |
| 414 | + * @throws InvalidArgumentException |
| 415 | + */ |
| 416 | + public function unsubscribe(SessionInterface $session, string $uri): void |
| 417 | + { |
| 418 | + $subscriptions = $session->get('resource_subscriptions', []); |
| 419 | + unset($subscriptions[$uri]); |
| 420 | + $session->set('resource_subscriptions', $subscriptions); |
| 421 | + $session->save(); |
| 422 | + } |
| 423 | + |
| 424 | + public function notifyResourceChanged(Protocol $protocol, string $uri): void |
| 425 | + { |
| 426 | + if (!$this->sessionStore || !$this->sessionFactory) { |
| 427 | + $this->logger->warning('Cannot send resource notifications: session store or factory not configured.'); |
| 428 | + |
| 429 | + return; |
| 430 | + } |
| 431 | + |
| 432 | + foreach ($this->sessionStore->getAllSessionIds() as $sessionId) { |
| 433 | + try { |
| 434 | + $sessionData = $this->sessionStore->read($sessionId); |
| 435 | + if (!$sessionData) { |
| 436 | + continue; |
| 437 | + } |
| 438 | + |
| 439 | + $sessionArray = json_decode($sessionData, true); |
| 440 | + if (!\is_array($sessionArray)) { |
| 441 | + continue; |
| 442 | + } |
| 443 | + |
| 444 | + if (!isset($sessionArray['resource_subscriptions'][$uri])) { |
| 445 | + continue; |
| 446 | + } |
| 447 | + |
| 448 | + $session = $this->sessionFactory->createWithId($sessionId, $this->sessionStore); |
| 449 | + $protocol->sendNotification(new ResourceUpdatedNotification($uri), $session); |
| 450 | + } catch (\Throwable $e) { |
| 451 | + $this->logger->error('Error sending resource notification to session', [ |
| 452 | + 'session_id' => $sessionId->toRfc4122(), |
| 453 | + 'uri' => $uri, |
| 454 | + 'exception' => $e, |
| 455 | + ]); |
| 456 | + } |
| 457 | + } |
| 458 | + } |
| 459 | + |
394 | 460 | /** |
395 | 461 | * Calculate next cursor for pagination. |
396 | 462 | * |
|
0 commit comments