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\Server\Prompt;
15:
16: use Nexus\Mcp\Core\Schema\ContentBlock\TextContent;
17: use Nexus\Mcp\Core\Schema\Enum\Role;
18: use Nexus\Mcp\Core\Schema\Prompt\PromptMessage;
19: use Nexus\Mcp\Core\Schema\Result\GetPromptResult;
20: use Nexus\Mcp\Server\Discovery\ArgumentBinder;
21: use Nexus\Mcp\Server\Exception\UnsupportedReturnValueException;
22: use Nexus\Mcp\Server\ServerContext;
23:
24: /**
25: * Adapts an attribute-discovered handler method to the `PromptRendererInterface` contract.
26: */
27: final readonly class ReflectedPromptRenderer implements PromptRendererInterface
28: {
29: public function __construct(
30: private object $handler,
31: private \ReflectionMethod $method,
32: private ArgumentBinder $binder = new ArgumentBinder(),
33: ) {
34: }
35:
36: #[\Override]
37: public function render(?array $arguments, ServerContext $context): GetPromptResult
38: {
39: $bound = $this->binder->bind($this->method, $arguments ?? [], $context);
40:
41: return $this->adapt($this->method->invokeArgs($this->handler, $bound));
42: }
43:
44: private function adapt(mixed $result): GetPromptResult
45: {
46: if ($result instanceof GetPromptResult) {
47: return $result;
48: }
49:
50: if (\is_string($result)) {
51: return new GetPromptResult(messages: [
52: new PromptMessage(role: Role::User, content: new TextContent(text: $result)),
53: ]);
54: }
55:
56: if ($result instanceof PromptMessage) {
57: return new GetPromptResult(messages: [$result]);
58: }
59:
60: if (\is_array($result)) {
61: return new GetPromptResult(messages: $this->buildMessageList($result));
62: }
63:
64: throw new UnsupportedReturnValueException(
65: $this->method->getDeclaringClass()->getName(),
66: $this->method->getName(),
67: \sprintf('a %s, a string, or prompt messages', GetPromptResult::class),
68: $result,
69: );
70: }
71:
72: /**
73: * @param array<array-key, mixed> $result
74: *
75: * @return list<PromptMessage>
76: */
77: private function buildMessageList(array $result): array
78: {
79: $messages = array_filter($result, static fn(mixed $item): bool => $item instanceof PromptMessage);
80:
81: if (! array_is_list($result) || [] === $result || \count($messages) !== \count($result)) {
82: throw new UnsupportedReturnValueException(
83: $this->method->getDeclaringClass()->getName(),
84: $this->method->getName(),
85: \sprintf('a %s, a string, or prompt messages', GetPromptResult::class),
86: $result,
87: );
88: }
89:
90: return array_values($messages);
91: }
92: }
93: