Skip to content

Fix MCP connection timeouts and disable tool call retries by default - #6744

Open
lorenzejay wants to merge 4 commits into
mainfrom
lorenze/imp/mcp-connection-failures
Open

Fix MCP connection timeouts and disable tool call retries by default#6744
lorenzejay wants to merge 4 commits into
mainfrom
lorenze/imp/mcp-connection-failures

Conversation

@lorenzejay

@lorenzejay lorenzejay commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Note

Medium Risk
Changes default MCP tool-call behavior (no automatic retries) and rework async connection/cleanup paths that agents depend on; misclassification or teardown bugs could affect production MCP integrations.

Overview
Hardens MCP connect/teardown so connect_timeout applies to transport SDK startup (HTTP/SSE/stdio) and session initialize(), using in-task async_timeout instead of asyncio.wait_for to avoid anyio “cancel scope in a different task” failures. Adds async-timeout on Python < 3.11 and wires timeout through BaseTransport.

Connection errors and cleanup are clearer: shared helpers classify failures (timeout, TLS, auth, network) for events, unwind partial connects via AsyncExitStack.__aexit__, and surface nested TLS/cancel cases without masking the root error. HTTP disconnect passes the triggering exception into the SDK context exit; optional best-effort logging on cleanup.

Tool execution no longer retries by default (retry_tool_calls=False); retries are opt-in to avoid replaying mutating MCP calls after a lost response.

Tests add lifecycle coverage in test_client.py; crewai-tools MCP adapter integration tests spawn servers with sys.executable instead of uv run python.

Reviewed by Cursor Bugbot for commit d0d2547. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

MCP transport connections now use configurable, Python-version-compatible timeouts. MCPClient initialization, cleanup, and tool-call retries were adjusted, with retries disabled by default. New tests cover lifecycle behavior, and MCP subprocess tests use the active Python interpreter.

Changes

MCP lifecycle updates

