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\Tasks\Server;
15:
16: use Nexus\Mcp\Core\Handler\RequestHandlerInterface;
17: use Nexus\Mcp\Core\Schema\Request\CallToolRequest;
18: use Nexus\Mcp\Extension\Tasks\Schema\Request\CancelTaskRequest;
19: use Nexus\Mcp\Extension\Tasks\Schema\Request\GetTaskRequest;
20: use Nexus\Mcp\Extension\Tasks\Schema\Request\UpdateTaskRequest;
21: use Nexus\Mcp\Extension\Tasks\Server\Handler\CancelTaskRequestHandler;
22: use Nexus\Mcp\Extension\Tasks\Server\Handler\GetTaskRequestHandler;
23: use Nexus\Mcp\Extension\Tasks\Server\Handler\TaskBrokeringCallToolHandler;
24: use Nexus\Mcp\Extension\Tasks\Server\Handler\UpdateTaskRequestHandler;
25: use Nexus\Mcp\Extension\Tasks\Server\Store\InMemoryTaskStore;
26: use Nexus\Mcp\Extension\Tasks\Server\Store\TaskStoreInterface;
27: use Nexus\Mcp\Extension\Tasks\Tasks;
28: use Nexus\Mcp\Server\Extension\RequestHandlerDecoratorInterface;
29: use Nexus\Mcp\Server\Extension\ServerExtensionInterface;
30: use Psr\Log\LoggerInterface;
31: use Psr\Log\NullLogger;
32:
33: /**
34: * The official tasks extension (`io.modelcontextprotocol/tasks`, SEP-2663) for
35: * the server. Use one instance per builder, since the runner binds that
36: * builder's `tools/call` handler.
37: */
38: final readonly class TasksServerExtension implements RequestHandlerDecoratorInterface, ServerExtensionInterface
39: {
40: public const int DEFAULT_MAX_RUNNING_TASKS = 1_024;
41:
42: private TaskCancellationRegistry $cancellations;
43: private ToolTaskRunner $runner;
44:
45: /**
46: * @param array<non-empty-string, ToolTaskPolicy> $toolPolicies Keyed by tool name, absence meaning always-synchronous
47: * @param null|int $defaultTtlMs Retention after a terminal transition, `null` for unlimited
48: * @param int<1, max> $maxRunningTasks Tasks running at once before a further one is refused
49: */
50: public function __construct(
51: private TaskStoreInterface $store = new InMemoryTaskStore(),
52: private array $toolPolicies = [],
53: private ?int $defaultTtlMs = 300_000,
54: private int $defaultPollIntervalMs = 1_000,
55: LoggerInterface $logger = new NullLogger(),
56: int $maxRunningTasks = self::DEFAULT_MAX_RUNNING_TASKS,
57: ) {
58: $this->cancellations = new TaskCancellationRegistry();
59: $this->runner = new ToolTaskRunner($store, $this->cancellations, $logger, $maxRunningTasks);
60: }
61:
62: #[\Override]
63: public function getIdentifier(): string
64: {
65: return Tasks::IDENTIFIER;
66: }
67:
68: #[\Override]
69: public function getSettings(): array
70: {
71: return [];
72: }
73:
74: #[\Override]
75: public function getRequests(): array
76: {
77: return [
78: GetTaskRequest::class,
79: UpdateTaskRequest::class,
80: CancelTaskRequest::class,
81: ];
82: }
83:
84: #[\Override]
85: public function getNotifications(): array
86: {
87: return [];
88: }
89:
90: #[\Override]
91: public function getRequestHandlers(): array
92: {
93: return [
94: GetTaskRequest::getMethod() => new GetTaskRequestHandler($this->store),
95: UpdateTaskRequest::getMethod() => new UpdateTaskRequestHandler($this->store, $this->runner),
96: CancelTaskRequest::getMethod() => new CancelTaskRequestHandler($this->store, $this->cancellations),
97: ];
98: }
99:
100: #[\Override]
101: public function getNotificationHandlers(): array
102: {
103: return [];
104: }
105:
106: #[\Override]
107: public function getRequestHandlerDecorators(): array
108: {
109: return [
110: CallToolRequest::getMethod() => function (RequestHandlerInterface $inner): RequestHandlerInterface {
111: $this->runner->bindInnerHandler($inner);
112:
113: return new TaskBrokeringCallToolHandler(
114: inner: $inner,
115: identifier: Tasks::IDENTIFIER,
116: store: $this->store,
117: runner: $this->runner,
118: toolPolicies: $this->toolPolicies,
119: defaultTtlMs: $this->defaultTtlMs,
120: defaultPollIntervalMs: $this->defaultPollIntervalMs,
121: );
122: },
123: ];
124: }
125: }
126: