[Schema][Server] Add the extensions framework SEP-2133 defines (#443)
* [Schema][Server] Add the extensions framework SEP-2133 defines enableExtension() took any string and advertised it, with no way for an extension to add a method: MessageFactory could not decode one, so nothing downstream ever saw it. Identifiers are now checked against the _meta key naming rules through ExtensionIdentifier - a prefix is mandatory, since an unprefixed name has no owner, and the modelcontextprotocol/mcp second labels the specification reserves are recognised. An extension implementing MethodProvidingExtensionInterface contributes both its message classes and the handlers serving them. MessageFactory::make() takes an $additional list of message classes, and RequestHandlerInterface's result template is covariant so a handler declaring a concrete result satisfies a collection typed by the interface. * Fix stale @see reference in MethodProvidingExtensionInterface docblock * [Schema][Server][Client] Make ExtensionIdentifier a value object ExtensionInterface::getId() now returns ExtensionIdentifier, which validates the SEP-2133 naming rules at construction instead of the callers checking it. getMessages()/getRequestHandlers() move into ExtensionInterface itself; AbstractExtension gives them empty defaults for extensions that only announce a capability.
This commit is contained in:
parent
5e3e69acc4
commit
60711951d4
4 changed files with 525 additions and 17 deletions
47
tests/Unit/Server/Extension/ThingExtension.php
Normal file
47
tests/Unit/Server/Extension/ThingExtension.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?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\Server\Extension;
|
||||
|
||||
use Mcp\Schema\Extension\ExtensionIdentifier;
|
||||
use Mcp\Schema\Extension\ExtensionInterface;
|
||||
|
||||
/**
|
||||
* A minimal extension: an identifier, a capability payload, and one method it
|
||||
* defines and serves.
|
||||
*/
|
||||
final class ThingExtension implements ExtensionInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $id = 'com.example/things',
|
||||
) {
|
||||
}
|
||||
|
||||
public function getId(): ExtensionIdentifier
|
||||
{
|
||||
return new ExtensionIdentifier($this->id);
|
||||
}
|
||||
|
||||
public function getCapabilities(): array
|
||||
{
|
||||
return ['flavour' => 'vanilla'];
|
||||
}
|
||||
|
||||
public function getMessages(): array
|
||||
{
|
||||
return [ThingListRequest::class];
|
||||
}
|
||||
|
||||
public function getRequestHandlers(): iterable
|
||||
{
|
||||
yield new ThingListHandler();
|
||||
}
|
||||
}
|
||||
33
tests/Unit/Server/Extension/ThingListHandler.php
Normal file
33
tests/Unit/Server/Extension/ThingListHandler.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?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\Server\Extension;
|
||||
|
||||
use Mcp\Schema\JsonRpc\Request;
|
||||
use Mcp\Schema\JsonRpc\Response;
|
||||
use Mcp\Server\Handler\Request\RequestHandlerInterface;
|
||||
use Mcp\Server\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* @implements RequestHandlerInterface<ThingListResult>
|
||||
*/
|
||||
final class ThingListHandler implements RequestHandlerInterface
|
||||
{
|
||||
public function supports(Request $request): bool
|
||||
{
|
||||
return $request instanceof ThingListRequest;
|
||||
}
|
||||
|
||||
public function handle(Request $request, SessionInterface $session): Response
|
||||
{
|
||||
return new Response($request->getId(), new ThingListResult(['a', 'b']));
|
||||
}
|
||||
}
|
||||
32
tests/Unit/Server/Extension/ThingListRequest.php
Normal file
32
tests/Unit/Server/Extension/ThingListRequest.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?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\Server\Extension;
|
||||
|
||||
use Mcp\Schema\JsonRpc\Request;
|
||||
|
||||
final class ThingListRequest extends Request
|
||||
{
|
||||
public static function getMethod(): string
|
||||
{
|
||||
return 'com.example/things.list';
|
||||
}
|
||||
|
||||
protected static function fromParams(?array $params): static
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
protected function getParams(): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
33
tests/Unit/Server/Extension/ThingListResult.php
Normal file
33
tests/Unit/Server/Extension/ThingListResult.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?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\Server\Extension;
|
||||
|
||||
use Mcp\Schema\JsonRpc\ResultInterface;
|
||||
|
||||
final class ThingListResult implements ResultInterface
|
||||
{
|
||||
/**
|
||||
* @param list<string> $things
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly array $things,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{things: list<string>}
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return ['things' => $this->things];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue