Skip to content

fix(mcp_client): enforce overall timeout for list_tools pagination (#836) - #912

Open
Artemon-line wants to merge 1 commit into
praxis-proxy:mainfrom
Artemon-line:836-mcp-pagination-timeout
Open

fix(mcp_client): enforce overall timeout for list_tools pagination (#836)#912
Artemon-line wants to merge 1 commit into
praxis-proxy:mainfrom
Artemon-line:836-mcp-pagination-timeout

Conversation

@Artemon-line

Copy link
Copy Markdown
Contributor

Summary

Enforces a top-level tokio::time::timeout wrapper across the complete list_tools and call_tool client lifecycle (DNS resolution, transport connection/handshake, and multi-page tools listing pagination).

Previously, each individual tools/list page request had its own timeout check inside paginate_tools. A degraded or malicious MCP server returning paginated tools (up to 100 pages) could delay every page request by slightly under the timeout duration, causing list_tools to execute for up to ~100x the configured timeout.

Related issue

Closes #836

Validation

  • Unit tests (cargo test -p praxis-ai-apis -- mcp_client)
  • Integration or functional tests (added list_tools_cumulative_pagination_timeout with mock slow paginated MCP server)
  • make lint

Checklist

  • I reviewed every changed line and can explain the change.
  • New capabilities include an example config and functional example test.
  • User-facing behavior and generated documentation are updated.
  • Performance-sensitive changes include appropriate benchmark or load-test evidence.
  • Commits are signed and include a Signed-off-by trailer.

Breaking changes

None.

@Artemon-line
Artemon-line marked this pull request as ready for review September 3, 2026 13:33
@Artemon-line
Artemon-line requested review from a team, crstrn13 and leseb September 3, 2026 13:33

@praxis-bot praxis-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP Client Cumulative Timeout Review

Purpose: Wraps list_tools and call_tool in a top-level tokio::time::timeout so that cumulative pagination, DNS, and transport time cannot exceed the configured timeout. Previously a malicious MCP server could delay each paginated page by just under the per-page timeout, causing list_tools to run for up to ~100x the configured duration.

Assessment: The outer timeout correctly closes the cumulative-pagination DoS vector described in #836. The new test (list_tools_cumulative_pagination_timeout) exercises the fix with a real mock MCP server. The parse_display_url extraction is a clean way to make a display URL available for the outer timeout error without running resolve_and_validate first.

Severity Count
Critical 0
Large 0
Medium 1

.map_err(|_source| McpClientError::Connection {
url: display_url.clone(),
})?;
let tools = paginate_tools(&client, timeout, max_tools, &display_url).await?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: inner timeouts are now unreachable dead code.

The outer tokio::time::timeout(timeout, Box::pin(work)) at line 256 uses the same timeout duration as every inner timeout: the connection timeout here (line 243), the per-page timeouts inside paginate_tools (line 353 in the unchanged code), and the call_tool timeout in the other function (line 316). Because the outer timer starts strictly before any inner timer using the same duration, the inner timeouts can never fire -- the outer one always expires first and cancels the inner future.

This means ~30 lines of inner timeout/map_err handling across both functions, plus the timeout parameter on paginate_tools, are dead code paths that will never execute.

Suggested change: either remove the inner timeouts (simplest) or, if per-operation limits are desired alongside the overall limit, use a shorter per-operation duration (e.g. timeout / 2 or a separate config field). Keeping both at the same duration is misleading -- a reader would assume both can fire.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP tools/list pagination can exceed the configured timeout by 100x

2 participants