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