Skip to content

Conversation

@HzaRashid
Copy link
Collaborator

@HzaRashid HzaRashid commented Nov 26, 2025

  • Implements streamable-http transport for v1 and project MCP servers ("/streamable" suffixed endpoints)

  • Keeps legacy SSE endpoints and logic for backwards compatibility

  • Modifies unit and integration tests accordingly

  • Existing MCP projects will continue to be supported through the legacy SSE endpoints. Any newly created project will be configured to use the streamable-http transport by default.

  • The Relevant API Endpoints:

    • /api/v1/mcp/sse HEAD (LEGACY)
    • /api/v1/mcp/sse GET (LEGACY)
    • /api/v1/mcp/ POST (LEGACY)
    • /api/v1/mcp/streamable HEAD
    • /api/v1/mcp/streamable GET
    • /api/v1/mcp/streamable POST
    • /api/v1/mcp/streamable DELETE
    • /api/v1/mcp/streamable/ GET
    • /api/v1/mcp/streamable/ POST
    • /api/v1/mcp/streamable/ DELETE
    • /api/v1/mcp/project/{project_id}/sse HEAD (LEGACY)
    • /api/v1/mcp/project/{project_id}/sse GET (LEGACY)
    • /api/v1/mcp/project/{project_id} POST (LEGACY)
    • /api/v1/mcp/project/{project_id}/ POST (LEGACY)
    • /api/v1/mcp/project/{project_id}/streamable HEAD
    • /api/v1/mcp/project/{project_id}/streamable GET
    • /api/v1/mcp/project/{project_id}/streamable POST
    • /api/v1/mcp/project/{project_id}/streamable DELETE
    • /api/v1/mcp/project/{project_id}/streamable/ GET
    • /api/v1/mcp/project/{project_id}/streamable/ POST
    • /api/v1/mcp/project/{project_id}/streamable/ DELETE

Summary by CodeRabbit

  • New Features

    • Implemented Streamable HTTP transport for MCP connections, providing improved reliability.
    • Added support for multiple connection URL types: streamable HTTP URLs and legacy SSE URLs for backward compatibility.
    • Enhanced session management with per-project lifecycle configuration.
  • Breaking Changes

    • Composer URL responses now return structured data with separate streamable HTTP and legacy SSE URL fields instead of a single URL.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 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.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR replaces the SSE-based MCP transport with Streamable HTTP transport across the backend and frontend. It introduces StreamableHTTPSessionManager for both global and per-project MCP handling, adds URL builders for streamable HTTP endpoints, updates composer configuration logic, and refactors related API schemas and frontend integration points.

Changes

Cohort / File(s) Summary
MCP Transport Replacement
src/backend/base/langflow/api/v1/mcp.py, src/backend/base/langflow/api/v1/mcp_projects.py
Migrated from SSE-based transport to StreamableHTTPSessionManager with per-server lifecycle hooks (startup/shutdown). Added unified dispatch routing (_dispatch_streamable_http) with error handling returning HTTP 500 on transport failures. Introduced DEFAULT_NOTIFICATION_OPTIONS and monkey-patched server initialization.
URL Building Utilities
src/backend/base/langflow/api/utils/mcp/__init__.py, src/backend/base/langflow/api/utils/mcp/config_utils.py
Added get_project_streamable_http_url and get_composer_streamable_http_url helpers. Introduced _get_project_base_url_components for WSL-aware host/port computation. Updated MCP starter auto-configuration to use streamable HTTP URLs with explicit --transport flag.
API Schema & Response Types
src/backend/base/langflow/api/v1/schemas.py
Added ComposerUrlResponse model with fields for project_id, uses_composer, streamable_http_url, legacy_sse_url, and error_message.
MCP Project Configuration
src/backend/base/langflow/api/v1/mcp_projects.py
Refactored URL handling from single SSE URL to ensemble (streamable_http_url, legacy_sse_url). Updated config utilities to operate on URL lists. Added per-project StreamableHTTPSessionManager integration with ensure_session_manager_running and stop_session_manager methods. Renamed config_contains_sse_url to config_contains_server_url and remove_server_by_sse_url to remove_server_by_urls.
MCP Server Configuration
src/backend/base/langflow/api/v2/mcp.py
Added _extract_project_id_from_url and _ensure_mcp_server_config helpers for URL-based project extraction and dynamic API key generation. Updated get_server_list to normalize MCP server configurations with streamable HTTP transport.
Backend Tests
src/backend/tests/unit/api/utils/test_config_utils.py, src/backend/tests/unit/api/v1/test_mcp.py, src/backend/tests/unit/api/v1/test_mcp_projects.py
Replaced SSE URL and transport mocks with StreamableHTTPSessionManager fixtures. Updated test assertions to verify handle_request calls and changed expected status codes from 404 to 500 for transport errors. Updated MCP server instantiation and caching tests.
Frontend API Queries
src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts, src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts
Updated to use ComposerUrlResponseType instead of inline objects. Modified PatchFlowMCPResponse.result to use the new response type.
Frontend Type Definitions
src/frontend/src/types/mcp/index.ts
Added ComposerUrlResponseType export with fields for streamable_http_url, legacy_sse_url, sse_url, project_id, uses_composer, and error_message.
Frontend MCP Utilities
src/frontend/src/customization/utils/custom-mcp-url.ts
Introduced ComposerConnectionOptions type. Updated customGetMCPUrl signature to accept options object with useComposer, streamableHttpUrl, and legacySseUrl. Prioritizes streamableHttpUrl when available.
Frontend MCP Server Hook
src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
Added composerConnection memoized computation from composer URL data. Updated apiUrl calculation to use customGetMCPUrl with composerConnection.
LFX Composer Service
src/lfx/src/lfx/services/mcp_composer/service.py
Replaced sse_url with streamable_http_url throughout. Added legacy_sse_url parameter (defaulting to streamable_http_url.rstrip('/') + "/sse"). Updated MCPComposerService.project_composers storage structure and all startup methods to accept and propagate both URLs.
LFX Tests
src/lfx/tests/unit/services/settings/test_mcp_composer.py, src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
Updated test invocations to use streamable_http_url parameter instead of sse_url. Added legacy_sse_url in test setup.
Configuration
src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json
Updated google dependency version from 0.8.5 to 0.6.15.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MCP API
    participant StreamableHTTPMgr as StreamableHTTPSessionManager
    participant MCP Server

    Note over Client,MCP Server: Old Flow (SSE)
    Client->>MCP API: POST /mcp (SSE)
    MCP API->>MCP Server: handle via SseServerTransport

    Note over Client,MCP Server: New Flow (Streamable HTTP)
    Client->>MCP API: GET/POST/DELETE / (streamable HTTP)
    MCP API->>MCP API: _ensure_streamable_http_manager_running()
    MCP API->>StreamableHTTPMgr: handle_request(request, context)
    StreamableHTTPMgr->>MCP Server: dispatch to server
    MCP Server-->>StreamableHTTPMgr: response
    StreamableHTTPMgr-->>MCP API: response
    MCP API-->>Client: HTTP response (200 or 500 on error)
Loading
sequenceDiagram
    participant App Startup
    participant Router
    participant StreamableHTTPMgr as StreamableHTTPSessionManager
    participant MCP Server

    Note over App Startup,MCP Server: Lifecycle Management
    App Startup->>Router: startup event
    Router->>Router: _start_streamable_http_manager()
    Router->>StreamableHTTPMgr: initialize manager
    StreamableHTTPMgr->>MCP Server: connect
    
    Note over App Startup,MCP Server: Request Handling
    Router->>Router: _ensure_streamable_http_manager_running()
    Router->>StreamableHTTPMgr: handle_request()
    
    Note over App Startup,MCP Server: Shutdown
    App Startup->>Router: shutdown event
    Router->>Router: _stop_streamable_http_manager()
    Router->>StreamableHTTPMgr: cleanup
    StreamableHTTPMgr->>MCP Server: disconnect
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Areas requiring extra attention:

  • src/backend/base/langflow/api/v1/mcp.py: Verify StreamableHTTPSessionManager lifecycle management (startup/shutdown ordering), context propagation for current_user, and error handling for transport failures. Ensure all endpoints properly delegate to _dispatch_streamable_http.
  • src/backend/base/langflow/api/v1/mcp_projects.py: Complex refactoring with per-project session managers, URL ensemble handling (streamable_http_url, legacy_sse_url), and config utilities operating on URL lists. Validate ProjectMCPServer instantiation, session manager coordination, and all call sites passing correct URL sets.
  • src/backend/base/langflow/api/v2/mcp.py: Review _ensure_mcp_server_config logic for dynamic API key generation, project ID extraction from URLs, and config update/persistence flow in get_server_list.
  • src/lfx/src/lfx/services/mcp_composer/service.py: Verify legacy_sse_url computation, parameter threading through startup sequence, and backward-compatibility handling for composer process startup.
  • Frontend integration (custom-mcp-url.ts, useMcpServer.ts): Validate ComposerConnectionOptions destructuring, URL priority logic (streamableHttpUrl vs legacySseUrl), and API hook updates with correct dependency tracking.
  • Test coverage: Ensure new StreamableHTTPSessionManager mocks properly simulate handle_request behavior; verify status code changes (404→500) are appropriate for all error scenarios.

Possibly related PRs

Suggested labels

enhancement, refactor, size:XXL

Suggested reviewers

  • lucaseduoli
  • ogabrielluiz
  • mfortman11
  • jordanrfrazier
  • edwinjosechittilappilly

Pre-merge checks and finishing touches

❌ Failed checks (1 error, 2 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error PR lacks dedicated unit tests for new public functions: get_composer_streamable_http_url(), get_project_streamable_http_url(), config_contains_server_url(), remove_server_by_urls(), ComposerUrlResponseType schema, and useMcpServer hook logic updates. Add unit tests in test_config_utils.py for URL builders, add tests in test_mcp_projects.py for config helpers, add frontend tests in useMcpServer.test.tsx, and consider integration tests for complete streamable HTTP flow.
Test Quality And Coverage ⚠️ Warning Test coverage for new MCP streamable HTTP functionality is incomplete, with critical utility functions entirely untested and only basic smoke tests present. Add unit tests for all utility functions in config_utils.py, implement behavioral tests for StreamableHTTPSessionManager, create parametrized tests for edge cases, and validate full request flow.
Excessive Mock Usage Warning ⚠️ Warning Pull request exhibits excessive mock usage that significantly undermines test quality and coverage, masking actual dispatch routing and error handling logic. Introduce test doubles for StreamableHTTPSessionManager implementing actual request routing, add integration tests, and replace mock assertions with behavior-centric checks validating response content and error propagation.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: migrating MCP transport from SSE to streamable HTTP, which aligns with the comprehensive changes across backend, frontend, and service layers documented in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Test File Naming And Structure ✅ Passed All 5 modified test files follow correct pytest naming and structural patterns with proper test organization and comprehensive testing practices.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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.

@HzaRashid HzaRashid changed the title Feat: migrate MCP SSE transport to streamable http Feat: migrate MCP transport from SSE to streamable http Nov 26, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 26, 2025

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 15%
15.49% (4268/27541) 8.72% (1838/21059) 9.76% (593/6070)

Unit Test Results

Tests Skipped Failures Errors Time
1675 0 💤 0 ❌ 0 🔥 21.019s ⏱️

@codecov
Copy link

codecov bot commented Nov 26, 2025

Codecov Report

❌ Patch coverage is 67.97900% with 122 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.96%. Comparing base (101cbd0) to head (b973b41).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/backend/base/langflow/api/v1/mcp_projects.py 68.78% 59 Missing ⚠️
...ackend/base/langflow/api/utils/mcp/config_utils.py 36.11% 23 Missing ⚠️
src/backend/base/langflow/api/v1/mcp.py 83.56% 12 Missing ⚠️
...frontend/src/customization/utils/custom-mcp-url.ts 0.00% 12 Missing ⚠️
src/backend/base/langflow/api/v1/mcp_utils.py 76.19% 5 Missing ⚠️
...ages/MainPage/pages/homePage/hooks/useMcpServer.ts 50.00% 0 Missing and 5 partials ⚠️
src/backend/base/langflow/main.py 73.33% 4 Missing ⚠️
src/backend/base/langflow/api/v1/projects.py 50.00% 1 Missing ⚠️
...ontrollers/API/queries/mcp/use-get-composer-url.ts 0.00% 1 Missing ⚠️

❌ Your project status has failed because the head coverage (40.00%) 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   #10727      +/-   ##
==========================================
+ Coverage   32.60%   32.96%   +0.36%     
==========================================
  Files        1372     1372              
  Lines       63563    63889     +326     
  Branches     9388     9404      +16     
==========================================
+ Hits        20725    21064     +339     
+ Misses      41795    41780      -15     
- Partials     1043     1045       +2     
Flag Coverage Δ
backend 52.71% <69.67%> (+1.04%) ⬆️
frontend 14.34% <48.57%> (+0.03%) ⬆️
lfx 40.00% <100.00%> (-0.02%) ⬇️

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/v1/schemas.py 96.15% <100.00%> (+0.10%) ⬆️
...controllers/API/queries/mcp/use-patch-flows-mcp.ts 0.00% <ø> (ø)
...ntrollers/API/queries/mcp/use-patch-install-mcp.ts 0.00% <ø> (ø)
...ages/homePage/components/McpAutoInstallContent.tsx 80.00% <100.00%> (ø)
...nPage/pages/homePage/components/McpJsonContent.tsx 84.44% <100.00%> (+1.11%) ⬆️
...ainPage/pages/homePage/components/McpServerTab.tsx 91.80% <100.00%> (+0.27%) ⬆️
...s/MainPage/pages/homePage/utils/mcpServerUtils.tsx 93.44% <100.00%> (+0.33%) ⬆️
src/lfx/src/lfx/services/mcp_composer/service.py 57.68% <100.00%> (+0.12%) ⬆️
src/backend/base/langflow/api/v1/projects.py 29.68% <50.00%> (ø)
...ontrollers/API/queries/mcp/use-get-composer-url.ts 0.00% <0.00%> (ø)
... and 7 more

... and 6 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.

@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from b580876 to 5ac3de0 Compare November 26, 2025 05:13
@HzaRashid
Copy link
Collaborator Author

@coderabbitai the chat component was not modified, please remove such details

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Tip

For best results, initiate chat on the files or code changes.

@HzaRashid: Understood! I acknowledge that the chat component modifications appearing in the starter project JSON files (ChatInput and ChatOutput changes including session_id handling and helper methods) are not part of the intentional changes in this PR.

The core focus of this PR is the migration from MCP SSE transport to streamable HTTP, and the chat component changes in the starter projects appear to be incidental updates or auto-generated content unrelated to the MCP transport migration.

I'll focus my analysis on the actual MCP transport-related changes in the backend API files, frontend controllers, and LFX services. Thank you for the clarification!

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: 3

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

388-400: Complex conditional logic for API key generation could benefit from clarification.

The nested conditions for should_generate_api_key span multiple branches based on mcp_composer_enabled, auth_settings, and AUTO_LOGIN/SUPERUSER flags. Consider extracting this into a separate helper function with clear docstring explaining when API keys are generated:

def _should_generate_api_key(
    project: Folder,
    settings_service: SettingsService,
) -> bool:
    """Determine if an API key should be generated for the MCP server.
    
    API keys are generated when:
    - MCP composer is enabled AND project uses apikey auth, OR
    - MCP composer is disabled AND project has apikey auth, OR
    - MCP composer is disabled AND no project auth AND auto-login is disabled, OR
    - Auto-login is enabled without superuser privileges
    """
    ...
src/frontend/src/customization/utils/custom-mcp-url.ts (1)

3-7: Clarify when legacySseUrl should be preferred over streamable HTTP

Current precedence returns legacySseUrl whenever it is present, even if useComposer is false and a streamableHttpUrl is also available. That can reintroduce SSE in non‑composer flows if callers pass both URLs.

If the intended behavior is “only use SSE when actually going through Composer”, consider gating legacySseUrl behind useComposer, or falling back to streamableHttpUrl first for non‑composer cases, e.g.:

export const customGetMCPUrl = (
  projectId: string,
  options: ComposerConnectionOptions = {},
) => {
  const { useComposer, streamableHttpUrl, legacySseUrl } = options;

  if (useComposer) {
    if (streamableHttpUrl) return streamableHttpUrl;
    if (legacySseUrl) return legacySseUrl;
  }

  if (streamableHttpUrl) return streamableHttpUrl;
  if (legacySseUrl) return legacySseUrl;

  const apiHost = api.defaults.baseURL || window.location.origin;
  return `${apiHost}/api/v1/mcp/project/${projectId}`;
};

Also applies to: 11-25

src/backend/base/langflow/api/utils/mcp/config_utils.py (1)

369-423: Starter-project auto-config now prefers authenticated streamable HTTP transport

The added AUTO_LOGIN/SUPERUSER branch and the move to:

streamable_http_url = await get_project_streamable_http_url(user_starter_folder.id)
args = ["mcp-proxy", "--transport", "streamablehttp", ... , streamable_http_url]

ensure:

  • API key auth is enforced when AUTO_LOGIN is enabled but no SUPERUSER exists, rather than silently falling back.
  • Starter MCP servers use streamable HTTP URLs built from runtime host/port, not hardcoded localhost.

This is a good tightening of auth semantics and URL handling.

src/lfx/src/lfx/services/mcp_composer/service.py (1)

950-1015: Clean up stale error_message usage at call sites

start_project_composer now raises on failure and does not return an error string, but register_project_with_composer in mcp_projects.py still does:

error_message = await mcp_composer_service.start_project_composer(...)
if error_message is not None:
    raise RuntimeError(error_message)

Since error_message will always be None on success and exceptions are already caught and logged in the caller, this check is effectively dead code.

You can simplify both sides by:

  • Keeping start_project_composer exception-based (no return value), and
  • Dropping the error_message assignment/condition in register_project_with_composer (just await start_project_composer(...)).

This removes confusion about the intended error-signaling mechanism without changing runtime behavior.

Also applies to: 1189-1197

src/backend/base/langflow/api/v1/mcp_projects.py (4)

216-295: New list_project_tools flow cleanly separates JSON metadata from streaming transport

_build_project_tools_response centralizes:

  • Project ownership checks,
  • MCP-enabled filtering (mcp_enabled flag),
  • Tool metadata construction, and
  • Auth settings decryption + masking before building MCPProjectResponse.

@router.get("/{project_id}"):

  • Routes Accept: text/event-stream requests into the streamable HTTP path via _dispatch_project_streamable_http with verify_project_auth_conditional, and
  • Returns a JSONResponse with metadata.model_dump(mode="json") for regular clients.

This keeps the HTTP surface backward compatible for JSON callers while letting streaming clients reuse the same URL.

Also applies to: 297-315


571-605: Install/config helpers now consider both streamable HTTP and legacy SSE URLs

The changes in install_mcp_config and the new helpers:

  • Compute:

    if use_mcp_composer:
        composer_streamable_http_url = await get_composer_streamable_http_url(project)
        sse_url = await get_composer_sse_url(project)
        connection_urls = [composer_streamable_http_url, sse_url]
    else:
        streamable_http_url = await get_project_streamable_http_url(project_id)
        legacy_sse_url = await get_project_sse_url(project_id)
        args = ["mcp-proxy", "--transport", "streamablehttp", ... , streamable_http_url]
        connection_urls = [streamable_http_url, legacy_sse_url]
  • Use remove_server_by_urls(config_data, connection_urls) and config_contains_server_url(config_data, [project_streamable_url, project_sse_url]) so both transports are treated as equivalent when detecting/removing existing entries.

That avoids duplicate client entries when migrating from SSE to streamable HTTP and keeps URL detection aligned with the new transport. Logging messages still refer to “SSE URL” but behavior now correctly accounts for both URLs; you may want to update the log text later for clarity.

Also applies to: 641-702, 753-755, 943-951, 1031-1055


783-861: Composer URL endpoint correctly returns structured ComposerUrlResponse but outer error path may mislead

Within the main try block, get_project_composer_url now:

  • Returns ComposerUrlResponse with uses_composer=False and both streamable_http_url/legacy_sse_url when Composer isn’t in use.
  • Returns ComposerUrlResponse with uses_composer=True and composer_streamable_http_url/composer_sse_url on success.
  • On MCPComposerError, returns uses_composer=True with error_message populated.

In the outermost except, however, you always return:

return ComposerUrlResponse(
    project_id=str(project_id),
    uses_composer=True,
    error_message="Failed to get MCP Composer URL. See logs for details.",
)

even when the failure might be unrelated to Composer (e.g., project not found or non‑composer path issues).

Consider deriving uses_composer in this outer error path from should_use_mcp_composer(project) (when available) or defaulting to False when you can’t determine it, to avoid suggesting Composer is in use when it is actually disabled or not applicable.


879-920: Type/behavior nits in URL helpers and remove-by-URL logic

Two small cleanups to consider:

  1. In get_composer_sse_url:
composer_sse_url = f"http://{composer_host}:{composer_port}/sse"
return await get_url_by_os(composer_host, composer_port, composer_sse_url)

composer_port is likely a string from auth settings; get_url_by_os is typed to take int and other call sites cast it. For consistency and clearer types, prefer:

return await get_url_by_os(composer_host, int(composer_port), composer_sse_url)
  1. In remove_server_by_urls, you match by:
if args and any(url == args[-1] or url in args for url in urls):

This is stricter than substring matching (which is good for avoiding false positives) and, combined with connection_urls containing both streamable and SSE URLs, should behave as intended. Just be aware that if clients ever embed URLs inside larger argument strings, they won’t be detected; in that case you might need a more explicit URL parsing step.

Both are non‑blocking but would tighten the implementation.

Also applies to: 1031-1055

src/backend/base/langflow/api/v1/mcp.py (1)

81-124: Refactor to avoid mutable globals and fix pipeline failures.

The global statements at lines 89 and 113 trigger Ruff PLW0603 errors that are blocking the pipeline. Additionally, @router.on_event is deprecated in FastAPI in favor of lifespan context managers.

Consider encapsulating the manager state in a class:

-_streamable_http_manager: StreamableHTTPSessionManager | None = None
-_streamable_http_manager_lock = asyncio.Lock()
-_streamable_http_manager_started = False
-_streamable_http_manager_stack: AsyncExitStack | None = None
-
-
-async def _ensure_streamable_http_manager_running() -> None:
-    """Start the Streamable HTTP session manager if it isn't already running."""
-    global _streamable_http_manager_started, _streamable_http_manager_stack, _streamable_http_manager
-
-    if _streamable_http_manager_started:
-        return
-
-    async with _streamable_http_manager_lock:
-        if _streamable_http_manager_started:
-            return
-
-        # Create a new instance each time we start
-        _streamable_http_manager = StreamableHTTPSessionManager(server)
-        _streamable_http_manager_stack = AsyncExitStack()
-        await _streamable_http_manager_stack.enter_async_context(_streamable_http_manager.run())
-        _streamable_http_manager_started = True
-        await logger.adebug("Streamable HTTP session manager started for global MCP server")
+class _StreamableHTTPManagerState:
+    def __init__(self):
+        self.manager: StreamableHTTPSessionManager | None = None
+        self.lock = asyncio.Lock()
+        self.started = False
+        self.stack: AsyncExitStack | None = None
+
+    async def ensure_running(self) -> None:
+        if self.started:
+            return
+        async with self.lock:
+            if self.started:
+                return
+            self.manager = StreamableHTTPSessionManager(server)
+            self.stack = AsyncExitStack()
+            await self.stack.enter_async_context(self.manager.run())
+            self.started = True
+            await logger.adebug("Streamable HTTP session manager started for global MCP server")
+
+    async def stop(self) -> None:
+        async with self.lock:
+            if not self.started or self.stack is None:
+                return
+            await self.stack.aclose()
+            self.stack = None
+            self.manager = None
+            self.started = False
+            await logger.adebug("Streamable HTTP session manager stopped for global MCP server")
+
+
+_manager_state = _StreamableHTTPManagerState()

Then update references to use _manager_state.manager, _manager_state.ensure_running(), and _manager_state.stop().

📜 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 f2fb7b3 and 63eb353.

📒 Files selected for processing (18)
  • src/backend/base/langflow/api/utils/mcp/__init__.py (1 hunks)
  • src/backend/base/langflow/api/utils/mcp/config_utils.py (6 hunks)
  • src/backend/base/langflow/api/v1/mcp.py (3 hunks)
  • src/backend/base/langflow/api/v1/mcp_projects.py (25 hunks)
  • src/backend/base/langflow/api/v1/schemas.py (1 hunks)
  • src/backend/base/langflow/api/v2/mcp.py (4 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1 hunks)
  • src/backend/tests/unit/api/utils/test_config_utils.py (5 hunks)
  • src/backend/tests/unit/api/v1/test_mcp.py (3 hunks)
  • src/backend/tests/unit/api/v1/test_mcp_projects.py (8 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (2 hunks)
  • src/frontend/src/customization/utils/custom-mcp-url.ts (1 hunks)
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1 hunks)
  • src/frontend/src/types/mcp/index.ts (1 hunks)
  • src/lfx/src/lfx/services/mcp_composer/service.py (9 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py (4 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
src/frontend/src/**/*.{ts,tsx}

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

src/frontend/src/**/*.{ts,tsx}: Use React 18 with TypeScript for frontend development
Use Zustand for state management

Files:

  • src/frontend/src/types/mcp/index.ts
  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts
  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
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/utils/mcp/__init__.py
  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/base/langflow/api/v2/mcp.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/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/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v2/mcp.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/mcp.py
src/backend/tests/**/*.py

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

src/backend/tests/**/*.py: Place backend unit tests in src/backend/tests/ directory, component tests in src/backend/tests/unit/components/ organized by component subdirectory, and integration tests accessible via make integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g., my_component.pytest_my_component.py)
Use the client fixture (FastAPI Test Client) defined in src/backend/tests/conftest.py for API tests; it provides an async httpx.AsyncClient with automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with @pytest.mark.noclient
Inherit from the correct ComponentTestBase family class located in src/backend/tests/base.py based on API access needs: ComponentTestBase (no API), ComponentTestBaseWithClient (needs API), or ComponentTestBaseWithoutClient (pure logic). Provide three required fixtures: component_class, default_kwargs, and file_names_mapping
Create comprehensive unit tests for all new backend components. If unit tests are incomplete, create a corresponding Markdown file documenting manual testing steps and expected outcomes
Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration
Use @pytest.mark.asyncio decorator for async component tests and ensure async methods are properly awaited
Test background tasks using asyncio.create_task() and verify completion with asyncio.wait_for() with appropriate timeout constraints
Test queue operations using non-blocking queue.put_nowait() and asyncio.wait_for(queue.get(), timeout=...) to verify queue processing without blocking
Use @pytest.mark.no_blockbuster marker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially using uv run pytest src/backend/tests/unit/test_database.py r...

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
**/{test_*.py,*.test.ts,*.test.tsx}

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

Check that test files follow the project's naming conventions (test_*.py for backend, *.test.ts for frontend)

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
**/test_*.py

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

**/test_*.py: Backend tests should follow pytest structure with proper test_*.py naming
For async functions, ensure proper async testing patterns are used with pytest for backend

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
src/frontend/src/**/{hooks,services}/**/*.{ts,tsx}

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

Use the useApi hook for API calls with proper error handling, including loading state, error state, and exception throwing

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
src/frontend/src/**/{services,hooks}/**/*.{ts,tsx}

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

Use async/await pattern for API calls instead of .then() chains

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.
📚 Learning: 2025-07-23T21:19:22.567Z
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.

Applied to files:

  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/base/langflow/api/v2/mcp.py
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/lfx/src/lfx/services/mcp_composer/service.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/backend/base/langflow/api/v1/mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `monkeypatch` fixture to mock internal functions for testing error handling scenarios; validate error status codes and error message content in responses

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `pytest.mark.api_key_required` and `pytest.mark.no_blockbuster` markers for components that need external APIs; use `MockLanguageModel` from `tests.unit.mock_language_model` for testing without external API keys

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `aiofiles` and `anyio.Path` for async file operations in tests; create temporary test files using `tmp_path` fixture and verify file existence and content

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use the `client` fixture (FastAPI Test Client) defined in `src/backend/tests/conftest.py` for API tests; it provides an async `httpx.AsyncClient` with automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with `pytest.mark.noclient`

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use async fixtures with proper cleanup using try/finally blocks to ensure resources are properly released after tests complete

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test webhook endpoints by posting to `api/v1/webhook/{endpoint_name}` with appropriate payloads and validating response status codes

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test component build config updates by calling `to_frontend_node()` to get the node template, then calling `update_build_config()` to apply configuration changes

Applied to files:

  • src/backend/tests/unit/api/utils/test_config_utils.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/base/langflow/services/database/models/**/*.py : Database models should be organized by domain (api_key/, flow/, folder/, user/, etc.) under `src/backend/base/langflow/services/database/models/`

Applied to files:

  • src/backend/base/langflow/api/v2/mcp.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/**/*.py : Use FastAPI async patterns with `await` for async operations in component execution methods

Applied to files:

  • src/backend/base/langflow/api/v1/mcp.py
🧬 Code graph analysis (9)
src/backend/base/langflow/api/utils/mcp/__init__.py (1)
src/backend/base/langflow/api/utils/mcp/config_utils.py (4)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
src/backend/tests/unit/api/v1/test_mcp.py (2)
src/backend/tests/unit/api/v1/test_mcp_projects.py (1)
  • mock_streamable_http_manager (71-86)
src/backend/tests/conftest.py (1)
  • logged_in_headers (503-509)
src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (1)
src/frontend/src/types/mcp/index.ts (1)
  • ComposerUrlResponseType (48-55)
src/backend/tests/unit/api/utils/test_config_utils.py (1)
src/backend/tests/unit/api/v2/test_registration.py (1)
  • client (19-22)
src/backend/base/langflow/api/v2/mcp.py (2)
src/backend/base/langflow/services/database/models/folder/model.py (1)
  • Folder (21-36)
src/backend/base/langflow/services/database/models/api_key/crud.py (1)
  • create_api_key (24-40)
src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)
src/frontend/src/customization/utils/custom-mcp-url.ts (1)
  • customGetMCPUrl (9-26)
src/backend/tests/unit/api/v1/test_mcp_projects.py (2)
src/backend/base/langflow/api/v1/mcp_projects.py (2)
  • ProjectMCPServer (1084-1151)
  • get_project_mcp_server (1158-1167)
src/backend/tests/unit/api/v1/test_mcp.py (1)
  • mock_streamable_http_manager (35-39)
src/backend/base/langflow/api/v1/mcp_projects.py (2)
src/backend/base/langflow/api/utils/mcp/config_utils.py (6)
  • auto_configure_starter_projects_mcp (268-453)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
  • _get_mcp_composer_auth_config (241-253)
src/backend/base/langflow/api/v1/schemas.py (2)
  • ComposerUrlResponse (463-470)
  • MCPProjectResponse (456-460)
src/backend/base/langflow/api/utils/mcp/config_utils.py (3)
src/backend/base/langflow/services/auth/mcp_encryption.py (2)
  • decrypt_auth_settings (58-97)
  • encrypt_auth_settings (18-55)
src/backend/base/langflow/api/v1/mcp_projects.py (1)
  • _get_mcp_composer_auth_config (1058-1080)
src/backend/base/langflow/services/database/models/folder/model.py (1)
  • Folder (21-36)
🪛 GitHub Actions: Ruff Style Check
src/backend/base/langflow/api/v1/mcp.py

[error] 89-89: Ruff lint error PLW0603: Using the global statement to update _streamable_http_manager_started is discouraged.

🪛 GitHub Check: Ruff Style Check (3.13)
src/backend/tests/unit/api/v1/test_mcp.py

[failure] 83-83: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:83:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 121-121: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:121:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 109-109: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:109:75: ARG001 Unused function argument: mock_ensure_manager_running

src/backend/base/langflow/api/v1/mcp.py

[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged

⏰ 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). (19)
  • GitHub Check: Validate PR
  • GitHub Check: Test Docker Images / Test docker images
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 3
  • GitHub Check: Run Frontend Tests / Determine Test Suites and Shard Distribution
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 1
  • GitHub Check: Run Backend Tests / LFX Tests - Python 3.10
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 4
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 2
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 5
  • GitHub Check: Lint Backend / Run Mypy (3.10)
  • GitHub Check: Lint Backend / Run Mypy (3.11)
  • GitHub Check: Run Backend Tests / Integration Tests - Python 3.10
  • GitHub Check: Lint Backend / Run Mypy (3.13)
  • GitHub Check: Lint Backend / Run Mypy (3.12)
  • GitHub Check: Run Frontend Unit Tests / Frontend Jest Unit Tests
  • GitHub Check: Test Starter Templates
  • GitHub Check: test-starter-projects
  • GitHub Check: Optimize new Python code in this PR
  • GitHub Check: Update Component Index
🔇 Additional comments (27)
src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1)

1927-1929: Verify the google package version downgrade and its connection to the PR.

The google package dependency has been downgraded from 0.8.5 to 0.6.15 in the SaveToFile component metadata (line 1928). This is a significant backwards compatibility change that requires clarification:

  1. Why was this version downgraded? Was a compatibility issue discovered?
  2. This file modification appears unrelated to the PR objective of migrating MCP transport from SSE to streamable HTTP. Is this change intentional or accidentally included?
  3. Will this downgrade cause compatibility issues with the googleapiclient dependency (version 2.154.0) listed on line 1932?

Can you clarify the rationale for this dependency downgrade and confirm it's intentional for this PR?

src/lfx/tests/unit/services/settings/test_mcp_composer.py (3)

194-195: LGTM! Mock state correctly reflects new URL structure.

The test properly sets up both streamable_http_url (new primary transport) and legacy_sse_url (backwards compatibility) in the mock composer state, aligning with the migration from SSE to streamable HTTP.


232-232: LGTM! Parameter correctly updated to use new transport URL.

The call to _do_start_project_composer now passes streamable_http_url instead of the previous sse_url, correctly reflecting the transport migration.


278-278: LGTM! All function calls consistently updated.

Both test cases correctly pass streamable_http_url to _do_start_project_composer, maintaining consistency across all test scenarios with the new transport mechanism.

Also applies to: 315-315

src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (1)

227-227: Parameter rename from sse_url to streamable_http_url is consistent across all test call sites.

The test updates correctly reflect the transport migration. All invocations of _start_project_composer_process and _do_start_project_composer now use the new parameter name, maintaining consistency with the production code changes.

Also applies to: 318-318, 364-364, 419-419, 514-514

src/frontend/src/types/mcp/index.ts (1)

47-55: New ComposerUrlResponseType aligns with backend schema with additional backwards compatibility.

The type correctly mirrors the backend ComposerUrlResponse model. The extra sse_url optional field provides backwards compatibility for transitional responses. This is properly consumed in useMcpServer.ts where it falls back through streamable_http_url → sse_url → legacy_sse_url.

src/backend/base/langflow/api/v1/schemas.py (1)

463-471: Well-structured response model for MCP Composer URL endpoints.

The ComposerUrlResponse model properly defines the streamable HTTP transport fields with appropriate optional typing. The inclusion of legacy_sse_url alongside streamable_http_url enables graceful migration for clients that may still need SSE fallback.

src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)

110-128: Streamable HTTP URL derivation with graceful fallback is well-implemented.

The composerConnection memo correctly prioritizes streamable_http_url while falling back to sse_url and legacy_sse_url for backwards compatibility. The dependencies are properly specified, and the integration with customGetMCPUrl (per the relevant snippet) is correct.

src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1)

1-17: Type consolidation to shared ComposerUrlResponseType is a good refactor.

Replacing the local interface with the shared type from @/types/mcp ensures consistency across all MCP-related frontend modules. The query implementation remains unchanged, properly handling both success and error responses.

src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (1)

3-7: Consistent type usage for PatchFlowMCPResponse.result.

Using ComposerUrlResponseType for the result field aligns with the shared type definition and ensures type consistency when the mutation response includes composer URL data. The cache invalidation logic correctly triggers a refetch of the composer URL after auth settings change.

Also applies to: 21-24

src/backend/base/langflow/api/v2/mcp.py (3)

312-327: URL parsing helper is robust and handles edge cases.

The _extract_project_id_from_url function safely handles malformed URLs and validates UUID format. The path segment iteration correctly identifies the project ID following the /project/ path component.


402-402: URL suffix removal handles SSE to streamable HTTP migration.

The removesuffix("/sse") correctly strips the SSE endpoint suffix when converting to streamable HTTP URLs. This aligns with the transport migration objective. Based on learnings, using dynamically computed URLs (rather than hardcoded) ensures compatibility across deployment environments.


115-144: Server config normalization loop is correctly structured.

The iteration uses list(mcp_servers.items()) to safely modify the dictionary during iteration. The commit-then-upload order ensures API keys are persisted before the config references them. Tracking servers_updated and created_api_key separately allows proper conditional handling.

src/backend/base/langflow/api/utils/mcp/__init__.py (1)

3-16: Re-exports for new streamable HTTP helpers look correct

Import paths and __all__ entries for get_composer_streamable_http_url and get_project_streamable_http_url are consistent with config_utils.py and keep MCP URL computation centralized instead of hardcoding hosts/ports. Based on learnings, this aligns with the dynamic URL guideline.

src/backend/tests/unit/api/utils/test_config_utils.py (2)

136-140: Validation tests correctly migrated to streamable HTTP URLs

Using streamable_http_url = f"{client.base_url}/api/v1/mcp/project/{...}" and ["mcp-proxy", "--transport", "streamablehttp", streamable_http_url] keeps the project ID embedded in the URL, so validate_mcp_server_for_project can still detect matching/non‑matching servers via URL inspection. Using client.base_url instead of hardcoded localhost also respects the dynamic URL guideline.

Also applies to: 172-179, 212-219


464-471: Multi-user MCP server configs now match new transport semantics

config_one / config_two and updated_config_one all use ["mcp-proxy", "--transport", "streamablehttp", <url>], which matches the new backend expectations and ensures the multi‑tenant isolation tests still exercise the correct transport and arg layout.

Also applies to: 501-504

src/backend/base/langflow/api/utils/mcp/config_utils.py (2)

207-223: URL helpers correctly centralize MCP URL construction and WSL handling

_get_project_base_url_components, get_project_streamable_http_url, and get_project_sse_url normalize host/port, avoid 0.0.0.0 as a connect address, and delegate WSL rewriting to get_url_by_os. This removes hardcoded URLs and keeps OS-specific logic in one place, matching the “dynamic URLs over localhost” guideline.

Also applies to: 225-231, 233-238


241-265: Composer streamable HTTP URL helper aligns with decrypted auth config

_get_mcp_composer_auth_config safely decrypts project.auth_settings and raises a clear ValueError when missing. get_composer_streamable_http_url then validates oauth_host/oauth_port and reuses get_url_by_os for WSL compatibility. This keeps Composer URLs consistent with project URLs and avoids hardcoded hosts.

src/backend/tests/unit/api/v1/test_mcp_projects.py (2)

70-87: Streamable HTTP manager mocking is consistent with new ProjectMCPServer lifecycle

Patching StreamableHTTPSessionManager so that:

  • run() returns an async context manager, and
  • handle_request is an AsyncMock,

matches how ensure_session_manager_running and _dispatch_project_streamable_http interact with the manager. test_handle_project_messages_success asserting handle_request.assert_called_once() cleanly verifies the new transport path without depending on SSE internals.

Also applies to: 186-198


570-617: ProjectMCPServer cache and init tests cover new per-project server semantics

test_project_sse_creation and test_init_mcp_servers now validate:

  • get_project_mcp_server populates project_mcp_servers with ProjectMCPServer instances, and
  • init_mcp_servers initializes entries for all projects.

This ensures the new per-project server abstraction and cache wiring behave as expected before streamable HTTP dispatch is exercised elsewhere.

src/lfx/src/lfx/services/mcp_composer/service.py (1)

74-88: Composer startup flow correctly switched to HTTP + keeps legacy SSE for compatibility

The service now:

  • Tracks streamable_http_url / legacy_sse_url per project,
  • Derives an effective_legacy_sse_url = legacy_sse_url or f"{streamable_http_url.rstrip('/')}/sse", and
  • Starts mcp-composer with:
"--mode", "http",
"--endpoint", streamable_http_url,
"--sse-url", effective_legacy_sse_url,

which matches the new streamable HTTP design while still satisfying tooling that expects --sse-url. The per‑project retry and error‑tracking logic remains intact.

Also applies to: 950-1015, 1189-1197, 1255-1272

src/backend/base/langflow/api/v1/mcp_projects.py (2)

71-75: Per-project MCP server + StreamableHTTPSessionManager wiring looks sound

Key pieces line up:

  • ProjectMCPServer now:
    • Applies _configure_server_notification_defaults so change notifications are advertised by default.
    • Instantiates a StreamableHTTPSessionManager and exposes ensure_session_manager_running / stop_session_manager guarded by an AsyncExitStack and lock.
  • _dispatch_project_streamable_http calls get_project_mcp_server(project_id), awaits ensure_session_manager_running(), injects current user/project/variables into contextvars, then delegates to session_manager.handle_request.

The shutdown hook:

@router.on_event("shutdown")
async def _shutdown_project_session_managers() -> None:
    for server in project_mcp_servers.values():
        await server.stop_session_manager()

ensures managers are torn down cleanly. Overall, the new streamable HTTP transport is integrated without breaking the existing per‑project handler model.

Also applies to: 78-94, 1084-1152, 1170-1177


1196-1205: Composer registration now uses project streamable HTTP + legacy SSE URLs consistently

Both register_project_with_composer and get_or_start_mcp_composer now call:

streamable_http_url = await get_project_streamable_http_url(project.id / project_id)
legacy_sse_url = await get_project_sse_url(project.id / project_id)
await mcp_composer_service.start_project_composer(
    project_id=str(project.id),
    streamable_http_url=streamable_http_url,
    auth_config=auth_config,
    legacy_sse_url=legacy_sse_url,
)

which ensures Composer instances are always pointed at dynamically computed Langflow MCP endpoints with correct OS/WSL handling. This aligns Composer startup with the same URL computation used elsewhere in the MCP stack.

Also applies to: 1310-1337

src/backend/base/langflow/api/v1/mcp.py (4)

34-48: Monkey-patching approach is functional but fragile.

The use of MethodType to override create_initialization_options works, but consider adding a brief comment explaining why this approach is used (e.g., to inject defaults without modifying upstream MCP library). This helps future maintainers understand the intent.


51-79: LGTM!

Server handlers correctly implement async patterns and delegate to shared utility functions.


126-128: LGTM!

Health check endpoint is straightforward.


161-170: LGTM!

The primary endpoint correctly handles GET/POST/DELETE methods, and the backward-compatible /sse endpoint ensures smooth migration for existing clients.

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: 3

♻️ Duplicate comments (4)
src/backend/base/langflow/api/v2/mcp.py (1)

132-142: Transaction handling risk: API key may be orphaned if config upload fails.

This concern was raised in a previous review and remains unaddressed. If session.commit() succeeds at line 135 but upload_server_config() fails (due to storage errors, permission issues, etc.), an API key will exist in the database without being referenced in the MCP server configuration.

Consider wrapping the config upload in a try/except and implementing cleanup if it fails:

     if servers_updated:
         servers["mcpServers"] = mcp_servers
         if created_api_key:
             await session.commit()
-        await upload_server_config(
-            servers,
-            current_user,
-            session,
-            storage_service=storage_service,
-            settings_service=settings_service,
-        )
+        try:
+            await upload_server_config(
+                servers,
+                current_user,
+                session,
+                storage_service=storage_service,
+                settings_service=settings_service,
+            )
+        except Exception:
+            # Consider logging and/or cleanup of orphaned API keys
+            raise
src/backend/tests/unit/api/v1/test_mcp.py (1)

82-91: Prefix unused fixture parameters with underscore to satisfy Ruff ARG001.

The mock_ensure_manager_running fixture is required to activate the patch but is never referenced in the test bodies. Rename to _mock_ensure_manager_running in test signatures to indicate intentional non-use.

 async def test_mcp_post_endpoint(
-    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, mock_ensure_manager_running
+    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, _mock_ensure_manager_running
 ):

 async def test_mcp_post_endpoint_disconnect_error(
-    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, mock_ensure_manager_running
+    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, _mock_ensure_manager_running
 ):

 async def test_mcp_post_endpoint_server_error(
-    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, mock_ensure_manager_running
+    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, _mock_ensure_manager_running
 ):

Also applies to: 108-117, 120-128

src/backend/base/langflow/api/v1/mcp.py (1)

148-148: request._send relies on private Starlette API.

This uses an undocumented internal attribute without stability guarantees. Consider refactoring to a native ASGI handler or pinning Starlette version with documentation.

src/backend/base/langflow/api/v1/mcp_projects.py (1)

337-337: Same private API concern with request._send.

This is the same issue as in mcp.py line 148 - relying on undocumented Starlette internals.

🧹 Nitpick comments (11)
src/backend/base/langflow/api/v1/schemas.py (1)

463-471: ComposerUrlResponse shape looks appropriate; consider clarifying semantics in docstring

The unified response model for streamable/legacy URLs is well-structured and matches how the frontend consumes it. As a minor improvement, you could expand the docstring (or field descriptions) to clarify when streamable_http_url vs legacy_sse_url are expected to be populated and whether error_message implies URLs may be None. This will help future callers reason about the state machine.

src/frontend/src/types/mcp/index.ts (1)

48-55: ComposerUrlResponseType aligns with backend; maybe annotate legacy sse_url usage

The type cleanly mirrors the backend ComposerUrlResponse while keeping sse_url optional for backward compatibility. To avoid confusion later, consider adding a brief comment that sse_url is a legacy/compat field and that new backends should prefer streamable_http_url / legacy_sse_url.

src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)

110-123: URL selection logic correctly prefers streamable HTTP; consider gating legacy URL on useComposer too

The composerConnection memo plus customGetMCPUrl(projectId, composerConnection) nicely centralize URL selection and give priority to streamable_http_url while still handling legacy sse_url / legacy_sse_url.

One nuance: customGetMCPUrl will return legacySseUrl whenever it’s set, regardless of useComposer, whereas the primary URL path is gated by useComposer. Given composerConnection currently only sources legacySseUrl from composer metadata, you might optionally tighten this to:

  • Either only pass legacySseUrl when useComposer is true, or
  • Gate the legacySseUrl branch in customGetMCPUrl on useComposer as well.

That would make it impossible to hit a composer endpoint when the project has composer explicitly disabled, without relying on the backend to clear legacy URLs.

Also applies to: 125-128

src/backend/tests/unit/api/utils/test_config_utils.py (1)

136-140: Validation tests updated correctly for streamable HTTP transport

Switching from SSE URLs to streamable_http_url and "--transport", "streamablehttp" keeps these tests aligned with the new MCP transport and endpoint shape. Building the URL from client.base_url instead of a hardcoded host is also good for environment independence (and matches prior guidance about avoiding raw localhost in MCP configs, based on learnings).

If you want to further reduce duplication, you could add a small helper or a dedicated test around get_project_streamable_http_url and reuse it here, but that’s optional.

Based on learnings, dynamic URL construction here is in line with previous MCP configuration guidance.

Also applies to: 174-179, 214-219

src/frontend/src/customization/utils/custom-mcp-url.ts (1)

3-7: URL helper correctly prioritizes streamable HTTP with sane fallbacks

The ComposerConnectionOptions struct plus the updated customGetMCPUrl give a clear precedence order: streamable HTTP (when composer is in use), then legacy SSE, then the per-project API URL built from api.defaults.baseURL/window.location.origin—which also avoids hardcoded localhost, matching previous MCP URL guidance.

One minor nit: since callers may occasionally pass an SSE endpoint into streamableHttpUrl for compatibility, consider either documenting that this field can hold “primary MCP endpoint (streamable or SSE)” or renaming it to something slightly more transport-agnostic in the future. Not blocking.

Based on learnings, the dynamic host selection here aligns well with prior recommendations against hardcoded localhost MCP URLs.

Also applies to: 11-21, 24-25

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

388-400: Consider simplifying the API key generation condition.

