From 8ec4fc8ebff5fb2b18005d97ff0b5f6fb8a8094b Mon Sep 17 00:00:00 2001 From: Kyrian Obikwelu Date: Fri, 13 Jun 2025 07:58:44 +0100 Subject: [PATCH] fix: Correct client state handling in LaravelHttpTransport Removes in-memory active client tracking (`$activeClients` array and associated event listeners) from `LaravelHttpTransport`. This local tracking was unreliable due to Laravel's typical request-response lifecycle where the transport instance is re-initialized per interaction. The `ClientStateManager`, which uses a persistent cache, is already responsible for managing active client state and activity across requests. The `LaravelHttpTransport` now correctly relies solely on `ClientStateManager` for queuing messages (`sendToClientAsync`) and updates client activity via `ClientStateManager` when messages are received. --- src/Transports/LaravelHttpTransport.php | 28 +------------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/Transports/LaravelHttpTransport.php b/src/Transports/LaravelHttpTransport.php index 0fe7043..29ccf3f 100644 --- a/src/Transports/LaravelHttpTransport.php +++ b/src/Transports/LaravelHttpTransport.php @@ -7,13 +7,11 @@ use Evenement\EventEmitterTrait; use PhpMcp\Server\Contracts\LoggerAwareInterface; use PhpMcp\Server\Contracts\ServerTransportInterface; -use PhpMcp\Server\Exception\TransportException; use PhpMcp\Server\State\ClientStateManager; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use React\Promise\PromiseInterface; -use function React\Promise\reject; use function React\Promise\resolve; class LaravelHttpTransport implements ServerTransportInterface, LoggerAwareInterface @@ -24,23 +22,11 @@ class LaravelHttpTransport implements ServerTransportInterface, LoggerAwareInter protected ClientStateManager $clientStateManager; - /** @var array Tracks active client IDs managed by this transport */ - private array $activeClients = []; - public function __construct(ClientStateManager $clientStateManager) { $this->clientStateManager = $clientStateManager; $this->logger = new NullLogger; - $this->on('client_connected', function (string $clientId) { - $this->activeClients[$clientId] = true; - $this->clientStateManager->updateClientActivity($clientId); - }); - - $this->on('client_disconnected', function (string $clientId, string $reason) { - unset($this->activeClients[$clientId]); - }); - $this->on('message', function (string $message, string $clientId) { $this->clientStateManager->updateClientActivity($clientId); }); @@ -68,12 +54,6 @@ public function listen(): void */ public function sendToClientAsync(string $clientId, string $rawFramedMessage): PromiseInterface { - if (! isset($this->activeClients[$clientId])) { - $this->logger->warning('Attempted to send message to inactive or unknown client.', ['clientId' => $clientId]); - - return reject(new TransportException("Client '{$clientId}' is not actively managed by this transport.")); - } - $messagePayload = rtrim($rawFramedMessage, "\n"); if (empty($messagePayload)) { @@ -90,13 +70,7 @@ public function sendToClientAsync(string $clientId, string $rawFramedMessage): P */ public function close(): void { - $activeClientIds = array_keys($this->activeClients); - - foreach ($activeClientIds as $clientId) { - $this->emit('client_disconnected', [$clientId, 'Transport globally closed']); - $this->emit('close', ['Transport closed.']); - } - + $this->emit('close', ['Transport closed.']); $this->removeAllListeners(); } }