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\Extension\Skills\Server;
15:
16: use Nexus\Mcp\Core\Handler\RequestHandlerInterface;
17: use Nexus\Mcp\Core\Schema\Request\ListResourcesRequest;
18: use Nexus\Mcp\Core\Schema\Request\ReadResourceRequest;
19: use Nexus\Mcp\Extension\Skills\Schema\Request\GetSkillRequest;
20: use Nexus\Mcp\Extension\Skills\Schema\Request\ListSkillsRequest;
21: use Nexus\Mcp\Extension\Skills\Schema\Request\ReadResourceDirectoryRequest;
22: use Nexus\Mcp\Extension\Skills\Server\Handler\GetSkillRequestHandler;
23: use Nexus\Mcp\Extension\Skills\Server\Handler\ListSkillsRequestHandler;
24: use Nexus\Mcp\Extension\Skills\Server\Handler\ReadResourceDirectoryRequestHandler;
25: use Nexus\Mcp\Extension\Skills\Server\Handler\SkillFileListResourcesHandler;
26: use Nexus\Mcp\Extension\Skills\Server\Handler\SkillFileReadResourceHandler;
27: use Nexus\Mcp\Extension\Skills\Server\Store\SkillStoreInterface;
28: use Nexus\Mcp\Extension\Skills\Skills;
29: use Nexus\Mcp\Server\Extension\OptionalClientDeclarationInterface;
30: use Nexus\Mcp\Server\Extension\RequestHandlerDecoratorInterface;
31: use Nexus\Mcp\Server\Extension\ServerExtensionInterface;
32:
33: /**
34: * The official skills extension (`io.modelcontextprotocol/skills`, SEP-2640) for the server.
35: */
36: final readonly class SkillsServerExtension implements OptionalClientDeclarationInterface, RequestHandlerDecoratorInterface, ServerExtensionInterface
37: {
38: /**
39: * @param bool $directoryRead Whether `resources/directory/read` is served and advertised
40: */
41: public function __construct(
42: private SkillStoreInterface $store,
43: private bool $directoryRead = true,
44: ) {
45: }
46:
47: #[\Override]
48: public function getIdentifier(): string
49: {
50: return Skills::IDENTIFIER;
51: }
52:
53: #[\Override]
54: public function getSettings(): array
55: {
56: return $this->directoryRead ? [Skills::DIRECTORY_READ_SETTING => true] : [];
57: }
58:
59: #[\Override]
60: public function getRequests(): array
61: {
62: $requests = [ListSkillsRequest::class, GetSkillRequest::class];
63:
64: if ($this->directoryRead) {
65: $requests[] = ReadResourceDirectoryRequest::class;
66: }
67:
68: return $requests;
69: }
70:
71: #[\Override]
72: public function getNotifications(): array
73: {
74: return [];
75: }
76:
77: #[\Override]
78: public function getRequestHandlers(): array
79: {
80: $handlers = [
81: ListSkillsRequest::getMethod() => new ListSkillsRequestHandler($this->store),
82: GetSkillRequest::getMethod() => new GetSkillRequestHandler($this->store),
83: ];
84:
85: if ($this->directoryRead) {
86: $handlers[ReadResourceDirectoryRequest::getMethod()] = new ReadResourceDirectoryRequestHandler($this->store);
87: }
88:
89: return $handlers;
90: }
91:
92: #[\Override]
93: public function getNotificationHandlers(): array
94: {
95: return [];
96: }
97:
98: #[\Override]
99: public function getRequestHandlerDecorators(): array
100: {
101: return [
102: ListResourcesRequest::getMethod() => fn(RequestHandlerInterface $inner): RequestHandlerInterface => new SkillFileListResourcesHandler($inner, $this->store),
103: ReadResourceRequest::getMethod() => fn(RequestHandlerInterface $inner): RequestHandlerInterface => new SkillFileReadResourceHandler($inner, $this->store),
104: ];
105: }
106: }
107: