Skip to content

Conversation

@phact
Copy link
Collaborator

@phact phact commented Dec 12, 2025

The /api/v2/mcp/servers?action_count=true endpoint in src/backend/base/langflow/api/v2/mcp.py calls update_tools() for each server to count tools which spanws mcp-proxy subprocesses but never cleans them up.

Noticed this when langflow was using 13gb of memory:

image

about 500 procs each w/ 10's of mb of RAM allocated after a couple of days of langflow being up.

user@f58f3aae4e79:/app$ count=0
for p in /proc/[0-9]*; do
  [ -r "$p/cmdline" ] || continue
  if tr '\0' ' ' < "$p/cmdline" 2>/dev/null | grep -q 'mcp-proxy'; then
    count=$((count+1))
  fi
done
echo "mcp-proxy processes: $count"
mcp-proxy processes: 554

Summary by CodeRabbit

  • Bug Fixes
    • Improved resource management to prevent potential leaks and ensure proper cleanup of server connections.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

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 get_servers function now explicitly manages MCP client instances (MCPStdioClient and MCPStreamableHttpClient) by creating them within the per-server check workflow and ensuring their cleanup via a finally block, preventing resource and process leaks.

Changes

Cohort / File(s) Summary
MCP Client Lifecycle Management
src/backend/base/langflow/api/v2/mcp.py
Modified get_servers function to create and own MCP client instances per server check, pass them to update_tools, and add finally block cleanup logic to disconnect both clients, preventing resource leaks. Added local imports for MCPStdioClient and MCPStreamableHttpClient.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring extra attention:

  • Verify that both MCP clients are properly disconnected in all code paths (success and error scenarios)
  • Confirm the finally block executes reliably within concurrent per-server checks without blocking operations
  • Check that the local client creation doesn't introduce any unintended side effects with the update_tools function interface
  • Validate that no other code paths in get_servers still create or reference client instances that might cause duplicate resource allocation

Suggested labels

bug

Suggested reviewers

  • edwinjosechittilappilly

Pre-merge checks and finishing touches

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error Bug fix for MCP resource leak lacks any regression tests to verify cleanup behavior and prevent future regressions. Add regression tests in src/backend/tests/unit/api/v2/ to verify disconnect() is called in success and error scenarios.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Test Quality And Coverage ⚠️ Warning PR introduces MCP client resource cleanup logic in finally block but lacks tests validating this cleanup behavior and resource leak fixes. Add pytest tests using @pytest.mark.asyncio and unittest.mock.AsyncMock to verify disconnect() calls in finally block for both success and error scenarios.
Test File Naming And Structure ⚠️ Warning PR modifies src/backend/base/langflow/api/v2/mcp.py to fix a process leak but adds no corresponding test files for this critical v2 API change. Create test_mcp.py in src/backend/tests/unit/api/v2/ with tests for get_servers endpoint, client cleanup, disconnect() calls, and error scenarios.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: mcp-proxy process leak' directly and accurately summarizes the main change: fixing a resource leak where mcp-proxy processes were not being cleaned up in the MCP servers endpoint.
Excessive Mock Usage Warning ✅ Passed The PR modifies only src/backend/base/langflow/api/v2/mcp.py to fix resource leak by adding cleanup in finally block. No test files are modified.

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.

@github-actions github-actions bot added the bug Something isn't working label Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 12, 2025

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 16%
16.42% (4606/28041) 9.73% (2106/21644) 10.76% (664/6166)

Unit Test Results

Tests Skipped Failures Errors Time
1803 0 💤 0 ❌ 0 🔥 24.462s ⏱️

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/backend/base/langflow/api/v2/mcp.py (1)

209-213: Wrap each disconnect in try/except to ensure both are attempted.

If mcp_stdio_client.disconnect() raises an exception, mcp_streamable_http_client.disconnect() will be skipped, potentially still causing leaks. As per coding guidelines, use try/except for proper cleanup.

         finally:
             # Always disconnect clients to prevent mcp-proxy process leaks
             # These clients spawn subprocesses that need to be explicitly terminated
-            await mcp_stdio_client.disconnect()
-            await mcp_streamable_http_client.disconnect()
+            try:
+                await mcp_stdio_client.disconnect()
+            except Exception:
+                await logger.aexception(f"Error disconnecting stdio client for {server_name}")
+            try:
+                await mcp_streamable_http_client.disconnect()
+            except Exception:
+                await logger.aexception(f"Error disconnecting http client for {server_name}")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9a537b and 271a230.

📒 Files selected for processing (1)
  • src/backend/base/langflow/api/v2/mcp.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/backend/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

src/backend/**/*.py: Use FastAPI async patterns with await for async operations in component execution methods
Use asyncio.create_task() for background tasks and implement proper cleanup with try/except for asyncio.CancelledError
Use queue.put_nowait() for non-blocking queue operations and asyncio.wait_for() with timeouts for controlled get operations

Files:

  • src/backend/base/langflow/api/v2/mcp.py
