-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: add paginated list decorators for prompts, resources, and tools #1286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a some docs with examples? not sure if we have good docs for the lowlevel api but some usage examples would be great (if not just to help with reviewing).
Otherwise this looks good to me. happy with the separate functions to avoid breaking changes. keen to know how you think we should approach this in the high level interface.
91fa584
to
9d72703
Compare
Updated the docs, added example code, and included testing in the PR desription |
66ee42f
to
83d4890
Compare
70435e5
to
5d823c8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backward compatibility: Modifying the existing decorators to require a cursor parameter in the callback would break existing uses of these decorators.
You can make it optional, can't you?
Add list_prompts_paginated, list_resources_paginated, and list_tools_paginated decorators to support cursor-based pagination for listing endpoints. These decorators: - Accept a cursor parameter (can be None for first page) - Return the respective ListResult type directly - Maintain backward compatibility with existing non-paginated decorators - Update tool cache for list_tools_paginated Also includes simplified unit tests that verify cursor passthrough. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Apply automatic formatting from ruff to ensure code meets project standards: - Remove trailing whitespace - Adjust line breaks for consistency - Format function arguments according to line length limits
- Create mcp_simple_pagination example server demonstrating all three paginated endpoints - Add pagination snippets for both server and client implementations - Update README to use snippet-source pattern for pagination examples - Move mutually exclusive note to blockquote format for better visibility - Complete example shows tools, resources, and prompts pagination with different page sizes
47c3f19
to
2b2e80c
Compare
The previous change to run tools directly (without uv run) broke the CI pipeline because the tools are installed in uv's virtual environment which isn't in PATH during pre-commit execution. Reverting to use `uv run --frozen` ensures the tools are found both locally and in CI environments.
], | ||
) | ||
|
||
if transport == "sse": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we doing this every time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just following the pattern of all the other sample servers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to leave this as is, and maybe chnage in a different PR dedicated to simplifying the example servers or something if that's alright with you?
if method: | ||
params.pop("self", None) | ||
params.pop("cls", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't rely on parameter names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh fair, turns out this isn't needed anyway as inspect.signature.parameters
doesn't contain these by default. Updated the unit tests to reflect this.
src/mcp/server/lowlevel/server.py
Outdated
cursor_func = cast(Callable[[types.Cursor | None], Awaitable[types.ListToolsResult]], func) | ||
|
||
async def cursor_handler(req: types.ListToolsRequest): | ||
result = await cursor_func(req.params.cursor if req.params is not None else None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to pass the ListToolRequest instead? What does that object contains?
I'm on my phone only this week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because then you, as a user don't need to type it as Cursor | None.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm well the only thing in the ListToolsRequest is the cursor. Unless you think in the future there's a chance more would be added to it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is more that it makes more sense to receive a request as parameter.
But answering the question... Yes, if something is added in the future, it will be there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alrighty, changed from pasing cursors to passing request objects
Summary
This PR adds pagination support for listing prompts, resources, and tools using cursor-based pagination as defined in the MCP spec.
Design Evolution
Initial Implementation (Updated)
Originally, this PR introduced separate paginated decorators (
list_prompts_paginated
,list_resources_paginated
,list_tools_paginated
) to maintain backward compatibility. The rationale was:Current Implementation (After Discussion)
Based on feedback and discussion, the implementation has evolved to use unified decorators with automatic callback inspection:
list_prompts()
,list_resources()
, andlist_tools()
decorators handle both paginated and non-paginated callbacksaccepts_cursor()
function to inspect callback signatures at runtimeHow It Works
The decorators now automatically detect whether a callback accepts a cursor parameter:
Changes
accepts_cursor()
function for callback signature inspectionlist_prompts()
,list_resources()
, andlist_tools()
decorators to support both signaturesTest plan
🤖 Generated with Claude Code
👨 Below generated by the human, Max Isbey
Manual testing
I spun up the MCP inspector and ran the example pagination server in both stdio mode and SSE mode. Below is some testing evidence using the SSE mode:
Clicking "List More Resources" correctly lists all 30 resources:
Note: this works for prompts and tools as well
Requesting a resource works:
Running the pagination client example code works listing all 30 resources: