|
22 | 22 | use Mcp\Event\ResourceTemplateListChangedEvent; |
23 | 23 | use Mcp\Event\ToolListChangedEvent; |
24 | 24 | use Mcp\Exception\InvalidArgumentException; |
| 25 | +use Mcp\Exception\InvalidCursorException; |
25 | 26 | use Mcp\Schema\Content\PromptMessage; |
26 | 27 | use Mcp\Schema\Content\ResourceContents; |
27 | 28 | use Mcp\Schema\Prompt; |
@@ -306,30 +307,137 @@ public function getPrompt(string $name): ?PromptReference |
306 | 307 | /** |
307 | 308 | * @return array<string, Tool> |
308 | 309 | */ |
309 | | - public function getTools(): array |
| 310 | + public function getTools(?int $limit = null, ?string $cursor = null): array |
310 | 311 | { |
311 | | - return array_map(fn (ToolReference $tool) => $tool->tool, $this->tools); |
| 312 | + $tools = []; |
| 313 | + foreach ($this->tools as $toolReference) { |
| 314 | + $tools[] = $toolReference->tool; |
| 315 | + } |
| 316 | + |
| 317 | + if ($limit === null) { |
| 318 | + return $tools; |
| 319 | + } |
| 320 | + |
| 321 | + return $this->paginateResults($tools, $limit, $cursor); |
312 | 322 | } |
313 | 323 |
|
314 | 324 | /** |
315 | 325 | * @return array<string, resource> |
316 | 326 | */ |
317 | | - public function getResources(): array |
| 327 | + public function getResources(?int $limit = null, ?string $cursor = null): array |
318 | 328 | { |
319 | | - return array_map(fn (ResourceReference $resource) => $resource->schema, $this->resources); |
| 329 | + $resources = []; |
| 330 | + foreach ($this->resources as $resource) { |
| 331 | + $resources[] = $resource->schema; |
| 332 | + } |
| 333 | + |
| 334 | + if ($limit === null) { |
| 335 | + return $resources; |
| 336 | + } |
| 337 | + |
| 338 | + return $this->paginateResults($resources, $limit, $cursor); |
320 | 339 | } |
321 | 340 |
|
322 | 341 | /** |
323 | 342 | * @return array<string, Prompt> |
324 | 343 | */ |
325 | | - public function getPrompts(): array |
| 344 | + public function getPrompts(?int $limit = null, ?string $cursor = null): array |
| 345 | + { |
| 346 | + $prompts = []; |
| 347 | + foreach ($this->prompts as $promptReference) { |
| 348 | + $prompts[] = $promptReference->prompt; |
| 349 | + } |
| 350 | + |
| 351 | + if ($limit === null) { |
| 352 | + return $prompts; |
| 353 | + } |
| 354 | + |
| 355 | + return $this->paginateResults($prompts, $limit, $cursor); |
| 356 | + } |
| 357 | + |
| 358 | + /** |
| 359 | + * Get total count of tools. |
| 360 | + */ |
| 361 | + public function getToolsCount(): int |
326 | 362 | { |
327 | | - return array_map(fn (PromptReference $prompt) => $prompt->prompt, $this->prompts); |
| 363 | + return count($this->tools); |
328 | 364 | } |
329 | 365 |
|
330 | | - /** @return array<string, ResourceTemplate> */ |
331 | | - public function getResourceTemplates(): array |
| 366 | + /** |
| 367 | + * Get total count of prompts. |
| 368 | + */ |
| 369 | + public function getPromptsCount(): int |
| 370 | + { |
| 371 | + return count($this->prompts); |
| 372 | + } |
| 373 | + |
| 374 | + /** |
| 375 | + * Get total count of resources. |
| 376 | + */ |
| 377 | + public function getResourcesCount(): int |
| 378 | + { |
| 379 | + return count($this->resources); |
| 380 | + } |
| 381 | + |
| 382 | + /** |
| 383 | + * @return array<string, ResourceTemplate> |
| 384 | + */ |
| 385 | + public function getResourceTemplates(?int $limit = null, ?string $cursor = null): array |
332 | 386 | { |
333 | | - return array_map(fn ($template) => $template->resourceTemplate, $this->resourceTemplates); |
| 387 | + $templates = array_map(fn ($template) => $template->resourceTemplate, $this->resourceTemplates); |
| 388 | + |
| 389 | + if ($limit === null) { |
| 390 | + return $templates; |
| 391 | + } |
| 392 | + |
| 393 | + return $this->paginateResults($templates, $limit, $cursor); |
| 394 | + } |
| 395 | + |
| 396 | + /** |
| 397 | + * @throws InvalidCursorException |
| 398 | + */ |
| 399 | + private function paginateResults(array $items, int $limit, ?string $cursor = null): array |
| 400 | + { |
| 401 | + $offset = 0; |
| 402 | + if ($cursor !== null) { |
| 403 | + $decodedCursor = base64_decode($cursor, true); |
| 404 | + |
| 405 | + if ($decodedCursor === false || !is_numeric($decodedCursor)) { |
| 406 | + throw new InvalidCursorException($cursor); |
| 407 | + } |
| 408 | + |
| 409 | + $offset = $decodedCursor; |
| 410 | + |
| 411 | + // Validate offset is within reasonable bounds |
| 412 | + if ($offset < 0 || $offset > count($items)) { |
| 413 | + throw new InvalidCursorException($cursor); |
| 414 | + } |
| 415 | + } |
| 416 | + |
| 417 | + return array_slice($items, $offset, $limit); |
| 418 | + } |
| 419 | + |
| 420 | + /** |
| 421 | + * Calculate next cursor for pagination. |
| 422 | + */ |
| 423 | + public function calculateNextCursor(int $totalCount, ?string $currentCursor, int $returnedCount): ?string |
| 424 | + { |
| 425 | + $currentOffset = 0; |
| 426 | + |
| 427 | + if ($currentCursor !== null) { |
| 428 | + $decodedCursor = base64_decode($currentCursor, true); |
| 429 | + if ($decodedCursor !== false && is_numeric($decodedCursor)) { |
| 430 | + $currentOffset = $decodedCursor; |
| 431 | + } |
| 432 | + } |
| 433 | + |
| 434 | + $nextOffset = $currentOffset + $returnedCount; |
| 435 | + |
| 436 | + // If we have more items available, return next cursor |
| 437 | + if ($nextOffset < $totalCount) { |
| 438 | + return base64_encode((string) $nextOffset); |
| 439 | + } |
| 440 | + |
| 441 | + return null; |
334 | 442 | } |
335 | 443 | } |
0 commit comments