Skip to content

Commit 4b91567

Browse files
feat: add completion/complete handler, centralize provider logic, and move completion capability (#97)
1 parent f0807f1 commit 4b91567

20 files changed

+173
-81
lines changed

examples/http-discovery-userprofile/UserIdCompletionProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Mcp\Example\HttpDiscoveryUserProfile;
1313

14-
use Mcp\Capability\Prompt\Completion\ProviderInterface;
14+
use Mcp\Capability\Completion\ProviderInterface;
1515

1616
final class UserIdCompletionProvider implements ProviderInterface
1717
{

phpstan-baseline.neon

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ parameters:
1212
count: 2
1313
path: examples/http-discovery-userprofile/McpElements.php
1414

15-
-
16-
message: '#^Call to an undefined method Mcp\\Capability\\Registry\\ResourceTemplateReference\:\:handle\(\)\.$#'
17-
identifier: method.notFound
18-
count: 1
19-
path: src/Capability/Registry/ResourceTemplateReference.php
20-
2115
-
2216
message: '#^PHPDoc tag @return with type array is incompatible with native type object\.$#'
2317
identifier: return.phpDocType

src/Capability/Attribute/CompletionProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Mcp\Capability\Attribute;
1313

14-
use Mcp\Capability\Prompt\Completion\ProviderInterface;
14+
use Mcp\Capability\Completion\ProviderInterface;
1515
use Mcp\Exception\InvalidArgumentException;
1616

1717
/**
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the official PHP MCP SDK.
5+
*
6+
* A collaboration between Symfony and the PHP Foundation.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Mcp\Capability\Completion;
13+
14+
use Mcp\Capability\Registry\ReferenceProviderInterface;
15+
use Mcp\Exception\RuntimeException;
16+
use Mcp\Schema\Request\CompletionCompleteRequest;
17+
use Mcp\Schema\Result\CompletionCompleteResult;
18+
use Psr\Container\ContainerInterface;
19+
20+
/**
21+
* @author Kyrian Obikwelu <[email protected]>
22+
*/
23+
final class Completer implements CompleterInterface
24+
{
25+
public function __construct(
26+
private readonly ReferenceProviderInterface $referenceProvider,
27+
private readonly ?ContainerInterface $container = null,
28+
) {
29+
}
30+
31+
public function complete(CompletionCompleteRequest $request): CompletionCompleteResult
32+
{
33+
$argumentName = $request->argument['name'] ?? '';
34+
$currentValue = $request->argument['value'] ?? '';
35+
36+
$reference = match (true) {
37+
'ref/prompt' === $request->ref->type => $this->referenceProvider->getPrompt($request->ref->name),
38+
'ref/resource' === $request->ref->type => $this->referenceProvider->getResourceTemplate($request->ref->uri),
39+
default => null,
40+
};
41+
42+
if (null === $reference) {
43+
return new CompletionCompleteResult([]);
44+
}
45+
46+
$providerClassOrInstance = $reference->completionProviders[$argumentName] ?? null;
47+
if (null === $providerClassOrInstance) {
48+
return new CompletionCompleteResult([]);
49+
}
50+
51+
if (\is_string($providerClassOrInstance)) {
52+
if (!class_exists($providerClassOrInstance)) {
53+
throw new RuntimeException(\sprintf('Completion provider class "%s" does not exist.', $providerClassOrInstance));
54+
}
55+
56+
$provider = $this->container?->has($providerClassOrInstance)
57+
? $this->container->get($providerClassOrInstance)
58+
: new $providerClassOrInstance();
59+
} else {
60+
$provider = $providerClassOrInstance;
61+
}
62+
63+
if (!$provider instanceof ProviderInterface) {
64+
throw new RuntimeException('Completion provider must implement ProviderInterface.');
65+
}
66+
67+
$completions = $provider->getCompletions($currentValue);
68+
69+
$total = \count($completions);
70+
$hasMore = $total > 100;
71+
$pagedCompletions = \array_slice($completions, 0, 100);
72+
73+
return new CompletionCompleteResult($pagedCompletions, $total, $hasMore);
74+
}
75+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the official PHP MCP SDK.
5+
*
6+
* A collaboration between Symfony and the PHP Foundation.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Mcp\Capability\Completion;
13+
14+
use Mcp\Schema\Request\CompletionCompleteRequest;
15+
use Mcp\Schema\Result\CompletionCompleteResult;
16+
17+
/**
18+
* Provides completion options for prompt arguments and resource template variables.
19+
*
20+
* @author Kyrian Obikwelu <[email protected]>
21+
*/
22+
interface CompleterInterface
23+
{
24+
public function complete(CompletionCompleteRequest $request): CompletionCompleteResult;
25+
}

src/Capability/Prompt/Completion/EnumCompletionProvider.php renamed to src/Capability/Completion/EnumCompletionProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace Mcp\Capability\Prompt\Completion;
12+
namespace Mcp\Capability\Completion;
1313

1414
use Mcp\Exception\InvalidArgumentException;
1515

src/Capability/Prompt/Completion/ListCompletionProvider.php renamed to src/Capability/Completion/ListCompletionProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace Mcp\Capability\Prompt\Completion;
12+
namespace Mcp\Capability\Completion;
1313

1414
/**
1515
* @author Kyrian Obikwelu <[email protected]>

src/Capability/Prompt/Completion/ProviderInterface.php renamed to src/Capability/Completion/ProviderInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace Mcp\Capability\Prompt\Completion;
12+
namespace Mcp\Capability\Completion;
1313

1414
/**
1515
* @author Kyrian Obikwelu <[email protected]>

src/Capability/Discovery/Discoverer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
use Mcp\Capability\Attribute\McpResource;
1717
use Mcp\Capability\Attribute\McpResourceTemplate;
1818
use Mcp\Capability\Attribute\McpTool;
19-
use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
20-
use Mcp\Capability\Prompt\Completion\ListCompletionProvider;
21-
use Mcp\Capability\Prompt\Completion\ProviderInterface;
19+
use Mcp\Capability\Completion\EnumCompletionProvider;
20+
use Mcp\Capability\Completion\ListCompletionProvider;
21+
use Mcp\Capability\Completion\ProviderInterface;
2222
use Mcp\Capability\Registry\PromptReference;
2323
use Mcp\Capability\Registry\ReferenceRegistryInterface;
2424
use Mcp\Capability\Registry\ResourceReference;

src/Capability/Registry/PromptReference.php

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
use Mcp\Schema\Content\TextResourceContents;
2323
use Mcp\Schema\Enum\Role;
2424
use Mcp\Schema\Prompt;
25-
use Mcp\Schema\Result\CompletionCompleteResult;
26-
use Psr\Container\ContainerInterface;
2725

2826
/**
2927
* @phpstan-import-type Handler from ElementReference
@@ -45,33 +43,6 @@ public function __construct(
4543
parent::__construct($handler, $isManual);
4644
}
4745

48-
public function complete(ContainerInterface $container, string $argument, string $value): CompletionCompleteResult
49-
{
50-
$providerClassOrInstance = $this->completionProviders[$argument] ?? null;
51-
if (null === $providerClassOrInstance) {
52-
return new CompletionCompleteResult([]);
53-
}
54-
55-
if (\is_string($providerClassOrInstance)) {
56-
if (!class_exists($providerClassOrInstance)) {
57-
throw new RuntimeException("Completion provider class '{$providerClassOrInstance}' does not exist.");
58-
}
59-
60-
$provider = $container->get($providerClassOrInstance);
61-
} else {
62-
$provider = $providerClassOrInstance;
63-
}
64-
65-
$completions = $provider->getCompletions($value);
66-
67-
$total = \count($completions);
68-
$hasMore = $total > 100;
69-
70-
$pagedCompletions = \array_slice($completions, 0, 100);
71-
72-
return new CompletionCompleteResult($pagedCompletions, $total, $hasMore);
73-
}
74-
7546
/**
7647
* Formats the raw result of a prompt generator into an array of MCP PromptMessages.
7748
*

0 commit comments

Comments
 (0)