The nested conditionals for should_generate_api_key are difficult to follow. Consider consolidating or adding a comment explaining the logic:

  1. MCP Composer enabled + project has apikey auth → generate
  2. MCP Composer disabled + project has apikey auth → generate
  3. MCP Composer disabled + no project auth + AUTO_LOGIN disabled → generate
  4. AUTO_LOGIN enabled without SUPERUSER → generate
src/backend/tests/unit/api/v1/test_mcp_projects.py (1)

570-596: Consider renaming test function for consistency with transport migration.

The test function is still named test_project_sse_creation but now tests MCP server creation with streamable HTTP transport. Consider renaming for clarity:

-async def test_project_sse_creation(user_test_project):
-    """Test that MCP server is correctly created for a project."""
+async def test_project_mcp_server_creation(user_test_project):
+    """Test that MCP server is correctly created for a project."""
src/backend/base/langflow/api/utils/mcp/config_utils.py (1)

256-265: Validate composer_port type before conversion.

Line 265 has a type ignore because composer_port may be a string or int. Consider explicit conversion and validation earlier:

 async def get_composer_streamable_http_url(project: Folder) -> str:
     """Generate Streamable HTTP URL for the MCP Composer instance."""
     auth_config = await _get_mcp_composer_auth_config(project)
     composer_host = auth_config.get("oauth_host")
     composer_port = auth_config.get("oauth_port")
     if not composer_host or not composer_port:
         error_msg = "OAuth host and port are required to get the MCP Composer URL"
         raise ValueError(error_msg)
+    try:
+        port_int = int(composer_port)
+    except (ValueError, TypeError) as e:
+        error_msg = f"Invalid OAuth port value: {composer_port}"
+        raise ValueError(error_msg) from e
     composer_url = f"http://{composer_host}:{composer_port}"
-    return await get_url_by_os(composer_host, int(composer_port), composer_url)  # type: ignore[arg-type]
+    return await get_url_by_os(composer_host, port_int, composer_url)
src/backend/tests/unit/api/v1/test_mcp.py (1)

42-47: Consider using AsyncMock directly for the async function mock.

The patched function _ensure_streamable_http_manager_running is async and returns None. While the current approach works, using AsyncMock() directly is more idiomatic for mocking async functions.

 @pytest.fixture
 def mock_ensure_manager_running():
     """Mock the _ensure_streamable_http_manager_running function."""
-    with patch("langflow.api.v1.mcp._ensure_streamable_http_manager_running") as mock:
-        mock.return_value = AsyncMock()
+    with patch("langflow.api.v1.mcp._ensure_streamable_http_manager_running", new_callable=AsyncMock) as mock:
         yield mock
src/backend/base/langflow/api/v1/mcp.py (2)

81-103: Consider encapsulating manager state in a dataclass or namespace.

Ruff PLW0603 flags the global statements. While the double-checked locking pattern is correct, encapsulating the state would improve maintainability and silence the linter.

from dataclasses import dataclass, field

@dataclass
class _StreamableHTTPManagerState:
    manager: StreamableHTTPSessionManager | None = None
    lock: asyncio.Lock = field(default_factory=asyncio.Lock)
    started: bool = False
    stack: AsyncExitStack | None = None

_manager_state = _StreamableHTTPManagerState()

async def _ensure_streamable_http_manager_running() -> None:
    if _manager_state.started:
        return
    async with _manager_state.lock:
        if _manager_state.started:
            return
        _manager_state.manager = StreamableHTTPSessionManager(server)
        _manager_state.stack = AsyncExitStack()
        await _manager_state.stack.enter_async_context(_manager_state.manager.run())
        _manager_state.started = True
        await logger.adebug("Streamable HTTP session manager started for global MCP server")

106-123: @router.on_event is deprecated in FastAPI; migrate to lifespan context managers for forward compatibility.

FastAPI deprecated on_event in favor of lifespan event handlers. Router-level events can be migrated to the app-level lifespan context (as already demonstrated in main.py), or refactored into separate lifecycle management. Since your project already uses the lifespan pattern, this refactor aligns with existing practices.

📜 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 f2fb7b3 and 63eb353.

📒 Files selected for processing (18)
  • src/backend/base/langflow/api/utils/mcp/__init__.py (1 hunks)
  • src/backend/base/langflow/api/utils/mcp/config_utils.py (6 hunks)
  • src/backend/base/langflow/api/v1/mcp.py (3 hunks)
  • src/backend/base/langflow/api/v1/mcp_projects.py (25 hunks)
  • src/backend/base/langflow/api/v1/schemas.py (1 hunks)
  • src/backend/base/langflow/api/v2/mcp.py (4 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1 hunks)
  • src/backend/tests/unit/api/utils/test_config_utils.py (5 hunks)
  • src/backend/tests/unit/api/v1/test_mcp.py (3 hunks)
  • src/backend/tests/unit/api/v1/test_mcp_projects.py (8 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (2 hunks)
  • src/frontend/src/customization/utils/custom-mcp-url.ts (1 hunks)
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1 hunks)
  • src/frontend/src/types/mcp/index.ts (1 hunks)
  • src/lfx/src/lfx/services/mcp_composer/service.py (9 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py (4 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
src/frontend/src/**/*.{ts,tsx}

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

src/frontend/src/**/*.{ts,tsx}: Use React 18 with TypeScript for frontend development
Use Zustand for state management

Files:

  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts
  • src/frontend/src/types/mcp/index.ts
  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
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/tests/unit/api/v1/test_mcp.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/base/langflow/api/v1/mcp.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.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
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/mcp.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
src/backend/tests/**/*.py

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

src/backend/tests/**/*.py: Place backend unit tests in src/backend/tests/ directory, component tests in src/backend/tests/unit/components/ organized by component subdirectory, and integration tests accessible via make integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g., my_component.pytest_my_component.py)
Use the client fixture (FastAPI Test Client) defined in src/backend/tests/conftest.py for API tests; it provides an async httpx.AsyncClient with automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with @pytest.mark.noclient
Inherit from the correct ComponentTestBase family class located in src/backend/tests/base.py based on API access needs: ComponentTestBase (no API), ComponentTestBaseWithClient (needs API), or ComponentTestBaseWithoutClient (pure logic). Provide three required fixtures: component_class, default_kwargs, and file_names_mapping
Create comprehensive unit tests for all new backend components. If unit tests are incomplete, create a corresponding Markdown file documenting manual testing steps and expected outcomes
Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration
Use @pytest.mark.asyncio decorator for async component tests and ensure async methods are properly awaited
Test background tasks using asyncio.create_task() and verify completion with asyncio.wait_for() with appropriate timeout constraints
Test queue operations using non-blocking queue.put_nowait() and asyncio.wait_for(queue.get(), timeout=...) to verify queue processing without blocking
Use @pytest.mark.no_blockbuster marker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially using uv run pytest src/backend/tests/unit/test_database.py r...

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
**/{test_*.py,*.test.ts,*.test.tsx}

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

Check that test files follow the project's naming conventions (test_*.py for backend, *.test.ts for frontend)

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
**/test_*.py

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

**/test_*.py: Backend tests should follow pytest structure with proper test_*.py naming
For async functions, ensure proper async testing patterns are used with pytest for backend

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
src/frontend/src/**/{hooks,services}/**/*.{ts,tsx}

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

Use the useApi hook for API calls with proper error handling, including loading state, error state, and exception throwing

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
src/frontend/src/**/{services,hooks}/**/*.{ts,tsx}

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

Use async/await pattern for API calls instead of .then() chains

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.
📚 Learning: 2025-07-23T21:19:22.567Z
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.

Applied to files:

  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/backend/base/langflow/api/v2/mcp.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
  • src/lfx/src/lfx/services/mcp_composer/service.py
  • src/backend/base/langflow/api/v1/mcp.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/base/langflow/services/database/models/**/*.py : Database models should be organized by domain (api_key/, flow/, folder/, user/, etc.) under `src/backend/base/langflow/services/database/models/`

Applied to files:

  • src/backend/base/langflow/api/v2/mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `monkeypatch` fixture to mock internal functions for testing error handling scenarios; validate error status codes and error message content in responses

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `pytest.mark.api_key_required` and `pytest.mark.no_blockbuster` markers for components that need external APIs; use `MockLanguageModel` from `tests.unit.mock_language_model` for testing without external API keys

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `aiofiles` and `anyio.Path` for async file operations in tests; create temporary test files using `tmp_path` fixture and verify file existence and content

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use async fixtures with proper cleanup using try/finally blocks to ensure resources are properly released after tests complete

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test component versioning and backward compatibility using `file_names_mapping` fixture with `VersionComponentMapping` objects mapping component files across Langflow versions

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test webhook endpoints by posting to `api/v1/webhook/{endpoint_name}` with appropriate payloads and validating response status codes

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/**/*.py : Use FastAPI async patterns with `await` for async operations in component execution methods

Applied to files:

  • src/backend/base/langflow/api/v1/mcp.py
🧬 Code graph analysis (8)
src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (1)
src/frontend/src/types/mcp/index.ts (1)
  • ComposerUrlResponseType (48-55)
src/frontend/src/customization/utils/custom-mcp-url.ts (1)
src/frontend/src/controllers/API/api.tsx (1)
  • api (371-371)
src/backend/base/langflow/api/v2/mcp.py (2)
src/backend/base/langflow/services/database/models/folder/model.py (1)
  • Folder (21-36)
src/backend/base/langflow/services/database/models/api_key/crud.py (1)
  • create_api_key (24-40)
src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (2)
src/frontend/src/types/api/index.ts (1)
  • useQueryFunctionType (266-277)
src/frontend/src/types/mcp/index.ts (1)
  • ComposerUrlResponseType (48-55)
src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)
src/frontend/src/customization/utils/custom-mcp-url.ts (1)
  • customGetMCPUrl (9-26)
src/backend/base/langflow/api/utils/mcp/__init__.py (1)
src/backend/base/langflow/api/utils/mcp/config_utils.py (4)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
src/backend/base/langflow/api/v1/mcp_projects.py (2)
src/backend/base/langflow/api/utils/mcp/config_utils.py (5)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
  • _get_mcp_composer_auth_config (241-253)
src/backend/base/langflow/api/v1/schemas.py (1)
  • ComposerUrlResponse (463-470)
src/backend/tests/unit/api/v1/test_mcp_projects.py (2)
src/backend/base/langflow/api/v1/mcp_projects.py (2)
  • ProjectMCPServer (1084-1151)
  • get_project_mcp_server (1158-1167)
src/backend/tests/unit/api/v1/test_mcp.py (1)
  • mock_streamable_http_manager (35-39)
🪛 GitHub Actions: CI
src/backend/base/langflow/api/v1/mcp.py

[error] 148-148: Mypy error: 'None' of 'Any | None' has no attribute 'handle_request' [union-attr].

🪛 GitHub Actions: Ruff Style Check
src/backend/base/langflow/api/v1/mcp.py

[warning] 89-89: ruff PLW0603: Using the global statement to update _streamable_http_manager_started is discouraged.

🪛 GitHub Check: Ruff Style Check (3.13)
src/backend/tests/unit/api/v1/test_mcp.py

[failure] 83-83: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:83:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 121-121: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:121:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 109-109: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:109:75: ARG001 Unused function argument: mock_ensure_manager_running

src/backend/base/langflow/api/v1/mcp.py

[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged

⏰ 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). (1)
  • GitHub Check: Optimize new Python code in this PR
🔇 Additional comments (20)
src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1)

1927-1929: Verify the intentionality of the google dependency version downgrade.

Line 1928 downgrades the google dependency from 0.8.5 to 0.6.15. This is unusual—version downgrades typically require explicit justification. Please clarify:

  1. Is this downgrade intentional and necessary for the MCP transport migration?
  2. Are there any breaking changes or compatibility concerns between these versions?
  3. Does this change relate to the PR's stated objectives, or is it incidental?

If this downgrade is unintended, consider reverting to 0.8.5 or the latest stable version.

src/backend/tests/unit/api/utils/test_config_utils.py (1)

465-471: Multi-user server access tests remain valid under streamable HTTP

