php-sdk/src/Server/Transport/Http/OAuth/ProtectedResourceMetadataHandler.php
Christopher Hertel 0058d93086
[Server] Expose OAuth protected resource metadata as a reusable request handler (#391)
* [Server] Expose OAuth protected resource metadata as a reusable request handler

* docs: show handler DI wiring in Symfony/Laravel controller examples

* Focus PR on transport refactor: drop docs and example changes

* docs: add changelog entry for ProtectedResourceMetadataHandler
2026-07-15 00:24:04 +02:00

60 lines
2.4 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\Server\Transport\Http\OAuth;
use Http\Discovery\Psr17FactoryDiscovery;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
* Serves OAuth 2.0 Protected Resource Metadata (RFC 9728) as a standalone request handler.
*
* This is a plain PSR-15 {@see RequestHandlerInterface} — the "controller" that decides *what*
* to return, independent of *when*. It can be used three ways from a single instance:
*
* - inside the MCP transport, wrapped by {@see \Mcp\Server\Transport\Http\Middleware\ProtectedResourceMetadataMiddleware};
* - as a bare PSR-7 handler in a hand-rolled front controller;
* - as a framework callable controller (Symfony/Laravel), by converting the framework
* request to PSR-7 and the returned PSR-7 response back — see docs/authorization.md.
*
* It performs no path or method matching: routing is the caller's responsibility (the
* middleware's guard, or the framework router).
*
* @see https://datatracker.ietf.org/doc/html/rfc9728
*
* @author Volodymyr Panivko <sveneld300@gmail.com>
*/
final class ProtectedResourceMetadataHandler implements RequestHandlerInterface
{
private ResponseFactoryInterface $responseFactory;
private StreamFactoryInterface $streamFactory;
public function __construct(
private readonly ProtectedResourceMetadata $metadata,
?ResponseFactoryInterface $responseFactory = null,
?StreamFactoryInterface $streamFactory = null,
) {
$this->responseFactory = $responseFactory ?? Psr17FactoryDiscovery::findResponseFactory();
$this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory();
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
return $this->responseFactory
->createResponse(200)
->withHeader('Content-Type', 'application/json')
->withBody($this->streamFactory->createStream(json_encode($this->metadata, \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES)));
}
}