[Client] Implement setMaxRetries connection retries (#413)

* [Client] Implement setMaxRetries connection retries

* [Client] Reject a negative retry count in Configuration

* [Client] Use a positive init timeout in the retry test

* [Client] Drop redundant docblock note on setMaxRetries

* [Client] Clear the initialized flag on a failed connection

* [Client] Trim comments to the non-obvious ones

* [Client] Cover retries and timeout cleanup with integration tests

The retry fixture counts the processes it was started as, which is what
separates a real retry from a second call into the same server. The timeout
fixture outlives the client's patience and then goes idle, so the next
request runs on a connection the client already gave up on once.
This commit is contained in:
Christopher Hertel 2026-08-15 02:43:50 +02:00 • committed by GitHub
commit d8cc21a345
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 616 additions and 26 deletions

View file

@ -0,0 +1,42 @@
<?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.
*/
/*
* Server for {@see \Mcp\Tests\Integration\RequestTimeoutTest}.
*
* `slow` outlives the client's request timeout and then goes idle again, which
* is what lets the test call `fast` on a connection the client has already
* given up on once.
*/
use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;
require_once dirname(__DIR__, 3).'/vendor/autoload.php';
Server::builder()
->setServerInfo('integration-server', '1.0.0')
->addTool(
static function (): string {
sleep(2);
return 'late';
},
name: 'slow',
description: 'Answers well after the client stopped waiting.',
)
->addTool(
static fn (): string => 'quick',
name: 'fast',
description: 'Returns at once.',
)
->build()
->run(new StdioTransport());

View file

@ -0,0 +1,46 @@
<?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.
*/
/*
* Server for {@see \Mcp\Tests\Integration\RetryTest}.
*
* Dies before speaking a frame while the spawn counter is below the configured
* number of failures, so the counter file records how many processes the client
* actually started.
*/
use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;
require_once dirname(__DIR__, 3).'/vendor/autoload.php';
$counter = getenv('MCP_SPAWN_COUNTER');
if (!is_string($counter) || '' === $counter) {
exit(1);
}
$spawns = 1 + (int) file_get_contents($counter);
file_put_contents($counter, (string) $spawns);
if ($spawns <= (int) getenv('MCP_FAIL_SPAWNS')) {
exit(1);
}
Server::builder()
->setServerInfo('integration-server', '1.0.0')
->addTool(
static fn (): string => 'quick',
name: 'fast',
description: 'Returns at once.',
)
->build()
->run(new StdioTransport());