Skip to content

Conversation

@Gaebobman
Copy link

@Gaebobman Gaebobman commented Jan 18, 2026

Problem

Langflow's MCP client tool execution path currently uses a fixed timeout
(e.g. asyncio.wait_for(..., timeout=30s)) for session.call_tool(...).

As a result:

  • Long-running tools that emit MCP progress notifications still get terminated
    by the fixed timeout.
  • This makes Langflow less interoperable with MCP servers that rely on progress
    for long-running operations.

Example:

# tool sends progress every 10–15 seconds
await client.call_tool(..., timeout=30)
# -> times out even though progress notifications are received

Spec reference

According to the MCP Progress specification (2025-11-25):

  • A progressToken in the request metadata allows the server to send progress notifications.
  • Progress notifications indicate that the operation is still in progress.
  • The progress value MUST be monotonically increasing and MUST stop after completion.

Receiving progress thus serves as a reliable signal that the operation is alive,
which clients can use to inform timeout decisions without violating the protocol’s requirements.

See:
https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress#progress

Scope / Transport coverage

This change is intended to apply across all MCP transports supported by Langflow:

  • SSE
  • Streamable HTTP
  • stdio

The behavior should be consistent regardless of transport.

Proposed solution (backend)

Add a progress-aware inactivity timeout mechanism for MCP tool calls:

  1. Register a progress_callback when the session supports it, and include a progressToken in request metadata
    (via kwargs meta when supported, otherwise fallback to arguments _meta injection). If token delivery fails,
    refresh-on-progress is disabled for safety.
  2. Each valid progress notification refreshes the inactivity deadline
    (sliding-window timeout).
  3. Always enforce an absolute maximum total timeout cap as a safety guardrail
    to prevent infinite execution on noisy/buggy progress streams.
  4. Preserve backward compatibility via opt-in or configurable defaults.
  5. Optionally ignore overly-frequent progress updates via a small refresh interval to avoid noisy spam.

Suggested configuration knobs:

  • mcp_inactivity_timeout_seconds
  • mcp_max_total_timeout_seconds
  • mcp_refresh_timeout_on_progress

(Default values can match current behavior unless enabled.)

Expected behavior

  • No progress notifications and refresh disabled -> timeout after inactivity timeout.
  • Periodic progress notifications with refresh enabled -> inactivity deadline is refreshed and call completes.
  • Progress continues but max total timeout is exceeded -> timeout is raised.
  • If max total timeout is set below inactivity timeout, it is auto-adjusted to match inactivity timeout and a warning is logged once.

Test scenarios (example tool duration ~30s):

  • A) Refresh OFF, inactivity=10s, max_total=90s -> times out around 10s.
  • B) Refresh ON, inactivity=10s, max_total=90s -> completes around 30s.
  • C) Refresh ON, inactivity=10s, max_total=20s -> times out around 20s despite progress.
  • D) Refresh ON, inactivity=60s, max_total=30s -> max_total auto-adjusted to 60s with a warning.

FastMCP test tool (used for manual verification):

Note: MCP Python SDK progress callbacks are request-scoped and may omit the progressToken in callback arguments;
the token still exists at the protocol level. Tokenless callbacks are accepted only when request-scoped callbacks
are enabled.
@mcp.tool(description="Sleep 1 second 30 times and report progress.")
async def sleep_with_progress(ctx: Context) -> str:
    for i in range(30):
        await asyncio.sleep(1)
        await ctx.report_progress(i + 1, 30, f"tick {i + 1}/30")
    return "done"

Component / UI changes (in-scope)

Expose user-configurable controls in the MCP Component UI, so users can choose:

  • Toggle: "Refresh timeout on progress"
  • Numeric inputs:
    • "Inactivity timeout (seconds)"
    • "Max total timeout (seconds)"

These should map to the backend configuration knobs above and be persisted
as part of the component configuration.

Impact

  • MCP client core (tool execution paths for SSE / Streamable HTTP / stdio).
  • Backward-compatible if disabled by default.
  • Additional test coverage required.

Questions to maintainers

  1. Should refresh-on-progress be enabled by default or guarded by a config flag?
  2. Are there preferred default values for inactivity timeout and max total cap?
  3. Any preferred config surface (env vars vs component config vs global settings)?
  4. Any existing or planned work related to MCP client timeouts that this should align with?

Summary by CodeRabbit

  • New Features

    • Added configurable timeout settings for MCP tools, including inactivity timeout and maximum total timeout limits
    • Added support for progress-aware timeout refresh, allowing deadline extension when tools report progress updates
    • Exposed three new configuration parameters for controlling timeout behavior
  • Chores

    • Updated MCP dependency to version 1.25.0 or higher

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added the community Pull Request from an external contributor label Jan 18, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a new MCP progress and timeout management subsystem. Dependencies are updated to support MCP 1.25.0, core utilities add timeout configuration types and progress-aware timeout control logic, and the MCPToolsComponent integrates these with three new public configuration inputs. Test coverage validates progress-based deadline refresh and timeout scenarios.

Changes

Cohort / File(s) Summary
Dependency Updates
src/lfx/pyproject.toml
Added mcp>=1.25.0 to dependencies.
MCP Timeout & Progress Subsystem
src/lfx/src/lfx/base/mcp/util.py
Introduced MCPToolTimeoutConfig class with normalization, MCPProgressTimeoutController for per-tool timeout and progress management, and call_tool_with_timeouts() function replacing direct session.call_tool invocations. Integrated timeout configuration into MCPStdioClient and MCPStreamableHttpClient with set_tool_timeout_config() method. Added helpers for progress token injection, progress field extraction, and argument adaptation.
Component Integration
src/lfx/src/lfx/components/models_and_agents/mcp_component.py
Added three public BoolInput and FloatInput configuration inputs for timeout behavior (mcp_refresh_timeout_on_progress, mcp_inactivity_timeout_seconds, mcp_max_total_timeout_seconds). Introduced _apply_timeout_config() method to construct and apply timeout configuration to MCP clients during tool execution.
Test Coverage
src/lfx/tests/unit/base/mcp/__init__.py, test_progress_timeouts.py
Added module docstring and comprehensive test module validating inactivity timeouts, progress-based deadline refresh, max total timeout enforcement, and progress token injection fallback mechanisms.

Sequence Diagram(s)

sequenceDiagram
    participant User as MCPToolsComponent
    participant Config as MCPToolTimeoutConfig
    participant CTT as call_tool_with_timeouts
    participant Controller as MCPProgressTimeoutController
    participant Session as ClientSession
    participant Tool as MCP Tool

    User->>Config: Create with timeout params
    Config->>Config: normalize()
    User->>CTT: Call with config
    CTT->>Controller: Initialize (if progress needed)
    Controller->>Session: Inject progress_token (if supported)
    Session->>Tool: Execute tool with token
    Tool->>Session: Progress update with token
    Session->>Controller: Process progress
    Controller->>Controller: Refresh deadline
    Tool->>Session: Return result
    Session->>CTT: Completion
    CTT->>User: Return result or TimeoutError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning PR introduces timeout functionality test coverage but lacks integration tests for new MCPToolsComponent inputs and async validation patterns. Add integration tests validating timeout configuration inputs, _apply_timeout_config propagation, and async tool execution patterns with edge cases.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main feature: a progress-aware inactivity timeout mechanism for MCP tool calls that refreshes when progress notifications are received.
Test Coverage For New Implementations ✅ Passed PR includes comprehensive test coverage for MCP progress and timeout functionality with 186 lines of tests covering multiple scenarios and helper classes.
Test File Naming And Structure ✅ Passed Test file follows all specified patterns: correct naming (test_progress_timeouts.py), module docstring, pytest structure with @pytest.mark.asyncio, seven descriptive test functions with comprehensive coverage including positive, negative, and edge cases, logically organized with helper classes.
Excessive Mock Usage Warning ✅ Passed Test file uses minimal custom fake classes instead of excessive mocking, enabling deterministic testing of async timeout logic and progress callbacks with clear behavioral assertions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Labels

community Pull Request from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant