diff --git a/CHANGELOG.md b/CHANGELOG.md index af2c1057..83668bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ All notable changes to `mcp/sdk` will be documented in this file. * Fix tools with an array shape parameter whose keys are nullable or unions (e.g. `array{name: string, nickname: string|null}`) rejecting valid arguments with `-32602`: a `null` inside the shape no longer turns the parameter into `array|null`, and such keys get their own types instead of `object`, including lists and nested shapes (`string[]|null`, `array{x: int}|null`). * Fix `array` and other generics with a union element getting `items` of type `object`. A `null` inside a generic no longer makes the parameter nullable, and neither does a DocBlock class whose name contains `null`. +### Client + +* Fix `Client::connect()` leaving the previous transport open when called again: it closes the current transport first, so no stdio process or HTTP session is left behind. + 0.9.0 ----- diff --git a/docs/client/connecting.md b/docs/client/connecting.md index 14f6ea7a..20664118 100644 --- a/docs/client/connecting.md +++ b/docs/client/connecting.md @@ -248,6 +248,8 @@ try { } ``` +Calling `connect()` again on the same client closes its current transport before opening the new one. + ## Server Information After successful connection, retrieve server metadata: diff --git a/src/Client.php b/src/Client.php index f70fdeee..993511de 100644 --- a/src/Client.php +++ b/src/Client.php @@ -92,6 +92,8 @@ public static function builder(): Builder */ public function connect(TransportInterface $transport): void { + $this->disconnect(); + $this->transport = $transport; $this->protocol->connect($transport, $this->config); diff --git a/tests/Unit/Client/ClientTest.php b/tests/Unit/Client/ClientTest.php index 29d23ffe..3c6da9a2 100644 --- a/tests/Unit/Client/ClientTest.php +++ b/tests/Unit/Client/ClientTest.php @@ -21,6 +21,29 @@ final class ClientTest extends TestCase { + public function testReconnectClosesPreviousTransport(): void + { + $client = Client::builder()->build(); + $firstTransport = $this->createMock(TransportInterface::class); + $firstTransport->expects($this->once())->method('close'); + $firstTransport->method('setState')->willReturnCallback(static function (ClientStateInterface $state): void { + $state->setInitialized(true); + }); + + $secondTransport = $this->createMock(TransportInterface::class); + $secondTransport->expects($this->once())->method('close'); + $secondTransport->method('setState')->willReturnCallback(static function (ClientStateInterface $state): void { + $state->setInitialized(true); + }); + + $client->connect($firstTransport); + $client->connect($secondTransport); + + $this->assertTrue($client->isConnected()); + + $client->disconnect(); + } + public function testSendRootsListChangedSendsNotificationWhenCapabilityDeclared(): void { $sent = [];