Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Example\HttpDiscoveryUserProfile;

use Mcp\Capability\Prompt\Completion\ProviderInterface;
use Mcp\Capability\Completion\ProviderInterface;

final class UserIdCompletionProvider implements ProviderInterface
{
Expand Down
6 changes: 0 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ parameters:
count: 2
path: examples/http-discovery-userprofile/McpElements.php

-
message: '#^Call to an undefined method Mcp\\Capability\\Registry\\ResourceTemplateReference\:\:handle\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Capability/Registry/ResourceTemplateReference.php

-
message: '#^PHPDoc tag @return with type array is incompatible with native type object\.$#'
identifier: return.phpDocType
Expand Down
2 changes: 1 addition & 1 deletion src/Capability/Attribute/CompletionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Capability\Attribute;

use Mcp\Capability\Prompt\Completion\ProviderInterface;
use Mcp\Capability\Completion\ProviderInterface;
use Mcp\Exception\InvalidArgumentException;

/**
Expand Down
75 changes: 75 additions & 0 deletions src/Capability/Completion/Completer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Mcp\Capability\Completion;

use Mcp\Capability\Registry\ReferenceProviderInterface;
use Mcp\Exception\RuntimeException;
use Mcp\Schema\Request\CompletionCompleteRequest;
use Mcp\Schema\Result\CompletionCompleteResult;
use Psr\Container\ContainerInterface;

/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
final class Completer implements CompleterInterface
{
public function __construct(
private readonly ReferenceProviderInterface $referenceProvider,
private readonly ?ContainerInterface $container = null,
) {
}

public function complete(CompletionCompleteRequest $request): CompletionCompleteResult
{
$argumentName = $request->argument['name'] ?? '';
$currentValue = $request->argument['value'] ?? '';

$reference = match (true) {
'ref/prompt' === $request->ref->type => $this->referenceProvider->getPrompt($request->ref->name),
'ref/resource' === $request->ref->type => $this->referenceProvider->getResourceTemplate($request->ref->uri),
default => null,
};

if (null === $reference) {
return new CompletionCompleteResult([]);
}

$providerClassOrInstance = $reference->completionProviders[$argumentName] ?? null;
if (null === $providerClassOrInstance) {
return new CompletionCompleteResult([]);
}

if (\is_string($providerClassOrInstance)) {
if (!class_exists($providerClassOrInstance)) {
throw new RuntimeException(\sprintf('Completion provider class "%s" does not exist.', $providerClassOrInstance));
}

$provider = $this->container?->has($providerClassOrInstance)
? $this->container->get($providerClassOrInstance)
: new $providerClassOrInstance();
} else {
$provider = $providerClassOrInstance;
}

if (!$provider instanceof ProviderInterface) {
throw new RuntimeException('Completion provider must implement ProviderInterface.');
}

$completions = $provider->getCompletions($currentValue);

$total = \count($completions);
$hasMore = $total > 100;
$pagedCompletions = \array_slice($completions, 0, 100);

return new CompletionCompleteResult($pagedCompletions, $total, $hasMore);
}
}
25 changes: 25 additions & 0 deletions src/Capability/Completion/CompleterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Mcp\Capability\Completion;

use Mcp\Schema\Request\CompletionCompleteRequest;
use Mcp\Schema\Result\CompletionCompleteResult;

/**
* Provides completion options for prompt arguments and resource template variables.
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
interface CompleterInterface
{
public function complete(CompletionCompleteRequest $request): CompletionCompleteResult;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/

namespace Mcp\Capability\Prompt\Completion;
namespace Mcp\Capability\Completion;

use Mcp\Exception\InvalidArgumentException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/

namespace Mcp\Capability\Prompt\Completion;
namespace Mcp\Capability\Completion;

/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/

namespace Mcp\Capability\Prompt\Completion;
namespace Mcp\Capability\Completion;

/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
Expand Down
6 changes: 3 additions & 3 deletions src/Capability/Discovery/Discoverer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
use Mcp\Capability\Attribute\McpResource;
use Mcp\Capability\Attribute\McpResourceTemplate;
use Mcp\Capability\Attribute\McpTool;
use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
use Mcp\Capability\Prompt\Completion\ListCompletionProvider;
use Mcp\Capability\Prompt\Completion\ProviderInterface;
use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\ListCompletionProvider;
use Mcp\Capability\Completion\ProviderInterface;
use Mcp\Capability\Registry\PromptReference;
use Mcp\Capability\Registry\ReferenceRegistryInterface;
use Mcp\Capability\Registry\ResourceReference;
Expand Down
29 changes: 0 additions & 29 deletions src/Capability/Registry/PromptReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
use Mcp\Schema\Content\TextResourceContents;
use Mcp\Schema\Enum\Role;
use Mcp\Schema\Prompt;
use Mcp\Schema\Result\CompletionCompleteResult;
use Psr\Container\ContainerInterface;

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

public function complete(ContainerInterface $container, string $argument, string $value): CompletionCompleteResult
{
$providerClassOrInstance = $this->completionProviders[$argument] ?? null;
if (null === $providerClassOrInstance) {
return new CompletionCompleteResult([]);
}

if (\is_string($providerClassOrInstance)) {
if (!class_exists($providerClassOrInstance)) {
throw new RuntimeException("Completion provider class '{$providerClassOrInstance}' does not exist.");
}

$provider = $container->get($providerClassOrInstance);
} else {
$provider = $providerClassOrInstance;
}

$completions = $provider->getCompletions($value);

$total = \count($completions);
$hasMore = $total > 100;

$pagedCompletions = \array_slice($completions, 0, 100);

return new CompletionCompleteResult($pagedCompletions, $total, $hasMore);
}

/**
* Formats the raw result of a prompt generator into an array of MCP PromptMessages.
*
Expand Down
31 changes: 2 additions & 29 deletions src/Capability/Registry/ResourceTemplateReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use Mcp\Schema\Content\ResourceContents;
use Mcp\Schema\Content\TextResourceContents;
use Mcp\Schema\ResourceTemplate;
use Mcp\Schema\Result\CompletionCompleteResult;
use Psr\Container\ContainerInterface;

/**
Expand Down Expand Up @@ -64,38 +63,12 @@ public function read(ContainerInterface $container, string $uri): array
{
$arguments = array_merge($this->uriVariables, ['uri' => $uri]);

$result = $this->handle($container, $arguments);
$referenceHandler = new ReferenceHandler($container);
$result = $referenceHandler->handle($this, $arguments);

return $this->formatResult($result, $uri, $this->resourceTemplate->mimeType);
}

public function complete(ContainerInterface $container, string $argument, string $value): CompletionCompleteResult
{
$providerClassOrInstance = $this->completionProviders[$argument] ?? null;
if (null === $providerClassOrInstance) {
return new CompletionCompleteResult([]);
}

if (\is_string($providerClassOrInstance)) {
if (!class_exists($providerClassOrInstance)) {
throw new RuntimeException(\sprintf('Completion provider class "%s" does not exist.', $providerClassOrInstance));
}

$provider = $container->get($providerClassOrInstance);
} else {
$provider = $providerClassOrInstance;
}

$completions = $provider->getCompletions($value);

$total = \count($completions);
$hasMore = $total > 100;

$pagedCompletions = \array_slice($completions, 0, 100);

return new CompletionCompleteResult($pagedCompletions, $total, $hasMore);
}

/**
* @return array<int, string>
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Server/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
namespace Mcp\Server;

use Mcp\Capability\Attribute\CompletionProvider;
use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\ListCompletionProvider;
use Mcp\Capability\Discovery\CachedDiscoverer;
use Mcp\Capability\Discovery\Discoverer;
use Mcp\Capability\Discovery\DocBlockParser;
use Mcp\Capability\Discovery\HandlerResolver;
use Mcp\Capability\Discovery\SchemaGenerator;
use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
use Mcp\Capability\Prompt\Completion\ListCompletionProvider;
use Mcp\Capability\Prompt\PromptGetter;
use Mcp\Capability\Prompt\PromptGetterInterface;
use Mcp\Capability\Registry;
Expand Down
2 changes: 2 additions & 0 deletions src/Server/Handler/JsonRpcHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Mcp\Server\Handler;

use Mcp\Capability\Completion\Completer;
use Mcp\Capability\Prompt\PromptGetterInterface;
use Mcp\Capability\Registry\ReferenceProviderInterface;
use Mcp\Capability\Registry\ReferenceRegistryInterface;
Expand Down Expand Up @@ -89,6 +90,7 @@ public static function make(
new Handler\Request\ListResourceTemplatesHandler($referenceProvider, $paginationLimit),
new Handler\Request\CallToolHandler($toolCaller, $logger),
new Handler\Request\ListToolsHandler($referenceProvider, $paginationLimit),
new Handler\Request\CompletionCompleteHandler(new Completer($referenceProvider)),
],
logger: $logger,
);
Expand Down
52 changes: 52 additions & 0 deletions src/Server/Handler/Request/CompletionCompleteHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Mcp\Server\Handler\Request;

use Mcp\Capability\Completion\CompleterInterface;
use Mcp\Exception\ExceptionInterface;
use Mcp\Schema\JsonRpc\Error;
use Mcp\Schema\JsonRpc\HasMethodInterface;
use Mcp\Schema\JsonRpc\Response;
use Mcp\Schema\Request\CompletionCompleteRequest;
use Mcp\Server\Handler\MethodHandlerInterface;
use Mcp\Server\Session\SessionInterface;

/**
* Handles completion/complete requests.
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
final class CompletionCompleteHandler implements MethodHandlerInterface
{
public function __construct(
private readonly CompleterInterface $completer,
) {
}

public function supports(HasMethodInterface $message): bool
{
return $message instanceof CompletionCompleteRequest;
}

public function handle(CompletionCompleteRequest|HasMethodInterface $message, SessionInterface $session): Response|Error
{
\assert($message instanceof CompletionCompleteRequest);

try {
$result = $this->completer->complete($message);
} catch (ExceptionInterface) {
return Error::forInternalError('Error while handling completion request', $message->getId());
}

return new Response($message->getId(), $result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Tests\Unit\Capability\Attribute;

use Mcp\Capability\Prompt\Completion\ProviderInterface;
use Mcp\Capability\Completion\ProviderInterface;

class CompletionProviderFixture implements ProviderInterface
{
Expand Down
4 changes: 2 additions & 2 deletions tests/Unit/Capability/Discovery/DiscoveryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace Mcp\Tests\Unit\Capability\Discovery;

use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\ListCompletionProvider;
use Mcp\Capability\Discovery\Discoverer;
use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
use Mcp\Capability\Prompt\Completion\ListCompletionProvider;
use Mcp\Capability\Registry;
use Mcp\Capability\Registry\PromptReference;
use Mcp\Capability\Registry\ResourceReference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Tests\Unit\Capability\Prompt\Completion;

use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Tests\Unit\Fixtures\Enum\PriorityEnum;
use Mcp\Tests\Unit\Fixtures\Enum\StatusEnum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Tests\Unit\Capability\Prompt\Completion;

use Mcp\Capability\Prompt\Completion\ListCompletionProvider;
use Mcp\Capability\Completion\ListCompletionProvider;
use PHPUnit\Framework\TestCase;

class ListCompletionProviderTest extends TestCase
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Capability/Prompt/PromptGetterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Mcp\Tests\Unit\Capability\Prompt;

use Mcp\Capability\Prompt\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Capability\Prompt\PromptGetter;
use Mcp\Capability\Registry\PromptReference;
use Mcp\Capability\Registry\ReferenceHandlerInterface;
Expand Down
Loading