1: <?php
2:
3: declare(strict_types=1);
4:
5: /**
6: * This file is part of the Nexus MCP SDK package.
7: *
8: * (c) 2026 John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9: *
10: * For the full copyright and license information, please view
11: * the LICENSE file that was distributed with this source code.
12: */
13:
14: namespace Nexus\Mcp\Core\Auth;
15:
16: /**
17: * The OAuth 2.0 Protected Resource Metadata document an MCP server publishes.
18: *
19: * @see https://datatracker.ietf.org/doc/html/rfc9728#section-2
20: */
21: final readonly class ProtectedResourceMetadata
22: {
23: /**
24: * @var non-empty-list<non-empty-string>
25: */
26: public array $authorizationServers;
27:
28: /**
29: * @param list<non-empty-string> $authorizationServers
30: * @param null|list<non-empty-string> $bearerMethodsSupported
31: * @param null|non-empty-string $resourceName
32: */
33: public function __construct(
34: public ResourceIdentifier $resource,
35: array $authorizationServers,
36: public ?ScopeSet $scopesSupported = null,
37: public ?array $bearerMethodsSupported = null,
38: public ?string $resourceName = null,
39: ) {
40: if ([] === $authorizationServers) {
41: throw new \InvalidArgumentException('Protected Resource Metadata must name at least one authorization server.');
42: }
43:
44: $this->authorizationServers = $authorizationServers;
45: }
46:
47: /**
48: * @param array<string, mixed> $data
49: */
50: public static function fromArray(array $data): self
51: {
52: $reader = new MetadataReader('Protected Resource Metadata');
53: $scopes = $reader->readStringList($data, 'scopes_supported');
54:
55: return new self(
56: new ResourceIdentifier($reader->readRequiredString($data, 'resource')),
57: $reader->readStringList($data, 'authorization_servers') ?? [],
58: null === $scopes ? null : ScopeSet::fromList($scopes),
59: $reader->readStringList($data, 'bearer_methods_supported'),
60: $reader->readString($data, 'resource_name'),
61: );
62: }
63:
64: /**
65: * @return array<string, mixed>
66: */
67: public function toArray(): array
68: {
69: $data = [
70: 'resource' => $this->resource->value,
71: 'authorization_servers' => $this->authorizationServers,
72: ];
73:
74: if (null !== $this->scopesSupported) {
75: $data['scopes_supported'] = $this->scopesSupported->values;
76: }
77:
78: if (null !== $this->bearerMethodsSupported) {
79: $data['bearer_methods_supported'] = $this->bearerMethodsSupported;
80: }
81:
82: if (null !== $this->resourceName) {
83: $data['resource_name'] = $this->resourceName;
84: }
85:
86: return $data;
87: }
88: }
89: