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([new PromptMessage(Role::User, new TextContent($result))]);
52: }
53:
54: if ($result instanceof PromptMessage) {
55: return new GetPromptResult([$result]);
56: }
57:
58: if (\is_array($result)) {
59: return new GetPromptResult($this->buildMessageList($result));
60: }
61:
62: throw new UnsupportedReturnValueException(
63: $this->method->getDeclaringClass()->getName(),
64: $this->method->getName(),
65: \sprintf('a %s, a string, or prompt messages', GetPromptResult::class),
66: $result,
67: );
68: }
69:
70: /**
71: * @param array<array-key, mixed> $result
72: *
73: * @return list<PromptMessage>
74: */
75: private function buildMessageList(array $result): array
76: {
77: $messages = array_filter($result, static fn(mixed $item): bool => $item instanceof PromptMessage);
78:
79: if (! array_is_list($result) || [] === $result || \count($messages) !== \count($result)) {
80: throw new UnsupportedReturnValueException(
81: $this->method->getDeclaringClass()->getName(),
82: $this->method->getName(),
83: \sprintf('a %s, a string, or prompt messages', GetPromptResult::class),
84: $result,
85: );
86: }
87:
88: return array_values($messages);
89: }
90: }
91: