Limit MCP catalog pagination (#36039)

## Why

MCP catalog discovery must not allow a server to keep pagination running or
growing without bounds.

## What changed

- Apply the shared pagination collector to tool, resource, and resource-template
  discovery.
- Limit each catalog to 100 pages and 1,024 items, and reject pagination cursors
  larger than 64 KiB or any repeated cursor.
- Bound the entire pagination operation by the configured tool timeout, falling
  back to 30 seconds when no timeout is configured.

## Testing

Add unit coverage for every limit and an end-to-end MCP test that preserves
valid multi-page tools while excluding a server that returns an oversized
cursor.

GitOrigin-RevId: 961a1bfa5fe8a265daf56e342a919c980cd720fa
This commit is contained in:
jif authored and copyberry committed 2026-07-29 22:37:09 +00:00
1 parent 3834c47ccb
commit 3e3ae08839
7 files changed
+315 -91

No files matched your search

+14 -19
View File
@@ -591,26 +591,21 @@ pub(crate) async fn list_tools_for_client_uncached(
server_instructions: Option<&str>,
) -> Result<Vec<ToolInfo>> {
let fetch_start = Instant::now();
let tools = match client.protocol_mode() {
McpProtocolMode::Legacy => {
client
.list_tools_with_connector_ids(/*params*/ None, timeout)
.await?
.tools
let protocol_mode = client.protocol_mode();
let tools = collect_paginated("tools/list", timeout, |params| {
let client = Arc::clone(client);
async move {
let response = client
.list_tools_with_connector_ids(params, timeout)
.await?;
let next_cursor = match protocol_mode {
McpProtocolMode::Legacy => None,
McpProtocolMode::V20260728 => response.next_cursor,
};
Ok((response.tools, next_cursor))
}
McpProtocolMode::V20260728 => {
collect_paginated("tools/list", timeout, |params| {
let client = Arc::clone(client);
async move {
let response = client
.list_tools_with_connector_ids(params, timeout)
.await?;
Ok((response.tools, response.next_cursor))
}
})
.await?
}
}
})
.await?
.into_iter()
.map(|tool| {
tool_info_from_listed_tool(