Layer / File(s) Summary
Timeout compatibility and transport contracts
lib/crewai/pyproject.toml, lib/crewai/src/crewai/mcp/_utils.py, lib/crewai/src/crewai/mcp/transports/*
Adds the Python-version-specific timeout helper, async-timeout dependency, configurable transport timeout, and timeout enforcement for HTTP, SSE, and stdio connections.
Client initialization and tool-call policy
lib/crewai/src/crewai/mcp/client.py
Propagates connection timeouts, bounds session initialization by the remaining timeout, preserves original errors when cleanup fails, and makes tool retries opt-in.
Client lifecycle regression coverage
lib/crewai/tests/mcp/test_client.py
Tests transport startup timeouts, same-task context management, default retry suppression, explicit retries, and cleanup logging.
MCP test runtime compatibility
lib/crewai-tools/tests/adapters/mcp_adapter_test.py, lib/crewai/tests/mcp/test_tool_resolver_native.py
Runs adapter subprocesses with the current Python interpreter and closes the mocked resolver coroutine before raising.

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant Transport
  participant MCPSession
  MCPClient->>Transport: connect within configured timeout
  Transport-->>MCPClient: transport context and streams
  MCPClient->>MCPSession: initialize within remaining timeout
  MCPSession-->>MCPClient: initialization result
Loading

Suggested reviewers: joaomdmoura

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% 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
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.
Title check ✅ Passed The title clearly summarizes the main changes: fixing MCP connection timeouts and disabling tool-call retries by default.
Description check ✅ Passed The description directly explains the timeout, cleanup, retry, dependency, and test changes in the pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch lorenze/imp/mcp-connection-failures
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lorenze/imp/mcp-connection-failures

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

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 `@lib/crewai/src/crewai/mcp/transports/http.py`:
- Around line 90-93: Preserve transport timeout exceptions so
MCPClient.connect() reports them as "timeout" rather than "network": in
lib/crewai/src/crewai/mcp/transports/http.py lines 90-93, retain TimeoutError or
use a dedicated timeout exception recognized by MCPClient; in
lib/crewai/src/crewai/mcp/transports/sse.py lines 72-73, handle TimeoutError
before broad Exception/ConnectionError handling; and in
lib/crewai/src/crewai/mcp/transports/stdio.py lines 101-102, preserve
TimeoutError through both the timeout wrapper and transport exception branch.
🪄 Autofix (Beta)

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: e78cd7ee-6ff5-44ea-836d-289b2e450b69

📥 Commits

Reviewing files that changed from the base of the PR and between ebe0082 and 2f6591d.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • lib/crewai-tools/tests/adapters/mcp_adapter_test.py
  • lib/crewai/pyproject.toml
  • lib/crewai/src/crewai/mcp/_utils.py
  • lib/crewai/src/crewai/mcp/client.py
  • lib/crewai/src/crewai/mcp/transports/base.py
  • lib/crewai/src/crewai/mcp/transports/http.py
  • lib/crewai/src/crewai/mcp/transports/sse.py
  • lib/crewai/src/crewai/mcp/transports/stdio.py
  • lib/crewai/tests/mcp/test_client.py
  • lib/crewai/tests/mcp/test_tool_resolver_native.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

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 `@lib/crewai/src/crewai/mcp/transports/http.py`:
- Around line 90-93: Preserve transport timeout exceptions so
MCPClient.connect() reports them as "timeout" rather than "network": in
lib/crewai/src/crewai/mcp/transports/http.py lines 90-93, retain TimeoutError or
use a dedicated timeout exception recognized by MCPClient; in
lib/crewai/src/crewai/mcp/transports/sse.py lines 72-73, handle TimeoutError
before broad Exception/ConnectionError handling; and in
lib/crewai/src/crewai/mcp/transports/stdio.py lines 101-102, preserve
TimeoutError through both the timeout wrapper and transport exception branch.
🪄 Autofix (Beta)

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: e78cd7ee-6ff5-44ea-836d-289b2e450b69

📥 Commits

Reviewing files that changed from the base of the PR and between ebe0082 and 2f6591d.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • lib/crewai-tools/tests/adapters/mcp_adapter_test.py
  • lib/crewai/pyproject.toml
  • lib/crewai/src/crewai/mcp/_utils.py
  • lib/crewai/src/crewai/mcp/client.py
  • lib/crewai/src/crewai/mcp/transports/base.py
  • lib/crewai/src/crewai/mcp/transports/http.py
  • lib/crewai/src/crewai/mcp/transports/sse.py
  • lib/crewai/src/crewai/mcp/transports/stdio.py
  • lib/crewai/tests/mcp/test_client.py
  • lib/crewai/tests/mcp/test_tool_resolver_native.py
🛑 Comments failed to post (1)
lib/crewai/src/crewai/mcp/transports/http.py (1)

90-93: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
# Run under each supported project Python version.
python - <<'PY'
import asyncio

try:
    from asyncio import timeout
except ImportError:
    from async_timeout import timeout

async def main():
    try:
        async with timeout(0):
            await asyncio.sleep(0)
    except TimeoutError as error:
        print(type(error).__name__, repr(str(error)))

asyncio.run(main())
PY

Repository: crewAIInc/crewAI

Length of output: 170


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf 'Repository files matching mcp transports/client:\n'
git ls-files | rg 'lib/crewai/src/crewai/mcp/(transports|.*client.*)' || true

printf '\nRelevant file excerpts:\n'
for f in lib/crewai/src/crewai/mcp/transports/http.py lib/crewai/src/crewai/mcp/transports/sse.py lib/crewai/src/crewai/mcp/transports/stdio.py; do
  [ -f "$f" ] && { printf '\n--- %s ---\n' "$f"; sed -n '1,140p' "$f" | nl -ba; }
done

printf '\nSearch for timeout handling and MCPClient connect:\n'
rg -n "class MCPClient|def connect|timeout|TimeoutError|ConnectionError|async_timeout|asyncio.timeout|timeout_event|emit|network|connect_timeout" lib/crewai/src/crewai/mcp -S

Repository: crewAIInc/crewAI

Length of output: 587


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for f in lib/crewai/src/crewai/mcp/transports/http.py lib/crewai/src/crewai/mcp/transports/sse.py lib/crewai/src/crewai/mcp/transports/stdio.py lib/crewai/src/crewai/mcp/client.py; do
  if [ -f "$f" ]; then
    echo "=== $f ==="
    cat -n "$f" | sed -n '1,160p'
  fi
done

echo "=== focused searches ==="
rg -n "class MCPClient|async def connect|def connect|connect_timeout|async_timeout|asyncio.timeout|TimeoutError|ErrorType|ErrorType|emit|ConnectionError" lib/crewai/src/crewai/mcp -S

Repository: crewAIInc/crewAI

Length of output: 31729


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "=== lib/crewai/src/crewai/mcp/client.py lines 148-275 ==="
cat -n lib/crewai/src/crewai/mcp/client.py | sed -n '148,275p'

echo "=== lib/crewai/src/crewai/mcp/client.py lines 334-360 ==="
cat -n lib/crewai/src/crewai/mcp/client.py | sed -n '334,360p'

echo "=== MCPConnectionFailedEvent definition ==="
fd -a 'mcp_events.py$|mcp_events' . | while read -r f; do
  echo "--- $f ---"
  cat -n "$f"
done

Repository: crewAIInc/crewAI

Length of output: 11403


Preserve transport-level timeouts for MCPClient.

MCPClient.connect() emits "timeout" only for asyncio.TimeoutError; wrapping transport timeout failures as ConnectionError first makes them emit as "network" failures.

  • lib/crewai/src/crewai/mcp/transports/http.py#L90-L95: preserve a timeout-specific exception, or add a dedicated transport timeout error that MCPClient recognizes.
  • lib/crewai/src/crewai/mcp/transports/sse.py#L72-L85: catch and preserve TimeoutError before Exception/ConnectionError.
  • lib/crewai/src/crewai/mcp/transports/stdio.py#L101-L108: preserve TimeoutError through both the timeout wrapper and the transport Exception branch.
📍 Affects 3 files
  • lib/crewai/src/crewai/mcp/transports/http.py#L90-L93 (this comment)
  • lib/crewai/src/crewai/mcp/transports/sse.py#L72-L73
  • lib/crewai/src/crewai/mcp/transports/stdio.py#L101-L102
🤖 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 `@lib/crewai/src/crewai/mcp/transports/http.py` around lines 90 - 93, Preserve
transport timeout exceptions so MCPClient.connect() reports them as "timeout"
rather than "network": in lib/crewai/src/crewai/mcp/transports/http.py lines
90-93, retain TimeoutError or use a dedicated timeout exception recognized by
MCPClient; in lib/crewai/src/crewai/mcp/transports/sse.py lines 72-73, handle
TimeoutError before broad Exception/ConnectionError handling; and in
lib/crewai/src/crewai/mcp/transports/stdio.py lines 101-102, preserve
TimeoutError through both the timeout wrapper and transport exception branch.

@github-actions github-actions Bot added size/XL and removed size/L labels Jul 31, 2026
Comment thread lib/crewai/src/crewai/mcp/client.py Dismissed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cf4d891. Configure here.

Comment thread lib/crewai/src/crewai/mcp/transports/http.py Outdated
Comment thread lib/crewai/src/crewai/mcp/client.py
Comment thread lib/crewai/src/crewai/mcp/transports/sse.py
Comment thread lib/crewai/src/crewai/mcp/transports/sse.py
max_retries: int = MCP_MAX_RETRIES,
cache_tools_list: bool = False,
logger: logging.Logger | None = None,
retry_tool_calls: bool = False,

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.

is this intended? jsut asking because disabling tool-call retries by default is a behavior change, so want to point it out

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants