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
18 changes: 0 additions & 18 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -78,30 +78,12 @@ parameters:
count: 1
path: src/Server/ServerBuilder.php

-
message: '#^Property Mcp\\Server\\ServerBuilder\:\:\$discoveryExcludeDirs type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: src/Server/ServerBuilder.php

-
message: '#^Property Mcp\\Server\\ServerBuilder\:\:\$instructions is never read, only written\.$#'
identifier: property.onlyWritten
count: 1
path: src/Server/ServerBuilder.php

-
message: '#^Property Mcp\\Server\\ServerBuilder\:\:\$paginationLimit \(int\|null\) is never assigned null so it can be removed from the property type\.$#'
identifier: property.unusedType
count: 1
path: src/Server/ServerBuilder.php

-
message: '#^Property Mcp\\Server\\ServerBuilder\:\:\$paginationLimit is never read, only written\.$#'
identifier: property.onlyWritten
count: 1
path: src/Server/ServerBuilder.php

-
message: '#^Property Mcp\\Server\\ServerBuilder\:\:\$prompts type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
Expand Down
7 changes: 4 additions & 3 deletions src/JsonRpc/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public static function make(
SessionStoreInterface $sessionStore,
SessionFactoryInterface $sessionFactory,
LoggerInterface $logger = new NullLogger(),
int $paginationLimit = 50,
): self {
return new self(
messageFactory: MessageFactory::make(),
Expand All @@ -82,12 +83,12 @@ public static function make(
new NotificationHandler\InitializedHandler(),
new RequestHandler\InitializeHandler($registry->getCapabilities(), $implementation),
new RequestHandler\PingHandler(),
new RequestHandler\ListPromptsHandler($referenceProvider),
new RequestHandler\ListPromptsHandler($referenceProvider, $paginationLimit),
new RequestHandler\GetPromptHandler($promptGetter),
new RequestHandler\ListResourcesHandler($referenceProvider),
new RequestHandler\ListResourcesHandler($referenceProvider, $paginationLimit),
new RequestHandler\ReadResourceHandler($resourceReader),
new RequestHandler\CallToolHandler($toolCaller, $logger),
new RequestHandler\ListToolsHandler($referenceProvider),
new RequestHandler\ListToolsHandler($referenceProvider, $paginationLimit),
],
logger: $logger,
);
Expand Down
25 changes: 3 additions & 22 deletions src/Server/RequestHandler/Reference/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
namespace Mcp\Server\RequestHandler\Reference;

/**
* @implements \ArrayAccess<int|string, mixed>
* @extends \ArrayObject<int|string, mixed>
*/
final class Page implements \Countable, \ArrayAccess
final class Page extends \ArrayObject
{
/**
* @param array<int|string, mixed> $references Items can be Tool, Prompt, ResourceTemplate, or Resource
Expand All @@ -23,30 +23,11 @@ public function __construct(
public readonly array $references,
public readonly ?string $nextCursor,
) {
parent::__construct($references, \ArrayObject::ARRAY_AS_PROPS);
}

public function count(): int
{
return \count($this->references);
}

public function offsetExists(mixed $offset): bool
{
return isset($this->references[$offset]);
}

public function offsetGet(mixed $offset): mixed
{
return $this->references[$offset] ?? null;
}

public function offsetSet(mixed $offset, mixed $value): void
{
return;
}

public function offsetUnset(mixed $offset): void
{
return;
}
}
6 changes: 5 additions & 1 deletion src/Server/ServerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ final class ServerBuilder

private int $sessionTtl = 3600;

private ?int $paginationLimit = 50;
private int $paginationLimit = 50;

private ?string $instructions = null;

Expand Down Expand Up @@ -119,6 +119,9 @@ final class ServerBuilder
* @var array|string[]
*/
private array $discoveryScanDirs = [];
/**
* @var array|string[]
*/
private array $discoveryExcludeDirs = [];

/**
Expand Down Expand Up @@ -337,6 +340,7 @@ public function build(): Server
sessionStore: $sessionStore,
sessionFactory: $sessionFactory,
logger: $logger,
paginationLimit: $this->paginationLimit,
),
logger: $logger,
);
Expand Down
47 changes: 46 additions & 1 deletion tests/Unit/Server/RequestHandler/ListToolsHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function testReturnsLastPageWithNullCursor(): void
public function testReturnsAllToolsWhenCountIsLessThanPageSize(): void
{
// Arrange
$this->addToolsToRegistry(2); // Less than page size (3)
$this->addToolsToRegistry(2); // Less than page size 3
$request = $this->createListToolsRequest();

// Act
Expand Down Expand Up @@ -239,6 +239,51 @@ public function testMaintainsStableCursorsAcrossCalls(): void
$this->assertEquals($result1->tools, $result2->tools);
}

#[TestDox('Uses custom page size when provided')]
public function testUsesCustomPageSizeWhenProvided(): void
{
// Arrange
$customPageSize = 5;
$customHandler = new ListToolsHandler($this->registry, pageSize: $customPageSize);
$this->addToolsToRegistry(10);
$request = $this->createListToolsRequest();

// Act
$response = $customHandler->handle($request, $this->session);

// Assert
/** @var ListToolsResult $result */
$result = $response->result;
$this->assertInstanceOf(ListToolsResult::class, $result);
$this->assertCount($customPageSize, $result->tools);
$this->assertNotNull($result->nextCursor);
}

#[TestDox('Different page sizes produce different pagination results')]
public function testDifferentPageSizesProduceDifferentPaginationResults(): void
{
// Arrange
$this->addToolsToRegistry(10);
$smallPageHandler = new ListToolsHandler($this->registry, pageSize: 2);
$largePageHandler = new ListToolsHandler($this->registry, pageSize: 7);
$request = $this->createListToolsRequest();

// Act
$smallPageResponse = $smallPageHandler->handle($request, $this->session);
$largePageResponse = $largePageHandler->handle($request, $this->session);

// Assert
/** @var ListToolsResult $smallResult */
$smallResult = $smallPageResponse->result;
/** @var ListToolsResult $largeResult */
$largeResult = $largePageResponse->result;

$this->assertCount(2, $smallResult->tools);
$this->assertCount(7, $largeResult->tools);
$this->assertNotNull($smallResult->nextCursor);
$this->assertNotNull($largeResult->nextCursor);
}

private function addToolsToRegistry(int $count): void
{
for ($i = 0; $i < $count; ++$i) {
Expand Down