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\Completion;
15:
16: use Nexus\Mcp\Core\Schema\Result\CompleteResult;
17: use Nexus\Mcp\Server\Discovery\InputSchemaGenerator;
18: use Nexus\Mcp\Server\Exception\UnsupportedReturnValueException;
19: use Nexus\Mcp\Server\ServerContext;
20:
21: /**
22: * Adapts an attribute-discovered method to the `CompletionProviderInterface` contract.
23: */
24: final readonly class ReflectedCompletionProvider implements CompletionProviderInterface
25: {
26: public function __construct(
27: private object $handler,
28: private \ReflectionMethod $method,
29: ) {
30: }
31:
32: #[\Override]
33: public function complete(string $argumentValue, ?array $contextArguments, ServerContext $context): CompleteResult
34: {
35: $arguments = [];
36:
37: foreach ($this->method->getParameters() as $parameter) {
38: $type = $parameter->getType();
39:
40: if (InputSchemaGenerator::isInjectedContext($parameter)) {
41: $arguments[] = $context;
42: } elseif ($type instanceof \ReflectionNamedType && $type->getName() === 'array') {
43: $arguments[] = $contextArguments ?? ($type->allowsNull() ? null : []);
44: } else {
45: $arguments[] = $argumentValue;
46: }
47: }
48:
49: return $this->adapt($this->method->invokeArgs($this->handler, $arguments));
50: }
51:
52: private function adapt(mixed $result): CompleteResult
53: {
54: if ($result instanceof CompleteResult) {
55: return $result;
56: }
57:
58: if (\is_array($result) && array_is_list($result)) {
59: $values = [];
60:
61: foreach ($result as $entry) {
62: if (! \is_string($entry)) {
63: $values = null;
64:
65: break;
66: }
67:
68: $values[] = $entry;
69: }
70:
71: if (null !== $values) {
72: return new CompleteResult(completion: ['values' => $values]);
73: }
74: }
75:
76: throw new UnsupportedReturnValueException(
77: $this->method->getDeclaringClass()->getName(),
78: $this->method->getName(),
79: \sprintf('a %s or a list of strings', CompleteResult::class),
80: $result,
81: );
82: }
83: }
84: