<?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.
*/
namespace Mcp\Tests\Integration;
use Mcp\Exception\ConnectionException;
use PHPUnit\Framework\Attributes\TestDox;
* Connection retries, counted in processes rather than calls.
*
* A retry is only worth anything if it starts a fresh server, which is what the
* spawn counter pins down: the fixture bumps it on every start, so the test can
* tell a genuine second process from a second call into the same one.
*
* A failed attempt costs the init timeout, so these are the slowest tests here.
*
* @see Fixture/retry.php for the server under test
*/
final class RetryTest extends IntegrationTestCase
{
private string $counter;
protected function setUp(): void
{
$counter = tempnam(sys_get_temp_dir(), 'mcp-spawns');
if (false === $counter) {
$this->fail('Could not create the spawn counter file.');
}
$this->counter = $counter;
file_put_contents($this->counter, '0');
}
protected function tearDown(): void
{
parent::tearDown();
@unlink($this->counter);
}
#[TestDox('a failed attempt is retried against a newly spawned server')]
public function testRetrySpawnsAnotherServer(): void
{
$client = $this->connect(
'retry',
$this->clientBuilder()->setInitTimeout(2)->setMaxRetries(1),
$this->environment(failing: 1),
);
$this->assertSame(2, $this->spawns());
$this->assertTrue($client->isConnected());
$this->assertSame('quick', $client->callTool('fast')->content[0]->text ?? null);
}
#[TestDox('no retries means the first failure is the last word')]
public function testRetriesCanBeDisabled(): void
{
$client = $this->clientBuilder()->setInitTimeout(2)->setMaxRetries(0)->build();
try {
$client->connect($this->transport('retry', $this->environment(failing: 1)));
$this->fail('Connecting should not have succeeded.');
} catch (ConnectionException) {
$this->assertSame(1, $this->spawns());
}
}
* @return array<string, string>
*/
private function environment(int $failing): array
{
return [
'MCP_SPAWN_COUNTER' => $this->counter,
'MCP_FAIL_SPAWNS' => (string) $failing,
];
}
private function spawns(): int
{
return (int) file_get_contents($this->counter);
}
}