src/backend/base/langflow/api/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

Backend API endpoints should be organized by version (v1/, v2/) under src/backend/base/langflow/api/ with specific modules for features (chat.py, flows.py, users.py, etc.)

Files:

  • src/backend/base/langflow/api/v2/mcp.py
🧬 Code graph analysis (1)
src/backend/base/langflow/api/v2/mcp.py (1)
src/lfx/src/lfx/base/mcp/util.py (3)
  • MCPStdioClient (1021-1231)
  • MCPStreamableHttpClient (1234-1506)
  • update_tools (1514-1659)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 5
  • GitHub Check: Lint Backend / Run Mypy (3.12)
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 2
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 1
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 4
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 3
  • GitHub Check: Lint Backend / Run Mypy (3.11)
  • GitHub Check: Lint Backend / Run Mypy (3.10)
  • GitHub Check: Run Backend Tests / Integration Tests - Python 3.10
  • GitHub Check: Run Backend Tests / LFX Tests - Python 3.10
  • GitHub Check: Test Docker Images / Test docker images
  • GitHub Check: Test Starter Templates
  • GitHub Check: Optimize new Python code in this PR
  • GitHub Check: Update Component Index
  • GitHub Check: Update Starter Projects
🔇 Additional comments (2)
src/backend/base/langflow/api/v2/mcp.py (2)

152-157: Good approach: Creating locally-owned client instances.

Creating the MCP client instances within the function scope and passing them to update_tools gives this code explicit control over their lifecycle, enabling proper cleanup in the finally block.


159-164: Correct: Passing owned client instances to update_tools.

This ensures update_tools uses the caller-provided instances rather than creating its own, which previously caused the leak.

@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@codecov
Copy link

codecov bot commented Dec 12, 2025

Codecov Report

❌ Patch coverage is 0% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 33.02%. Comparing base (a54e508) to head (87d54c9).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/backend/base/langflow/api/v2/mcp.py 0.00% 5 Missing ⚠️

❌ Your patch status has failed because the patch coverage (0.00%) is below the target coverage (40.00%). You can increase the patch coverage or adjust the target coverage.
❌ Your project status has failed because the head coverage (39.26%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #10988      +/-   ##
==========================================
- Coverage   33.02%   33.02%   -0.01%     
==========================================
  Files        1388     1388              
  Lines       65544    65549       +5     
  Branches     9680     9680              
==========================================
+ Hits        21648    21649       +1     
- Misses      42798    42802       +4     
  Partials     1098     1098              
Flag Coverage Δ
backend 52.29% <0.00%> (-0.01%) ⬇️
frontend 15.16% <ø> (ø)
lfx 39.26% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/backend/base/langflow/api/v2/mcp.py 57.14% <0.00%> (-2.24%) ⬇️

... and 2 files with indirect coverage changes

🚀 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.

server_name=server_name,
server_config=server_list["mcpServers"][server_name],
mcp_stdio_client=mcp_stdio_client,
mcp_streamable_http_client=mcp_streamable_http_client,
Copy link
Collaborator

Choose a reason for hiding this comment

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

lgtm. I say we get this in for v1.7.

Let's add a follow up to the update_tools function that reminds us to refactor how it creates clients, so nobody can call it in the future without cleanup.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that's a good point Jordan

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@copilot can you think of a clean refactor where it cleans up after itself?

@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added the lgtm This PR has been approved by a maintainer label Dec 12, 2025
@github-actions github-actions bot added the bug Something isn't working label Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@Adam-Aghili Adam-Aghili added this pull request to the merge queue Dec 12, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to a conflict with the base branch Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 12, 2025
@Adam-Aghili Adam-Aghili added this pull request to the merge queue Dec 12, 2025
Merged via the queue into main with commit d4ada6b Dec 12, 2025
159 of 164 checks passed
@Adam-Aghili Adam-Aghili deleted the fix-mcp-proxy-process-leak branch December 12, 2025 22:34
Adam-Aghili added a commit that referenced this pull request Dec 12, 2025
* fix leak

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* Move MCP client imports out of loop iteration in get_servers (#10993)

* Initial plan

* Move MCPStdioClient and MCPStreamableHttpClient imports to get_servers function

Co-authored-by: phact <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: phact <[email protected]>

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Edwin Jose <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: phact <[email protected]>
Co-authored-by: Adam Aghili <[email protected]>
Adam-Aghili added a commit that referenced this pull request Dec 12, 2025
* fix: mcp-proxy process leak (#10988)

* fix leak

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* Move MCP client imports out of loop iteration in get_servers (#10993)

* Initial plan

* Move MCPStdioClient and MCPStreamableHttpClient imports to get_servers function

Co-authored-by: phact <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: phact <[email protected]>

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Edwin Jose <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: phact <[email protected]>
Co-authored-by: Adam Aghili <[email protected]>

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

---------

Co-authored-by: Sebastián Estévez <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Edwin Jose <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: phact <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants