php-sdk/tests/Unit/Client/BuilderTest.php
Christopher Hertel 6f58fe596a
[Schema][Client][Server] Close MCP Apps gaps: extension negotiation checks (#429)
* [Schema][Client][Server] Close MCP Apps gaps: extension negotiation checks

Add ClientGateway::supportsExtension(), Client\Builder::enableExtension() and
ClientCapabilities::withExtensions(); introduce side-agnostic ExtensionInterface
and deprecate ServerExtensionInterface; document attribute-based UI linkage.

* Drop ServerExtensionInterface in favour of ExtensionInterface
2026-08-18 01:46:53 +02:00

91 lines
3.2 KiB
PHP

<?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\Unit\Client;
use Mcp\Client;
use Mcp\Client\Configuration;
use Mcp\Exception\LogicException;
use Mcp\Schema\ClientCapabilities;
use Mcp\Schema\Extension\Apps\McpApps;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
final class BuilderTest extends TestCase
{
#[TestDox('setClientInfo() forwards the title to the advertised clientInfo')]
public function testSetClientInfoForwardsTitle(): void
{
$client = Client::builder()
->setClientInfo('my-client', '1.0.0', 'A test client', 'My Client')
->build();
$clientInfo = $this->extractConfiguration($client)->clientInfo;
$this->assertSame('my-client', $clientInfo->name);
$this->assertSame('1.0.0', $clientInfo->version);
$this->assertSame('A test client', $clientInfo->description);
$this->assertSame('My Client', $clientInfo->title);
}
#[TestDox('setClientInfo() leaves the title absent when it is not given')]
public function testSetClientInfoTitleIsOptional(): void
{
$client = Client::builder()
->setClientInfo('my-client', '1.0.0')
->build();
$this->assertNull($this->extractConfiguration($client)->clientInfo->title);
}
#[TestDox('enableExtension() announces the extension under capabilities.extensions')]
public function testEnableExtensionAdvertisesExtension(): void
{
$client = Client::builder()
->enableExtension(new McpApps())
->build();
$capabilities = $this->extractConfiguration($client)->capabilities;
$this->assertSame([McpApps::EXTENSION_ID => ['mimeTypes' => [McpApps::MIME_TYPE]]], $capabilities->extensions);
}
#[TestDox('enableExtension() extensions are merged into capabilities set via setCapabilities()')]
public function testEnableExtensionMergesIntoCustomCapabilities(): void
{
$client = Client::builder()
->setCapabilities(new ClientCapabilities(elicitation: true, extensions: ['io.example/other' => []]))
->enableExtension(new McpApps())
->build();
$capabilities = $this->extractConfiguration($client)->capabilities;
$this->assertTrue($capabilities->elicitation);
$this->assertSame(['io.example/other', McpApps::EXTENSION_ID], array_keys($capabilities->extensions ?? []));
}
#[TestDox('enableExtension() throws when the same extension is enabled twice')]
public function testEnableExtensionRejectsDuplicate(): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage(McpApps::EXTENSION_ID);
Client::builder()->enableExtension(new McpApps(), new McpApps());
}
private function extractConfiguration(Client $client): Configuration
{
$config = (new \ReflectionClass($client))->getProperty('config')->getValue($client);
$this->assertInstanceOf(Configuration::class, $config);
return $config;
}
}