The updated config_one, config_two, and updated_config_one using "--transport", "streamablehttp" and distinct URLs keep the conflict/isolation semantics intact while matching the new transport. Assertions still clearly verify per-user separation of configs.

Also applies to: 501-504

src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (1)

3-7: Good consolidation on shared ComposerUrlResponseType

Using the shared ComposerUrlResponseType for PatchFlowMCPResponse.result keeps this hook consistent with use-get-composer-url and the backend ComposerUrlResponse model, reducing the risk of shape drift. No further changes needed here.

Also applies to: 21-24

src/backend/base/langflow/api/utils/mcp/__init__.py (1)

3-9: Re-export of streamable HTTP helpers is consistent and useful

Exporting get_composer_streamable_http_url and get_project_streamable_http_url from the MCP utils package keeps the public API aligned with the new transport configuration helpers and mirrors the existing pattern for get_project_sse_url / get_url_by_os. Looks good.

Also applies to: 11-16

src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (1)

223-231: Tests correctly updated to new streamable_http_url parameter

The Windows-specific MCPComposerService tests now pass streamable_http_url via keyword at all call sites, matching the updated private method signatures while preserving the original behaviors under test (temp-file usage, startup checks, retries, and error handling). No issues spotted here.

Also applies to: 314-322, 360-368, 417-422, 512-519

src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1)

1-35: LGTM! Type migration to ComposerUrlResponseType is consistent.

The type import and usage align with the centralized type definition in @/types/mcp, which includes the new streamable_http_url and legacy_sse_url fields alongside the deprecated sse_url for backward compatibility.

src/lfx/tests/unit/services/settings/test_mcp_composer.py (2)

194-196: LGTM! Test data correctly updated for new transport URL fields.

The project composer dictionary now includes both streamable_http_url and legacy_sse_url fields, aligning with the new transport model while maintaining backward compatibility.


230-237: LGTM! Test function calls updated to use streamable_http_url parameter.

The _do_start_project_composer invocations correctly use the new streamable_http_url parameter name instead of the deprecated sse_url.

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

312-327: LGTM! URL parsing helper is defensive and handles edge cases.

The _extract_project_id_from_url function correctly:

  • Catches ValueError from urlparse
  • Handles missing or malformed path segments
  • Returns None on any UUID conversion failure
src/backend/tests/unit/api/v1/test_mcp_projects.py (2)

71-86: LGTM! Mock fixture correctly simulates StreamableHTTPSessionManager.

The fixture properly:

  • Patches the class constructor
  • Returns an async context manager from run()
  • Mocks handle_request as an async method

This aligns with the ProjectMCPServer implementation shown in the relevant code snippets.


186-197: LGTM! Test correctly verifies streamable HTTP transport usage.

The test properly asserts that handle_request is called on the mock manager, which matches the new transport implementation.

src/backend/base/langflow/api/utils/mcp/config_utils.py (2)

207-230: LGTM! Good refactoring to extract common URL components.

The _get_project_base_url_components helper follows DRY principles and centralizes the host/port resolution logic. The new get_project_streamable_http_url correctly omits the /sse suffix for the streamable HTTP transport. Based on learnings, this approach of dynamically computing URLs is preferred over hardcoded localhost values.


397-423: LGTM! Server config correctly uses streamable HTTP transport.

Both authenticated and unauthenticated paths now include:

  • --transport streamablehttp flag
  • streamable_http_url (without /sse suffix)

This completes the transport migration for auto-configured starter projects.

src/lfx/src/lfx/services/mcp_composer/service.py (2)

950-961: API migration to streamable_http_url with backward-compatible legacy_sse_url looks correct.

The method signature properly introduces the new streamable_http_url parameter while maintaining backward compatibility through the optional legacy_sse_url keyword argument. The default computation on line 1040 correctly derives the SSE URL from the HTTP endpoint.


1189-1197: Composer info structure properly maintains backward compatibility.

The stored info includes both new fields (streamable_http_url, legacy_sse_url) and the legacy sse_url alias, ensuring code that still references sse_url continues to work during migration.

src/backend/base/langflow/api/v1/mcp_projects.py (5)

1084-1151: Well-structured ProjectMCPServer class with proper lifecycle management.

The class encapsulates session manager state with proper locking and uses AsyncExitStack for clean resource management. The double-checked locking pattern in ensure_session_manager_running is correctly implemented.


1170-1178: Shutdown handler correctly handles per-project cleanup with error isolation.

The loop continues even if individual servers fail to stop, ensuring best-effort cleanup. Exception logging provides visibility into failures.


800-823: Proper use of dynamically computed URLs in ComposerUrlResponse.

The endpoint correctly uses get_project_streamable_http_url and get_project_sse_url helper functions to compute URLs dynamically, ensuring compatibility across different deployment environments. Based on learnings, this approach is preferred over hardcoded localhost URLs.


943-951: URL matching utilities handle both positional and embedded URL detection.

The functions correctly check for URLs both as the last argument (typical for mcp-proxy) and anywhere in the args list (for other configurations). The dual-URL support (streamable_http_url and legacy_sse_url) ensures existing installations are properly detected during reinstall.

Also applies to: 1031-1055


297-314: Content negotiation on GET endpoint routes streaming vs metadata requests.

The endpoint now serves dual purposes: SSE/streaming connections (via text/event-stream Accept header) are dispatched to the Streamable HTTP handler, while standard requests return tool metadata as JSON. This maintains backward compatibility while supporting the new transport.

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: 3

♻️ Duplicate comments (3)
src/backend/base/langflow/api/v2/mcp.py (1)

132-142: API key orphaning risk when config upload fails.

This concern was raised in a previous review. The sequence commits the API key (line 135) before calling upload_server_config (lines 136-142). If the upload fails, the API key persists in the database but isn't referenced by any server configuration.

Consider wrapping the upload in try/except to delete the orphaned key on failure.

src/backend/tests/unit/api/v1/test_mcp.py (1)

82-84: Prefix unused fixture parameters with underscore to satisfy Ruff ARG001.

The mock_ensure_manager_running fixture is required to activate the patch but isn't referenced in test bodies.

 async def test_mcp_post_endpoint(
-    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, mock_ensure_manager_running
+    client: AsyncClient, logged_in_headers, mock_streamable_http_manager, _mock_ensure_manager_running
 ):

Apply the same rename in test_mcp_post_endpoint_disconnect_error and test_mcp_post_endpoint_server_error.

Also applies to: 108-110, 120-122

src/backend/base/langflow/api/v1/mcp.py (1)

148-148: request._send is an undocumented private Starlette attribute.

This was flagged in a previous review. The same pattern exists in mcp_projects.py line 337.

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

373-386: Verify arg parsing handles edge cases correctly.

The while-loop parses args by skipping --transport (2 tokens), capturing --headers (3 tokens), and filtering HTTP URLs. However:

  1. If --headers has fewer than 3 tokens remaining, slicing args[idx:idx+3] may capture fewer elements than expected.
  2. If an arg starts with "http" but isn't the URL (e.g., a header value like httpbin), it would be incorrectly skipped.

Consider adding bounds checking and more precise URL detection.

     while idx < len(args):
         arg_item = args[idx]
         if arg_item == "--transport":
+            if idx + 1 >= len(args):
+                idx += 1
+                continue
             idx += 2
             continue
         if arg_item == "--headers":
+            if idx + 2 >= len(args):
+                idx += 1
+                continue
             existing_header_tokens = args[idx : idx + 3]
             idx += 3
             continue
-        if isinstance(arg_item, str) and arg_item.startswith("http"):
+        if isinstance(arg_item, str) and arg_item.startswith("http://") or arg_item.startswith("https://"):
             idx += 1
             continue
         preserved_args.append(arg_item)
         idx += 1

388-400: Simplify API key generation conditions.

The nested conditionals for should_generate_api_key are hard to follow. Consider consolidating:

-    should_generate_api_key = False
-
-    if settings_service.settings.mcp_composer_enabled:
-        if project.auth_settings and project.auth_settings.get("auth_type") == "apikey":
-            should_generate_api_key = True
-    elif project.auth_settings:
-        if project.auth_settings.get("auth_type") == "apikey":
-            should_generate_api_key = True
-    elif not settings_service.auth_settings.AUTO_LOGIN:
-        should_generate_api_key = True
-
-    if settings_service.auth_settings.AUTO_LOGIN and not settings_service.auth_settings.SUPERUSER:
-        should_generate_api_key = True
+    auth_type = (project.auth_settings or {}).get("auth_type")
+    auto_login = settings_service.auth_settings.AUTO_LOGIN
+    is_superuser = settings_service.auth_settings.SUPERUSER
+
+    should_generate_api_key = (
+        auth_type == "apikey"
+        or (not auto_login and not project.auth_settings)
+        or (auto_login and not is_superuser)
+    )
src/backend/base/langflow/api/utils/mcp/config_utils.py (1)

256-265: Validate composer_port type before conversion.

The int(composer_port) conversion on line 265 could raise ValueError if composer_port is not a valid numeric string. Consider validating or handling this case.

 async def get_composer_streamable_http_url(project: Folder) -> str:
     """Generate Streamable HTTP URL for the MCP Composer instance."""
     auth_config = await _get_mcp_composer_auth_config(project)
     composer_host = auth_config.get("oauth_host")
     composer_port = auth_config.get("oauth_port")
     if not composer_host or not composer_port:
         error_msg = "OAuth host and port are required to get the MCP Composer URL"
         raise ValueError(error_msg)
+    try:
+        port_int = int(composer_port)
+    except (ValueError, TypeError) as e:
+        error_msg = f"Invalid OAuth port value: {composer_port}"
+        raise ValueError(error_msg) from e
     composer_url = f"http://{composer_host}:{composer_port}"
-    return await get_url_by_os(composer_host, int(composer_port), composer_url)  # type: ignore[arg-type]
+    return await get_url_by_os(composer_host, port_int, composer_url)
src/backend/base/langflow/api/v1/mcp.py (1)

81-103: Consider refactoring global state to a class-based pattern.

Ruff PLW0603 flags the use of global statements. While the double-checked locking pattern is correctly implemented, encapsulating this state in a singleton class would improve testability and satisfy the linter.

Example pattern:

class _StreamableHTTPManagerState:
    def __init__(self):
        self.manager: StreamableHTTPSessionManager | None = None
        self.lock = asyncio.Lock()
        self.started = False
        self.stack: AsyncExitStack | None = None

_state = _StreamableHTTPManagerState()
📜 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 f2fb7b3 and 63eb353.

📒 Files selected for processing (18)
  • src/backend/base/langflow/api/utils/mcp/__init__.py (1 hunks)
  • src/backend/base/langflow/api/utils/mcp/config_utils.py (6 hunks)
  • src/backend/base/langflow/api/v1/mcp.py (3 hunks)
  • src/backend/base/langflow/api/v1/mcp_projects.py (25 hunks)
  • src/backend/base/langflow/api/v1/schemas.py (1 hunks)
  • src/backend/base/langflow/api/v2/mcp.py (4 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1 hunks)
  • src/backend/tests/unit/api/utils/test_config_utils.py (5 hunks)
  • src/backend/tests/unit/api/v1/test_mcp.py (3 hunks)
  • src/backend/tests/unit/api/v1/test_mcp_projects.py (8 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1 hunks)
  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (2 hunks)
  • src/frontend/src/customization/utils/custom-mcp-url.ts (1 hunks)
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1 hunks)
  • src/frontend/src/types/mcp/index.ts (1 hunks)
  • src/lfx/src/lfx/services/mcp_composer/service.py (9 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer.py (4 hunks)
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/{test_*.py,*.test.ts,*.test.tsx}

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

Check that test files follow the project's naming conventions (test_*.py for backend, *.test.ts for frontend)

Files:

  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
**/test_*.py

📄 CodeRabbit inference engine (coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt)

**/test_*.py: Backend tests should follow pytest structure with proper test_*.py naming
For async functions, ensure proper async testing patterns are used with pytest for backend

Files:

  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
src/frontend/src/**/*.{ts,tsx}

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

src/frontend/src/**/*.{ts,tsx}: Use React 18 with TypeScript for frontend development
Use Zustand for state management

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
  • src/frontend/src/types/mcp/index.ts
  • src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts
  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts
src/frontend/src/**/{hooks,services}/**/*.{ts,tsx}

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

Use the useApi hook for API calls with proper error handling, including loading state, error state, and exception throwing

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
src/frontend/src/**/{services,hooks}/**/*.{ts,tsx}

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

Use async/await pattern for API calls instead of .then() chains

Files:

  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
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/v1/schemas.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/base/langflow/api/v1/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
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/base/langflow/api/v1/mcp.py
src/backend/tests/**/*.py

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

src/backend/tests/**/*.py: Place backend unit tests in src/backend/tests/ directory, component tests in src/backend/tests/unit/components/ organized by component subdirectory, and integration tests accessible via make integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g., my_component.pytest_my_component.py)
Use the client fixture (FastAPI Test Client) defined in src/backend/tests/conftest.py for API tests; it provides an async httpx.AsyncClient with automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with @pytest.mark.noclient
Inherit from the correct ComponentTestBase family class located in src/backend/tests/base.py based on API access needs: ComponentTestBase (no API), ComponentTestBaseWithClient (needs API), or ComponentTestBaseWithoutClient (pure logic). Provide three required fixtures: component_class, default_kwargs, and file_names_mapping
Create comprehensive unit tests for all new backend components. If unit tests are incomplete, create a corresponding Markdown file documenting manual testing steps and expected outcomes
Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration
Use @pytest.mark.asyncio decorator for async component tests and ensure async methods are properly awaited
Test background tasks using asyncio.create_task() and verify completion with asyncio.wait_for() with appropriate timeout constraints
Test queue operations using non-blocking queue.put_nowait() and asyncio.wait_for(queue.get(), timeout=...) to verify queue processing without blocking
Use @pytest.mark.no_blockbuster marker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially using uv run pytest src/backend/tests/unit/test_database.py r...

Files:

  • src/backend/tests/unit/api/v1/test_mcp.py
  • src/backend/tests/unit/api/v1/test_mcp_projects.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
🧠 Learnings (14)
📓 Common learnings
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.
📚 Learning: 2025-07-23T21:19:22.567Z
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.

Applied to files:

  • src/lfx/tests/unit/services/settings/test_mcp_composer.py
  • src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts
  • src/frontend/src/customization/utils/custom-mcp-url.ts
  • src/backend/base/langflow/api/v2/mcp.py
  • src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/tests/unit/api/utils/test_config_utils.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/lfx/src/lfx/services/mcp_composer/service.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
  • src/backend/base/langflow/api/v1/mcp.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/base/langflow/services/database/models/**/*.py : Database models should be organized by domain (api_key/, flow/, folder/, user/, etc.) under `src/backend/base/langflow/services/database/models/`

Applied to files:

  • src/backend/base/langflow/api/v2/mcp.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/base/langflow/components/**/__init__.py : Update `__init__.py` with alphabetically sorted imports when adding new components

Applied to files:

  • src/backend/base/langflow/api/utils/mcp/__init__.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `monkeypatch` fixture to mock internal functions for testing error handling scenarios; validate error status codes and error message content in responses

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `pytest.mark.api_key_required` and `pytest.mark.no_blockbuster` markers for components that need external APIs; use `MockLanguageModel` from `tests.unit.mock_language_model` for testing without external API keys

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use `aiofiles` and `anyio.Path` for async file operations in tests; create temporary test files using `tmp_path` fixture and verify file existence and content

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Each test should have a clear docstring explaining its purpose; complex test setups should be commented; mock usage should be documented; expected behaviors should be explicitly stated

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use async fixtures with proper cleanup using try/finally blocks to ensure resources are properly released after tests complete

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Use the `client` fixture (FastAPI Test Client) defined in `src/backend/tests/conftest.py` for API tests; it provides an async `httpx.AsyncClient` with automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with `pytest.mark.noclient`

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Create comprehensive unit tests for all new backend components. If unit tests are incomplete, create a corresponding Markdown file documenting manual testing steps and expected outcomes

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:47:28.965Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.965Z
Learning: Applies to src/backend/tests/**/*.py : Test webhook endpoints by posting to `api/v1/webhook/{endpoint_name}` with appropriate payloads and validating response status codes

Applied to files:

  • src/backend/tests/unit/api/v1/test_mcp.py
📚 Learning: 2025-11-24T19:46:09.074Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.074Z
Learning: Applies to src/backend/**/*.py : Use FastAPI async patterns with `await` for async operations in component execution methods

Applied to files:

  • src/backend/base/langflow/api/v1/mcp.py
🧬 Code graph analysis (10)
src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)
src/frontend/src/customization/utils/custom-mcp-url.ts (1)
  • customGetMCPUrl (9-26)
src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (3)
src/frontend/src/types/api/index.ts (1)
  • useQueryFunctionType (266-277)
src/frontend/src/types/mcp/index.ts (1)
  • ComposerUrlResponseType (48-55)
src/frontend/src/controllers/API/services/request-processor.ts (1)
  • UseRequestProcessor (14-54)
src/frontend/src/customization/utils/custom-mcp-url.ts (1)
src/frontend/src/controllers/API/api.tsx (1)
  • api (371-371)
src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (1)
src/frontend/src/types/mcp/index.ts (1)
  • ComposerUrlResponseType (48-55)
src/backend/base/langflow/api/utils/mcp/__init__.py (1)
src/backend/base/langflow/api/utils/mcp/config_utils.py (4)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
src/backend/tests/unit/api/v1/test_mcp.py (1)
src/backend/tests/unit/api/v1/test_mcp_projects.py (1)
  • mock_streamable_http_manager (71-86)
src/backend/tests/unit/api/v1/test_mcp_projects.py (2)
src/backend/base/langflow/api/v1/mcp_projects.py (2)
  • ProjectMCPServer (1084-1151)
  • get_project_mcp_server (1158-1167)
src/backend/tests/unit/api/v1/test_mcp.py (1)
  • mock_streamable_http_manager (35-39)
src/backend/tests/unit/api/utils/test_config_utils.py (1)
src/backend/tests/unit/api/v2/test_registration.py (1)
  • client (19-22)
src/backend/base/langflow/api/utils/mcp/config_utils.py (2)
src/backend/base/langflow/services/auth/mcp_encryption.py (2)
  • decrypt_auth_settings (58-97)
  • encrypt_auth_settings (18-55)
src/backend/base/langflow/api/v1/mcp_projects.py (1)
  • _get_mcp_composer_auth_config (1058-1080)
src/backend/base/langflow/api/v1/mcp_projects.py (5)
src/backend/base/langflow/api/utils/mcp/config_utils.py (6)
  • auto_configure_starter_projects_mcp (268-453)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
  • _get_mcp_composer_auth_config (241-253)
src/backend/base/langflow/api/v1/schemas.py (2)
  • ComposerUrlResponse (463-470)
  • MCPProjectResponse (456-460)
src/backend/base/langflow/api/v1/mcp.py (1)
  • im_alive (127-128)
src/lfx/src/lfx/services/mcp_composer/service.py (3)
  • clear_last_error (97-99)
  • get_last_error (89-91)
  • start_project_composer (951-1013)
src/backend/base/langflow/__main__.py (1)
  • run (185-417)
🪛 GitHub Actions: CI
src/backend/base/langflow/api/v1/mcp.py

[error] 148-148: mypy: Item "None" of "Any | None" has no attribute "handle_request" [union-attr]. Command: uv run mypy --namespace-packages -p "langflow"

🪛 GitHub Actions: Ruff Style Check
src/backend/base/langflow/api/v1/mcp.py

[error] 89-89: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged. (ruff check with --output-format=github)

🪛 GitHub Check: Ruff Style Check (3.13)
src/backend/tests/unit/api/v1/test_mcp.py

[failure] 83-83: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:83:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 121-121: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:121:75: ARG001 Unused function argument: mock_ensure_manager_running


[failure] 109-109: Ruff (ARG001)
src/backend/tests/unit/api/v1/test_mcp.py:109:75: ARG001 Unused function argument: mock_ensure_manager_running

src/backend/base/langflow/api/v1/mcp.py

[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 113-113: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:113:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:78: PLW0603 Using the global statement to update _streamable_http_manager is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:46: PLW0603 Using the global statement to update _streamable_http_manager_stack is discouraged


[failure] 89-89: Ruff (PLW0603)
src/backend/base/langflow/api/v1/mcp.py:89:12: PLW0603 Using the global statement to update _streamable_http_manager_started is discouraged

🔇 Additional comments (42)
src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1)

1927-1929: Verify the Google library version downgrade and confirm it aligns with PR objectives.

Line 1928 shows the google dependency version downgraded from 0.8.5 to 0.6.15. This change appears unrelated to the PR's stated focus on MCP transport migration (SSE to Streamable HTTP), and the PR comments already noted that incidental changes to starter project JSON files should be avoided.

Please confirm:

  1. Whether this downgrade is intentional and necessary for the MCP migration.
  2. Whether this version change aligns with the overall PR scope or if it should be reverted.
  3. Any compatibility concerns or breaking changes introduced by downgrading the Google library.
src/lfx/src/lfx/services/mcp_composer/service.py (3)

954-961: LGTM! Parameter migration to streamable HTTP is well-structured.

The signature change from sse_url to streamable_http_url with backward-compatible legacy_sse_url parameter aligns with the PR objectives. The keyword-only parameter syntax ensures clarity at call sites.


1040-1040: I'll help you verify the default legacy SSE URL derivation pattern. Let me start by examining the code context and searching for SSE endpoint definitions.
<function_calls>
<function_calls>


#!/bin/bash

First, let's examine the context around line 1040

cat -n src/lfx/src/lfx/services/mcp_composer/service.py | sed -n '1030,1050p'


</function_calls>

Let me search for file structure to confirm the exact path:
<function_calls>


#!/bin/bash

Find the service.py file

find . -name "service.py" -path "/mcp_composer/" -type f


</function_calls>

Now let me search for SSE endpoint definitions and configurations:
<function_calls>


#!/bin/bash

Search for SSE endpoint definitions and routes

rg -n --type py -C3 'sse|SSE' --max-count=50 | head -100


</function_calls>

Let me also check for environment-specific configurations:
<function_calls>


#!/bin/bash

Search for route decorators and endpoint definitions related to sse

rg -n --type py -C3 '@.*route|@.*get|@.*post' | grep -i sse | head -50


</function_calls>

Let me examine the specific file and context more carefully:
<function_calls>


#!/bin/bash

Look for Python files with mcp_composer in path

find . -path "mcp_composer" -name "*.py" -type f


</function_calls>

Let me check for the structure more broadly:
<function_calls>


#!/bin/bash

Search for legacy_sse_url references

rg -n "legacy_sse_url" --type py -C5


</function_calls>

Let me also check if there are any environment configs or documentation about the endpoints:
<function_calls>


#!/bin/bash

Search for streamable_http_url and related URL configurations

rg -n "streamable_http_url" --type py -C3


</function_calls>

Let me verify the actual repository structure:
<function_calls>


#!/bin/bash

List files at root to understand repo structure

ls -la


</function_calls>

<capability_instructions>

I need to gather evidence about the SSE endpoint pattern to verify the concern raised in the review. Let me request the shell capability to execute these scripts.

</capability_instructions>


1256-1271: Based on my verification through web searches and codebase examination, I can now provide the rewritten review comment:

Verified: Command line argument construction for HTTP mode is correctly implemented.

The --mode http, --endpoint, and --sse-url flags are all supported by mcp-composer. The comment accurately reflects that --sse-url is retained for backward compatibility with composer tooling even in HTTP mode, which is documented in the package's public examples.

src/lfx/tests/unit/services/settings/test_mcp_composer.py (2)

194-195: LGTM! Test data updated consistently with service changes.

The test fixture now uses streamable_http_url and legacy_sse_url keys, matching the updated service implementation.


232-237: LGTM! Test invocations updated correctly.

All test calls to _do_start_project_composer now use streamable_http_url parameter instead of sse_url, consistent with the updated signature.

src/backend/base/langflow/api/v1/schemas.py (1)

463-470: LGTM! Well-structured response model.

The ComposerUrlResponse model clearly defines the composer URL response structure with appropriate optional fields for both streamable HTTP and legacy SSE URLs. The docstring and field types are clear.

src/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.ts (1)

125-128: Verify the removal of dependencies from apiUrl useMemo.

The apiUrl dependencies have been changed from [folderName, apiKeyFromStore, ...] to [projectId, composerConnection]. This appears correct since customGetMCPUrl now takes projectId and composerConnection as parameters.

However, ensure that changes to folderName or apiKeyFromStore that should trigger URL recomputation are properly handled elsewhere in the component.

src/backend/tests/unit/api/utils/test_config_utils.py (2)

136-140: LGTM! Test updated to use streamable HTTP transport.

The test configuration now uses the streamable HTTP URL with the --transport streamablehttp flag, aligning with the transport migration.


464-471: The transport flag value "streamablehttp" is correct and valid.

According to mcp-proxy documentation, the --transport flag accepts either "sse" or "streamablehttp". The test configurations correctly use "streamablehttp", which is an explicitly supported transport type. No changes are needed.

src/frontend/src/types/mcp/index.ts (1)

48-55: LGTM! Frontend type matches backend schema.

The ComposerUrlResponseType definition correctly mirrors the backend ComposerUrlResponse model fields, ensuring type safety across the API boundary.

src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1)

2-2: LGTM! Type references updated consistently.

All type references have been updated from ComposerUrlResponse to ComposerUrlResponseType, maintaining consistency with the new frontend type definitions.

Also applies to: 13-13, 17-17

src/backend/base/langflow/api/utils/mcp/__init__.py (1)

3-17: LGTM! Public API exports updated correctly.

The new URL utility functions get_composer_streamable_http_url and get_project_streamable_http_url are properly exported. The imports and __all__ list are alphabetically sorted, following the project's conventions.

Based on learnings, alphabetically sorted imports in __init__.py files are a project convention, which has been followed here.

src/lfx/tests/unit/services/settings/test_mcp_composer_windows.py (1)

227-227: Parameter renames align with the streamable HTTP migration.

The test call sites consistently update sse_url to streamable_http_url, matching the new API signature in MCPComposerService. The test logic and assertions remain unchanged, validating the same behavior with the renamed parameter.

Also applies to: 318-318, 364-364, 419-419, 514-514

src/frontend/src/customization/utils/custom-mcp-url.ts (3)

3-7: Clean type definition for connection options.

The ComposerConnectionOptions type provides a clear, extensible interface for the MCP URL configuration. Using an options object pattern improves maintainability over multiple positional parameters.


15-21: URL priority logic correctly implements the transport migration.

The precedence order (streamableHttpUrl → legacySseUrl → fallback) ensures:

  1. New streamable HTTP transport is preferred when the composer is active
  2. Legacy SSE URLs remain functional for backward compatibility
  3. Dynamic fallback for direct project access

23-25: Based on my verification of the codebase, I can now provide the rewritten review comment:


The endpoint correctly handles streamable HTTP connections via content-negotiation.

The backend endpoint at GET /api/v1/mcp/project/{project_id} (without /sse suffix) is properly designed. It uses the Accept header to determine response type:

  • When Accept: text/event-stream is present, it routes to _dispatch_project_streamable_http for streaming
  • Otherwise, it returns JSON metadata

The frontend correctly passes this URL to mcp-proxy, which handles content-negotiation. The /sse endpoint (line 351) remains available for legacy compatibility.

src/backend/base/langflow/api/v2/mcp.py (2)

312-327: Robust URL parsing for project ID extraction.

The helper correctly handles malformed URLs with try/except, searches for the "project" path segment, and validates UUID format. Edge cases (invalid UUID, missing path parts) return None appropriately.


402-404: Transport migration logic is correct.

Stripping /sse suffix and setting --transport streamablehttp correctly converts legacy SSE configs to the new transport format.

src/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.ts (2)

3-7: Type imports align with the centralized MCP type definitions.

Importing ComposerUrlResponseType from @/types/mcp ensures consistent typing across MCP-related queries and avoids inline type duplication.


21-24: Response type correctly references the shared ComposerUrlResponseType.

Using the centralized type ensures the response shape stays synchronized with the backend schema changes (streamable_http_url, legacy_sse_url).

src/backend/base/langflow/api/utils/mcp/config_utils.py (4)

207-222: Clean extraction of base URL components.

The helper correctly handles 0.0.0.0 bind address conversion to localhost and falls back through runtime_port → configured port → default 7860. This follows the learning about dynamically computed URLs.


225-230: Streamable HTTP URL builder follows the new transport model.

The function correctly builds the URL without the /sse suffix and applies WSL IP handling. Based on learnings, this dynamic URL computation ensures deployment compatibility.


374-388: New condition for AUTO_LOGIN without SUPERUSER.

The added condition correctly handles the case where AUTO_LOGIN is enabled but SUPERUSER is false, defaulting to API key authentication for starter folders. The logging provides useful debug context.


400-423: Server config args correctly use streamable HTTP transport.

Both the API key and no-auth paths now include --transport streamablehttp and use the new streamable_http_url. This is consistent with the transport migration.

src/backend/tests/unit/api/v1/test_mcp.py (2)

34-47: LGTM!

The fixtures correctly mock the new Streamable HTTP transport components and align with the production code patterns in mcp.py.


94-99: LGTM!

The status code change from 400 to 403 Forbidden correctly reflects authentication requirements via CurrentActiveMCPUser.

src/backend/tests/unit/api/v1/test_mcp_projects.py (5)

70-86: LGTM!

The fixture correctly mocks StreamableHTTPSessionManager at the class level, matching how ProjectMCPServer instantiates it. The handle_request mock and async context manager setup align with the production usage.


186-197: LGTM!

Test correctly verifies that handle_request is invoked on the StreamableHTTPSessionManager, matching the new streamable HTTP transport dispatch.


570-595: LGTM!

The test properly validates MCP server creation, caching behavior, and instance type. The asyncio.sleep(0) correctly yields control for async test compliance.


598-619: LGTM!

Test correctly verifies that init_mcp_servers initializes ProjectMCPServer instances for all projects and stores them in the cache.


622-640: LGTM!

The error handling test correctly simulates a failure during MCP server creation for the first project and verifies that init_mcp_servers continues processing subsequent projects.

src/backend/base/langflow/api/v1/mcp.py (2)

27-48: LGTM!

The default notification options and monkey-patching approach correctly ensure MCP servers advertise change notifications by default. This pattern aligns with the _configure_server_notification_defaults function in mcp_projects.py.


161-169: LGTM!

The route handlers correctly delegate to the common dispatch function, and the /sse backward-compatibility endpoint ensures existing clients continue to work during the migration.

src/backend/base/langflow/api/v1/mcp_projects.py (8)

71-93: LGTM!

The notification defaults configuration mirrors the pattern in mcp.py and correctly ensures per-project MCP servers advertise change notifications.


297-314: LGTM!

The endpoint correctly implements content negotiation, delegating to streamable HTTP dispatch for event-stream requests while returning JSON for standard requests.


322-348: LGTM!

The dispatch handler correctly manages per-project MCP server lifecycle and context propagation. The pattern aligns with the global MCP handler in mcp.py.

Note: request._send usage is a known concern flagged in a previous review.


467-476: LGTM!

The response correctly includes both streamable_http_url and legacy_sse_url for client compatibility. Based on learnings, dynamic URL computation ensures compatibility across deployment environments.


1084-1151: LGTM!

The ProjectMCPServer class cleanly encapsulates per-project MCP server lifecycle management with proper async locking and context-aware handler registration.


1170-1177: LGTM!

The shutdown hook ensures graceful cleanup of all per-project session managers, with proper exception handling to prevent one failure from blocking others.


943-951: LGTM!

The utility correctly checks multiple URLs against server configurations, supporting both streamable HTTP and legacy SSE URL matching.


783-860: LGTM!

The endpoint properly returns ComposerUrlResponse model instances for all code paths, ensuring consistent API response typing.

fix tests

refactor mcp and mcp_projects

backwards compat with SSE transport

provide streamable http option for json mcp config

remove streamable_http mgmt and update tests
@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from 63eb353 to 386fa99 Compare November 27, 2025 04:02
@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from 21c123a to 07909c4 Compare November 28, 2025 04:23
@HzaRashid
Copy link
Collaborator Author

@coderabbitai update the walkthrough, it is outdated

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

@HzaRashid: I'll regenerate the walkthrough to ensure it accurately reflects the MCP transport migration from SSE to streamable HTTP, excluding any incidental chat component changes.

✅ Actions performed

Summary regeneration triggered.

fix tests

refactor mcp and mcp_projects

backwards compat with SSE transport

provide streamable http option for json mcp config

remove streamable_http mgmt and update tests
@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from 043e96b to 1c2f734 Compare December 4, 2025 23:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a migration from Server-Sent Events (SSE) to Streamable HTTP transport for the Model Context Protocol (MCP) connections in Langflow, while maintaining backwards compatibility with existing SSE endpoints. The migration affects both the global v1 MCP server and project-specific MCP servers, introducing new /streamable suffixed endpoints alongside legacy /sse endpoints.

Key Changes:

  • Adds Streamable HTTP transport using StreamableHTTPSessionManager with new /streamable endpoints
  • Maintains legacy SSE transport endpoints for backwards compatibility
  • Updates MCP Composer to use Streamable HTTP by default while keeping SSE fallback support
  • Introduces transport selection UI in the frontend (SSE vs Streamable HTTP)

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
src/backend/base/langflow/api/v1/mcp.py Adds Streamable HTTP transport implementation with new StreamableHTTP class and /streamable endpoints
src/backend/base/langflow/api/v1/mcp_projects.py Implements project-specific Streamable HTTP endpoints, session manager lifecycle, and task group coordination
src/backend/base/langflow/api/v1/projects.py Updates project creation to use Streamable HTTP URLs by default in MCP server configurations
src/backend/base/langflow/api/v1/schemas.py Adds ComposerUrlResponse schema with both streamable HTTP and legacy SSE URL fields
src/backend/base/langflow/api/utils/mcp/config_utils.py Adds URL generation helpers for both transport types and updates auto-configuration logic
src/backend/base/langflow/main.py Integrates Streamable HTTP manager lifecycle into application startup and shutdown
src/lfx/src/lfx/services/mcp_composer/service.py Updates MCP Composer service to accept and use Streamable HTTP URLs
src/frontend/src/types/mcp/index.ts Adds TypeScript types for transport selection and composer URL responses
src/frontend/src/pages/MainPage/pages/homePage/components/McpJsonContent.tsx Adds transport selection UI with SSE and Streamable HTTP tabs
src/frontend/src/customization/utils/custom-mcp-url.ts Updates URL generation logic to support both transport types
Test files Comprehensive updates to unit and integration tests for both transport types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jinzishuai
Copy link

@HzaRashid I am testing your branch and found a problem. When you upload a file using the File component and run the flow, the file doesn't appear in MCP resources because of a storage location mismatch:

The Problem:

  • File component uploads → Stored as user_id/filename
  • MCP resource discovery → Only looks for files under flow_id/filename

Does it make sense to you?

@HzaRashid
Copy link
Collaborator Author

HzaRashid commented Dec 6, 2025

@HzaRashid I am testing your branch and found a problem. When you upload a file using the File component and run the flow, the file doesn't appear in MCP resources because of a storage location mismatch:

The Problem:

  • File component uploads → Stored as user_id/filename
  • MCP resource discovery → Only looks for files under flow_id/filename

Does it make sense to you?

@jinzishuai,
Absolutely, this is a known architectural issue that is unrelated to this PR. I have a solution and might create a separate PR.

@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from dac5be9 to b943b6e Compare December 8, 2025 18:17
@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from c5b4e94 to d458fc2 Compare December 8, 2025 18:42
@HzaRashid HzaRashid force-pushed the feat/http-stream-mcp branch from 664fca4 to b973b41 Compare December 8, 2025 19:16
HzaRashid added a commit that referenced this pull request Dec 8, 2025
@HzaRashid HzaRashid added this pull request to the merge queue Dec 8, 2025
HzaRashid added a commit that referenced this pull request Dec 8, 2025
* port #10727

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Merged via the queue into main with commit b0bae86 Dec 8, 2025
90 of 95 checks passed
@HzaRashid HzaRashid deleted the feat/http-stream-mcp branch December 8, 2025 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants