Skip to content

Commit 886307f

Browse files
committed
minor #6 Add support for pagination (Nyholm)
This PR was squashed before being merged into the main branch. Discussion ---------- Add support for pagination | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | no | Issues | #5 | License | MIT Just following the protocol. Note that there is no real benefit with pagination for our standard Collection implementation Commits ------- 1fc5e9c Add support for pagination
2 parents 9372bc4 + 1fc5e9c commit 886307f

14 files changed

+245
-84
lines changed

src/mcp-sdk/src/Capability/Prompt/CollectionInterface.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111

1212
namespace Symfony\AI\McpSdk\Capability\Prompt;
1313

14+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
15+
1416
interface CollectionInterface
1517
{
1618
/**
17-
* @return MetadataInterface[]
19+
* @param int $count the number of metadata items to return
20+
*
21+
* @return iterable<MetadataInterface>
22+
*
23+
* @throws InvalidCursorException if no item with $lastIdentifier was found
1824
*/
19-
public function getMetadata(): array;
25+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
2026
}

src/mcp-sdk/src/Capability/PromptChain.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\AI\McpSdk\Capability\Prompt\PromptGet;
1818
use Symfony\AI\McpSdk\Capability\Prompt\PromptGetResult;
1919
use Symfony\AI\McpSdk\Capability\Prompt\PromptGetterInterface;
20+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
2021
use Symfony\AI\McpSdk\Exception\PromptGetException;
2122
use Symfony\AI\McpSdk\Exception\PromptNotFoundException;
2223

@@ -33,9 +34,28 @@ public function __construct(
3334
) {
3435
}
3536

36-
public function getMetadata(): array
37+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable
3738
{
38-
return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
39+
$found = null === $lastIdentifier;
40+
foreach ($this->items as $item) {
41+
if (!$item instanceof MetadataInterface) {
42+
continue;
43+
}
44+
45+
if (false === $found) {
46+
$found = $item->getName() === $lastIdentifier;
47+
continue;
48+
}
49+
50+
yield $item;
51+
if (--$count <= 0) {
52+
break;
53+
}
54+
}
55+
56+
if (!$found) {
57+
throw new InvalidCursorException($lastIdentifier);
58+
}
3959
}
4060

4161
public function get(PromptGet $input): PromptGetResult

src/mcp-sdk/src/Capability/Resource/CollectionInterface.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111

1212
namespace Symfony\AI\McpSdk\Capability\Resource;
1313

14+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
15+
1416
interface CollectionInterface
1517
{
1618
/**
17-
* @return MetadataInterface[]
19+
* @param int $count the number of metadata items to return
20+
*
21+
* @return iterable<MetadataInterface>
22+
*
23+
* @throws InvalidCursorException if no item with $lastIdentifier was found
1824
*/
19-
public function getMetadata(): array;
25+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
2026
}

src/mcp-sdk/src/Capability/ResourceChain.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\AI\McpSdk\Capability\Resource\ResourceRead;
1818
use Symfony\AI\McpSdk\Capability\Resource\ResourceReaderInterface;
1919
use Symfony\AI\McpSdk\Capability\Resource\ResourceReadResult;
20+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
2021
use Symfony\AI\McpSdk\Exception\ResourceNotFoundException;
2122
use Symfony\AI\McpSdk\Exception\ResourceReadException;
2223

@@ -33,9 +34,28 @@ public function __construct(
3334
) {
3435
}
3536

36-
public function getMetadata(): array
37+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable
3738
{
38-
return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
39+
$found = null === $lastIdentifier;
40+
foreach ($this->items as $item) {
41+
if (!$item instanceof MetadataInterface) {
42+
continue;
43+
}
44+
45+
if (false === $found) {
46+
$found = $item->getUri() === $lastIdentifier;
47+
continue;
48+
}
49+
50+
yield $item;
51+
if (--$count <= 0) {
52+
break;
53+
}
54+
}
55+
56+
if (!$found) {
57+
throw new InvalidCursorException($lastIdentifier);
58+
}
3959
}
4060

4161
public function read(ResourceRead $input): ResourceReadResult

src/mcp-sdk/src/Capability/Tool/CollectionInterface.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111

1212
namespace Symfony\AI\McpSdk\Capability\Tool;
1313

14+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
15+
1416
interface CollectionInterface
1517
{
1618
/**
17-
* @return MetadataInterface[]
19+
* @param int $count the number of metadata items to return
20+
*
21+
* @return iterable<MetadataInterface>
22+
*
23+
* @throws InvalidCursorException if no item with $lastIdentifier was found
1824
*/
19-
public function getMetadata(): array;
25+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
2026
}

src/mcp-sdk/src/Capability/ToolChain.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\AI\McpSdk\Capability\Tool\ToolCall;
1818
use Symfony\AI\McpSdk\Capability\Tool\ToolCallResult;
1919
use Symfony\AI\McpSdk\Capability\Tool\ToolExecutorInterface;
20+
use Symfony\AI\McpSdk\Exception\InvalidCursorException;
2021
use Symfony\AI\McpSdk\Exception\ToolExecutionException;
2122
use Symfony\AI\McpSdk\Exception\ToolNotFoundException;
2223

@@ -33,9 +34,28 @@ public function __construct(
3334
) {
3435
}
3536

36-
public function getMetadata(): array
37+
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable
3738
{
38-
return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
39+
$found = null === $lastIdentifier;
40+
foreach ($this->items as $item) {
41+
if (!$item instanceof MetadataInterface) {
42+
continue;
43+
}
44+
45+
if (false === $found) {
46+
$found = $item->getName() === $lastIdentifier;
47+
continue;
48+
}
49+
50+
yield $item;
51+
if (--$count <= 0) {
52+
break;
53+
}
54+
}
55+
56+
if (!$found) {
57+
throw new InvalidCursorException($lastIdentifier);
58+
}
3959
}
4060

4161
public function call(ToolCall $input): ToolCallResult

src/mcp-sdk/src/Exception/ExceptionInterface.php

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

1212
namespace Symfony\AI\McpSdk\Exception;
1313

14-
interface ExceptionInterface
14+
interface ExceptionInterface extends \Throwable
1515
{
1616
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Symfony package.
7+
*
8+
* (c) Fabien Potencier <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Symfony\AI\McpSdk\Exception;
15+
16+
class HandlerNotFoundException extends \InvalidArgumentException implements NotFoundExceptionInterface
17+
{
18+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Symfony package.
7+
*
8+
* (c) Fabien Potencier <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Symfony\AI\McpSdk\Exception;
15+
16+
final class InvalidCursorException extends \InvalidArgumentException implements ExceptionInterface
17+
{
18+
public function __construct(
19+
public readonly string $cursor,
20+
) {
21+
parent::__construct(\sprintf('Invalid value for pagination parameter "cursor": "%s"', $cursor));
22+
}
23+
}

src/mcp-sdk/src/Exception/PromptNotFoundException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ final class PromptNotFoundException extends \RuntimeException implements NotFoun
1818
public function __construct(
1919
public readonly PromptGet $promptGet,
2020
) {
21-
parent::__construct(\sprintf('Resource not found for uri: "%s"', $promptGet->name));
21+
parent::__construct(\sprintf('Prompt not found for name: "%s"', $promptGet->name));
2222
}
2323
}

0 commit comments

Comments
 (0)