*/ final class ProtocolVersionMiddleware implements MiddlewareInterface { private ResponseFactoryInterface $responseFactory; private StreamFactoryInterface $streamFactory; /** @var list */ private readonly array $supportedVersions; /** * @param list|null $supportedVersions Versions the server accepts. Defaults to {@see ProtocolVersion::handshakeVersions()}; modern revisions are excluded as their per-request negotiation is not served yet. * @param ResponseFactoryInterface|null $responseFactory PSR-17 response factory (auto-discovered if null) * @param StreamFactoryInterface|null $streamFactory PSR-17 stream factory (auto-discovered if null) */ public function __construct( ?array $supportedVersions = null, ?ResponseFactoryInterface $responseFactory = null, ?StreamFactoryInterface $streamFactory = null, ) { $versions = $supportedVersions ?? ProtocolVersion::handshakeVersions(); $this->supportedVersions = array_values(array_map(static fn (ProtocolVersion $v): string => $v->value, $versions)); $this->responseFactory = $responseFactory ?? Psr17FactoryDiscovery::findResponseFactory(); $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory(); } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $headerValue = $request->getHeaderLine(StreamableHttpTransport::PROTOCOL_VERSION_HEADER); // Spec backwards-compat: when the header is absent, the server SHOULD assume // protocol version 2025-03-26 — the release in which Streamable HTTP and the // header itself were introduced. This is deliberately lower than the SDK's // own default so clients predating the header convention still get a // deterministic protocol version applied. Servers that whitelist only newer // versions in $supportedVersions will reject such requests with 400. $version = '' === $headerValue ? ProtocolVersion::DEFAULT_HEADER_VERSION->value : $headerValue; if (\in_array($version, $this->supportedVersions, true)) { return $handler->handle($request); } $message = '' === $headerValue ? \sprintf( 'Missing %s header; backwards-compat default %s is not accepted. Supported versions: %s.', StreamableHttpTransport::PROTOCOL_VERSION_HEADER, $version, implode(', ', $this->supportedVersions), ) : \sprintf( 'Unsupported %s header value: %s. Supported versions: %s.', StreamableHttpTransport::PROTOCOL_VERSION_HEADER, $headerValue, implode(', ', $this->supportedVersions), ); return JsonRpcErrorResponse::create($this->responseFactory, $this->streamFactory, 400, Error::forInvalidParams($message)); } }