Skip to content

feat(agent): make MCP tool call timeout configurable - #18016

Open
var4dev wants to merge 6 commits into
infiniflow:mainfrom
var4dev:feat/agent-tool-timeout
Open

feat(agent): make MCP tool call timeout configurable#18016
var4dev wants to merge 6 commits into
infiniflow:mainfrom
var4dev:feat/agent-tool-timeout

Conversation

@var4dev

@var4dev var4dev commented Aug 8, 2026

Copy link
Copy Markdown

Summary

Make the MCP tool call timeout configurable per agent.

Previously LLMToolPluginCallSession hardcoded a 10s timeout for every
tool call (including MCP tools). This PR:

  • adds a \Tool timeout\ field to agent settings (Advanced Settings, shown
    when MCP tools are connected, default 10s);
  • passes the configured value down to the actual MCP tool call instead of
    the hardcoded 10s;
  • keeps the 10s default for existing agents that don't have the field;
  • adds a unit test verifying the session default timeout is applied and
    that an explicit per-call timeout overrides it.

Files touched: \�gent/tools/base.py, \�gent/component/agent_with_tools.py,
web agent form + locales (en/ru/zh), new unit test under
\ est/unit_test/agent/tools/.

- add Tool timeout field to agent settings (Advanced Settings, shown when MCP tools are connected, default 10s)
- pass the value through to the actual MCP tool call instead of the hardcoded 10s
- keep 10s default for existing agents without the field
- add unit test verifying default_timeout is applied
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. 🌈 python Pull requests that update Python code 💞 feature Feature request, pull request that fullfill a new feature. 🧪 test Pull requests that update test cases. labels Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds configurable MCP tool-call timeouts. The backend applies normalized defaults, the agent form exposes the setting when MCP tools exist, and English, Russian, and Chinese translations describe the field.

Changes

Tool timeout configuration

Layer / File(s) Summary
Backend timeout propagation
agent/tools/base.py, agent/component/agent_with_tools.py, test/unit_test/agent/test_llm_tool_plugin_session.py
LLMToolPluginCallSession accepts a configurable default timeout and applies it when request timeouts are omitted. Tests cover fallback, overrides, and synchronous calls.
Web timeout configuration
web/src/pages/agent/constant/index.tsx, web/src/pages/agent/form/agent-form/index.tsx, web/src/pages/agent/form/agent-form/use-values.ts, web/src/locales/*
The agent form initializes, validates, and displays tool_timeout when MCP tools exist. Locales include the field label and tooltip.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant LLMToolPluginCallSession
  participant MCPTool
  Agent->>LLMToolPluginCallSession: Create session with default_timeout
  LLMToolPluginCallSession->>MCPTool: Call with resolved timeout
  MCPTool-->>LLMToolPluginCallSession: Return tool result
Loading

Poem

A rabbit sets ten seconds bright,
MCP tools hop within the limit tight.
Forms show the setting, clear and neat,
Tests check defaults and overrides complete.
Localization helps each burrow read.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: configurable MCP tool call timeouts.
Description check ✅ Passed The description includes the required Summary section, background context, implementation details, compatibility behavior, and test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
agent/tools/base.py (1)

51-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the resolved timeout.

The new timeout selection changes runtime behavior, but the existing tool-call logs do not record it. Include the resolved timeout in the invocation or completion log so operators can confirm whether an agent uses the configured value or the 10-second default.

As per coding guidelines, **/*.py: Add logging for new flows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agent/tools/base.py` around lines 51 - 60, Update tool_call_async to log the
resolved request_timeout after applying the default_timeout fallback, including
it in the existing invocation or completion log so both configured and default
timeout values are observable. Use the established logging flow and preserve the
current timeout behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@agent/tools/base.py`:
- Around line 51-60: Validate the timeout in the tool component constructor
before creating LLMToolPluginCallSession, rejecting values below the minimum of
1 instead of allowing invalid defaults to propagate. In agent/tools/base.py
around the constructor and default_timeout assignment, enforce this validation;
update agent/component/agent_with_tools.py lines 115-117 to stop converting
falsy tool_timeout values to 10 and pass the validated value consistently. The
form field at web/src/pages/agent/form/agent-form/index.tsx line 79 requires no
direct change unless needed to preserve submission of zero for server-side
validation.

In `@test/unit_test/agent/tools/test_llm_tool_plugin_session.py`:
- Around line 48-64: Update _BlockingSession to record the timeout supplied by
MCP calls, then assert exact propagation for the session default and explicit
request timeout instead of relying on elapsed-time thresholds. Add coverage for
the 10-second constructor default and the synchronous tool_call wrapper, using a
non-blocking session behavior so tests do not wait 10 seconds.

---

Nitpick comments:
In `@agent/tools/base.py`:
- Around line 51-60: Update tool_call_async to log the resolved request_timeout
after applying the default_timeout fallback, including it in the existing
invocation or completion log so both configured and default timeout values are
observable. Use the established logging flow and preserve the current timeout
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a86f16de-ed27-4938-a6da-4a58aff1e8d2

📥 Commits

Reviewing files that changed from the base of the PR and between 2d63ad6 and 336c618.

📒 Files selected for processing (9)
  • agent/component/agent_with_tools.py
  • agent/tools/base.py
  • test/unit_test/agent/tools/test_llm_tool_plugin_session.py
  • web/src/locales/en.ts
  • web/src/locales/ru.ts
  • web/src/locales/zh.ts
  • web/src/pages/agent/constant/index.tsx
  • web/src/pages/agent/form/agent-form/index.tsx
  • web/src/pages/agent/form/agent-form/use-values.ts

Comment thread agent/tools/base.py Outdated
Comment on lines +51 to +60
def __init__(self, tools_map: dict[str, object], callback: partial, default_timeout: float = 10):
self.tools_map = tools_map
self.callback = callback
self.default_timeout = default_timeout

def tool_call(self, name: str, arguments: dict[str, Any], timeout: float | int = 10) -> Any:
def tool_call(self, name: str, arguments: dict[str, Any], timeout: float | int | None = None) -> Any:
return asyncio.run(self.tool_call_async(name, arguments, request_timeout=timeout))

async def tool_call_async(self, name: str, arguments: dict[str, Any], request_timeout: float | int = 10) -> Any:
async def tool_call_async(self, name: str, arguments: dict[str, Any], request_timeout: float | int | None = None) -> Any:
request_timeout = self.default_timeout if request_timeout is None else request_timeout

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(base\.py$|agent_with_tools\.py$|agent-form/index\.tsx$)' || true

echo "== base.py outline =="
ast-grep outline agent/tools/base.py 2>/dev/null || true

echo "== base.py relevant lines =="
cat -n agent/tools/base.py | sed -n '1,140p'

echo "== agent_with_tools.py relevant lines =="
cat -n agent/component/agent_with_tools.py | sed -n '1,180p'

echo "== agent-form schema relevant lines =="
cat -n web/src/pages/agent/form/agent-form/index.tsx | sed -n '1,140p'

echo "== search timeout usages =="
rg -n "tool_timeout|default_timeout|request_timeout|toolcall_session|ToolPluginCallSession|LLMToolPluginCallSession|NumberInputStepper|min" agent web -S

Repository: infiniflow/ragflow

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tracked target files =="
git ls-files | rg '(^|/)agent/tools/base\.py$|(^|/)agent/component/agent_with_tools\.py$|(^|/)web/src/pages/agent/form/agent-form/index\.tsx$' || true

echo "== base.py relevant lines =="
sed -n '1,160p' agent/tools/base.py

echo "== agent_with_tools.py relevant lines =="
sed -n '80,145p' agent/component/agent_with_tools.py

echo "== agent-form relevant lines =="
sed -n '55,95p' web/src/pages/agent/form/agent-form/index.tsx

echo "== focused timeout/tool_timeout usages (files only first) =="
rg -n -S --glob '!docs/**' --glob '!*.png' --glob '!*.jpg' --glob '!*.svg' --glob '!*.txt' \
  'tool_timeout|default_timeout|request_timeout|LLMToolPluginCallSession|NumberInputStepper|min' agent web -s || true

echo "== behavioral probe: current truthiness/default expressions =="
python3 - <<'PY'
cases = [None, 0, "0", "14", -1, float("nan"), float("inf")]
for case in cases:
    agent_fallback = float(case or 10) if case is not None else float(10)
    base_default = 10 if case is None else case
    print({"case": case, "agent_with_tools_fallback_value": agent_fallback, "tool_call_default_if_none": base_default})
PY

Repository: infiniflow/ragflow

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== exact git status for target files =="
git status --short -- agent/tools/base.py agent/component/agent_with_tools.py web/src/pages/agent/form/agent-form/index.tsx || true

echo "== exact tracked target files =="
git ls-files -- agent/tools/base.py agent/component/agent_with_tools.py web/src/pages/agent/form/agent-form/index.tsx

echo "== exact target file sizes =="
wc -l agent/tools/base.py agent/component/agent_with_tools.py web/src/pages/agent/form/agent-form/index.tsx

echo "== sed exact target ranges =="
sed -n '1,145p' agent/tools/base.py
printf '\n--- agent_with_tools.py ---\n'
sed -n '90,130p' agent/component/agent_with_tools.py
printf '\n--- agent-form/schema ---\n'
sed -n '60,90p' web/src/pages/agent/form/agent-form/index.tsx

echo "== focused timeout/tool_timeout matches in tracked files =="
rg -n -S \
  'tool_timeout|default_timeout|request_timeout|LLMToolPluginCallSession|NumberInputStepper|z\.coerce\.number\(\)\.optional|z\.number\(\).*optional|NumberInputStepper' \
  agent/tools/base.py agent/component/agent_with_tools.py web/src/pages/agent/form/agent-form/index.tsx || true

printf '%s\n' '--- behavioral probe: truthiness/default expressions ---'
python3 - <<'PY'
cases = [
    ("missing", None, 10),
    ("0", 0, float(0)),
    ("empty string", "", float(0)),
    ("14", 14, float(14)),
    ("negative", -1, float(-1)),
    ("nan", float("nan"), float("nan")),
    ("inf", float("inf"), float("inf")),
]
print("agent/component fallback: float(value or 10) if value is not None else float(10)")
print("agent/tools default: 10 if request_timeout is None else request_timeout")
for label, value, fallback in cases:
    agent_value_or_10 = float(value or 10)
    default_value = 10 if value is None else value
    print(label, "value=", repr(value), "agent fallback=", repr(fallback), "base default=", repr(default_value), "Python timeout passes?", timeout_is_valid(default_value))
PY

Repository: infiniflow/ragflow

Length of output: 11777


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== common/mcp_tool_call_conn.py outline =="
ast-grep outline common/mcp_tool_call_conn.py 2>/dev/null || true

echo "== common/mcp_tool_call_conn.py relevant lines =="
sed -n '1,240p' common/mcp_tool_call_conn.py

echo "== focused call signature checks =="
rg -n -S \
  'class MCPToolBinding|class MCPToolCallSession|class ToolCallSession|def (tool_call|tool_call_async|invoke)|thread_pool_exec|request_timeout|timeout' \
  common/mcp_tool_call_conn.py

Repository: infiniflow/ragflow

Length of output: 13718


Validate tool_timeout at component construction.

The form accepts optional values, including zero, and agent/component/agent_with_tools.py#L115-L117 converts falsy values to 10. agent/tools/base.py#L60 then uses whatever default came from construction, so invalid settings can hide behind the fallback or propagate to MCP calls. Reject values below the intended minimum (for example, less than 1) before creating LLMToolPluginCallSession.

📍 Affects 3 files
  • agent/tools/base.py#L51-L60 (this comment)
  • agent/component/agent_with_tools.py#L115-L117
  • web/src/pages/agent/form/agent-form/index.tsx#L79-L79
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agent/tools/base.py` around lines 51 - 60, Validate the timeout in the tool
component constructor before creating LLMToolPluginCallSession, rejecting values
below the minimum of 1 instead of allowing invalid defaults to propagate. In
agent/tools/base.py around the constructor and default_timeout assignment,
enforce this validation; update agent/component/agent_with_tools.py lines
115-117 to stop converting falsy tool_timeout values to 10 and pass the
validated value consistently. The form field at
web/src/pages/agent/form/agent-form/index.tsx line 79 requires no direct change
unless needed to preserve submission of zero for server-side validation.

Comment thread test/unit_test/agent/tools/test_llm_tool_plugin_session.py Outdated
- fall back to the 10s default when tool_timeout is missing or below 1
- stop converting falsy tool_timeout values to 10 in agent_with_tools
- log the resolved request_timeout on tool invocation
- rewrite unit tests to record and assert the exact timeout received by
  the MCP session instead of relying on elapsed-time thresholds

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/unit_test/agent/tools/test_llm_tool_plugin_session.py (1)

64-68: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover configured values below 1.

LLMToolPluginCallSession maps None and values below 1 to the 10-second default. This test covers only None. Add cases for 0 and a negative value so the required fallback remains protected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit_test/agent/tools/test_llm_tool_plugin_session.py` around lines 64 -
68, Extend the timeout coverage in
test_constructor_default_timeout_is_ten_seconds for LLMToolPluginCallSession by
adding cases with configured timeouts of 0 and a negative value. Verify each
call still returns "done" and recording.timeouts contains [10], preserving the
existing None/default case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/unit_test/agent/tools/test_llm_tool_plugin_session.py`:
- Around line 64-68: Extend the timeout coverage in
test_constructor_default_timeout_is_ten_seconds for LLMToolPluginCallSession by
adding cases with configured timeouts of 0 and a negative value. Verify each
call still returns "done" and recording.timeouts contains [10], preserving the
existing None/default case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fea0c115-8176-4ba8-838f-cb41bca5c21f

📥 Commits

Reviewing files that changed from the base of the PR and between 336c618 and 15db558.

📒 Files selected for processing (3)
  • agent/component/agent_with_tools.py
  • agent/tools/base.py
  • test/unit_test/agent/tools/test_llm_tool_plugin_session.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • agent/tools/base.py

@JinHai-CN JinHai-CN added the ci Continue Integration label Aug 10, 2026
@JinHai-CN

Copy link
Copy Markdown
Contributor
_ ERROR collecting test/unit_test/agent/tools/test_llm_tool_plugin_session.py __
ImportError while importing test module '/home/infiniflow/runners_work/inf29-b7d4861e1e89/ragflow/ragflow/test/unit_test/agent/tools/test_llm_tool_plugin_session.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
.venv/lib/python3.13/site-packages/_pytest/python.py:507: in importtestmodule
    ???
.venv/lib/python3.13/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/home/alice/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1395: in _gcd_import
    ???
<frozen importlib._bootstrap>:1360: in _find_and_load
    ???
<frozen importlib._bootstrap>:1331: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:935: in _load_unlocked
    ???
.venv/lib/python3.13/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
test/unit_test/agent/tools/test_llm_tool_plugin_session.py:21: in <module>
    ???
E   ImportError: cannot import name 'LLMToolPluginCallSession' from 'agent.tools.base' (unknown location)

…espace conflict

The test lived in test/unit_test/agent/tools/, where pytest's prepend
import mode resolves 'agent' to the test folder as a namespace package
(no __init__.py), breaking 'from agent.tools.base import ...' at
collection ('cannot import name ... (unknown location)'). Move it to
test/unit_test/agent/ so the root 'agent' package is imported correctly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/unit_test/agent/test_llm_tool_plugin_session.py`:
- Around line 64-69: Extend test_constructor_default_timeout_is_ten_seconds to
include 0.5 in the configured values, preserving the existing assertion that a
positive timeout below one second falls back to a 10-second tool-call timeout.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1aa1c99b-668d-4367-9512-0ad684649d03

📥 Commits

Reviewing files that changed from the base of the PR and between 0ac7aed and 244e5db.

📒 Files selected for processing (1)
  • test/unit_test/agent/test_llm_tool_plugin_session.py

Comment thread test/unit_test/agent/test_llm_tool_plugin_session.py
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.65%. Comparing base (4eaad55) to head (73aba03).
⚠️ Report is 12 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #18016   +/-   ##
=======================================
  Coverage   90.65%   90.65%           
=======================================
  Files          10       10           
  Lines         717      717           
  Branches      118      118           
=======================================
  Hits          650      650           
  Misses         39       39           
  Partials       28       28           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

ci Continue Integration 💞 feature Feature request, pull request that fullfill a new feature. 🌈 python Pull requests that update Python code size:M This PR changes 30-99 lines, ignoring generated files. 🧪 test Pull requests that update test cases.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants