[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
This commit is contained in:
parent
590afb575b
commit
6f58fe596a
1 changed files with 273 additions and 21 deletions
|
|
@ -14,8 +14,10 @@ namespace Mcp\Client;
|
|||
use Mcp\Client;
|
||||
use Mcp\Client\Handler\Notification\NotificationHandlerInterface;
|
||||
use Mcp\Client\Handler\Request\RequestHandlerInterface;
|
||||
use Mcp\Exception\LogicException;
|
||||
use Mcp\Schema\ClientCapabilities;
|
||||
use Mcp\Schema\Enum\ProtocolVersion;
|
||||
use Mcp\Schema\Extension\ExtensionInterface;
|
||||
use Mcp\Schema\Implementation;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
|
@ -33,6 +35,9 @@ final class Builder
|
|||
private ?string $title = null;
|
||||
private ?ProtocolVersion $protocolVersion = null;
|
||||
private ?ClientCapabilities $capabilities = null;
|
||||
|
||||
/** @var array<string, array<string, mixed>> */
|
||||
private array $extensions = [];
|
||||
private int $initTimeout = 30;
|
||||
private int $requestTimeout = 120;
|
||||
private int $maxRetries = 3;
|
||||
|
|
@ -79,6 +84,27 @@ final class Builder
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable one or more MCP protocol extensions, announced to the server under
|
||||
* `capabilities.extensions` in the initialize request.
|
||||
*
|
||||
* @throws LogicException if the same extension is enabled more than once
|
||||
*/
|
||||
public function enableExtension(ExtensionInterface ...$extensions): self
|
||||
{
|
||||
foreach ($extensions as $extension) {
|
||||
$id = $extension->getId();
|
||||
|
||||
if (isset($this->extensions[$id])) {
|
||||
throw new LogicException(\sprintf('Extension "%s" is already enabled.', $id));
|
||||
}
|
||||
|
||||
$this->extensions[$id] = $extension->getCapabilities();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initialization timeout in seconds.
|
||||
*/
|
||||
|
|
@ -159,9 +185,17 @@ final class Builder
|
|||
title: $this->title,
|
||||
);
|
||||
|
||||
$capabilities = $this->capabilities ?? new ClientCapabilities();
|
||||
|
||||
// Extensions enabled via enableExtension() are folded into caller-supplied
|
||||
// capabilities too, so setCapabilities() does not silently drop them.
|
||||
if ([] !== $this->extensions) {
|
||||
$capabilities = $capabilities->withExtensions($this->extensions);
|
||||
}
|
||||
|
||||
$config = new Configuration(
|
||||
clientInfo: $clientInfo,
|
||||
capabilities: $this->capabilities ?? new ClientCapabilities(),
|
||||
capabilities: $capabilities,
|
||||
protocolVersion: $this->protocolVersion ?? ProtocolVersion::V2025_11_25,
|
||||
initTimeout: $this->initTimeout,
|
||||
requestTimeout: $this->requestTimeout,
|
||||
|
|
|
|||
Loading…
Reference in a new issue