-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Feat: migrate MCP transport from SSE to streamable http #10727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughThis 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
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)
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (4 passed)
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. Comment |
Codecov Report❌ Patch coverage is ❌ 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@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
b580876 to
5ac3de0
Compare
|
@coderabbitai the chat component was not modified, please remove such details |
|
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! |
There was a problem hiding this 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_keyspan multiple branches based onmcp_composer_enabled,auth_settings, andAUTO_LOGIN/SUPERUSERflags. 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 whenlegacySseUrlshould be preferred over streamable HTTPCurrent precedence returns
legacySseUrlwhenever it is present, even ifuseComposeris false and astreamableHttpUrlis 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
legacySseUrlbehinduseComposer, or falling back tostreamableHttpUrlfirst 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 transportThe 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 staleerror_messageusage at call sites
start_project_composernow raises on failure and does not return an error string, butregister_project_with_composerinmcp_projects.pystill does:error_message = await mcp_composer_service.start_project_composer(...) if error_message is not None: raise RuntimeError(error_message)Since
error_messagewill always beNoneon 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_composerexception-based (no return value), and- Dropping the
error_messageassignment/condition inregister_project_with_composer(justawait 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_responsecentralizes:
- Project ownership checks,
- MCP-enabled filtering (
mcp_enabledflag),- Tool metadata construction, and
- Auth settings decryption + masking before building
MCPProjectResponse.
@router.get("/{project_id}"):
- Routes
Accept: text/event-streamrequests into the streamable HTTP path via_dispatch_project_streamable_httpwithverify_project_auth_conditional, and- Returns a
JSONResponsewithmetadata.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 URLsThe changes in
install_mcp_configand 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)andconfig_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 misleadWithin the main try block,
get_project_composer_urlnow:
- Returns
ComposerUrlResponsewithuses_composer=Falseand bothstreamable_http_url/legacy_sse_urlwhen Composer isn’t in use.- Returns
ComposerUrlResponsewithuses_composer=Trueandcomposer_streamable_http_url/composer_sse_urlon success.- On
MCPComposerError, returnsuses_composer=Truewitherror_messagepopulated.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_composerin this outer error path fromshould_use_mcp_composer(project)(when available) or defaulting toFalsewhen 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 logicTwo small cleanups to consider:
- 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_portis likely a string from auth settings;get_url_by_osis typed to takeintand 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)
- 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_urlscontaining 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
globalstatements at lines 89 and 113 trigger Ruff PLW0603 errors that are blocking the pipeline. Additionally,@router.on_eventis 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
📒 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.tssrc/frontend/src/customization/utils/custom-mcp-url.tssrc/frontend/src/controllers/API/queries/mcp/use-get-composer-url.tssrc/frontend/src/controllers/API/queries/mcp/use-patch-flows-mcp.tssrc/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 withawaitfor async operations in component execution methods
Useasyncio.create_task()for background tasks and implement proper cleanup with try/except forasyncio.CancelledError
Usequeue.put_nowait()for non-blocking queue operations andasyncio.wait_for()with timeouts for controlled get operations
Files:
src/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/backend/base/langflow/api/v2/mcp.pysrc/backend/base/langflow/api/v1/schemas.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/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__.pysrc/backend/base/langflow/api/v2/mcp.pysrc/backend/base/langflow/api/v1/schemas.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/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 insrc/backend/tests/directory, component tests insrc/backend/tests/unit/components/organized by component subdirectory, and integration tests accessible viamake integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g.,my_component.py→test_my_component.py)
Use theclientfixture (FastAPI Test Client) defined insrc/backend/tests/conftest.pyfor API tests; it provides an asynchttpx.AsyncClientwith automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with@pytest.mark.noclient
Inherit from the correctComponentTestBasefamily class located insrc/backend/tests/base.pybased on API access needs:ComponentTestBase(no API),ComponentTestBaseWithClient(needs API), orComponentTestBaseWithoutClient(pure logic). Provide three required fixtures:component_class,default_kwargs, andfile_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.asynciodecorator for async component tests and ensure async methods are properly awaited
Test background tasks usingasyncio.create_task()and verify completion withasyncio.wait_for()with appropriate timeout constraints
Test queue operations using non-blockingqueue.put_nowait()andasyncio.wait_for(queue.get(), timeout=...)to verify queue processing without blocking
Use@pytest.mark.no_blockbustermarker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially usinguv run pytest src/backend/tests/unit/test_database.pyr...
Files:
src/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/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.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/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.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/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.tssrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/base/langflow/api/v2/mcp.pysrc/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.tssrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/lfx/src/lfx/services/mcp_composer/service.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/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:
- Why was this version downgraded? Was a compatibility issue discovered?
- 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?
- Will this downgrade cause compatibility issues with the
googleapiclientdependency (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) andlegacy_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_composernow passesstreamable_http_urlinstead of the previoussse_url, correctly reflecting the transport migration.
278-278: LGTM! All function calls consistently updated.Both test cases correctly pass
streamable_http_urlto_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 fromsse_urltostreamable_http_urlis consistent across all test call sites.The test updates correctly reflect the transport migration. All invocations of
_start_project_composer_processand_do_start_project_composernow 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: NewComposerUrlResponseTypealigns with backend schema with additional backwards compatibility.The type correctly mirrors the backend
ComposerUrlResponsemodel. The extrasse_urloptional field provides backwards compatibility for transitional responses. This is properly consumed inuseMcpServer.tswhere it falls back throughstreamable_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
ComposerUrlResponsemodel properly defines the streamable HTTP transport fields with appropriate optional typing. The inclusion oflegacy_sse_urlalongsidestreamable_http_urlenables 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
composerConnectionmemo correctly prioritizesstreamable_http_urlwhile falling back tosse_urlandlegacy_sse_urlfor backwards compatibility. The dependencies are properly specified, and the integration withcustomGetMCPUrl(per the relevant snippet) is correct.src/frontend/src/controllers/API/queries/mcp/use-get-composer-url.ts (1)
1-17: Type consolidation to sharedComposerUrlResponseTypeis a good refactor.Replacing the local interface with the shared type from
@/types/mcpensures 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 forPatchFlowMCPResponse.result.Using
ComposerUrlResponseTypefor 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_urlfunction 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. Trackingservers_updatedandcreated_api_keyseparately allows proper conditional handling.src/backend/base/langflow/api/utils/mcp/__init__.py (1)
3-16: Re-exports for new streamable HTTP helpers look correctImport paths and
__all__entries forget_composer_streamable_http_urlandget_project_streamable_http_urlare consistent withconfig_utils.pyand 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 URLsUsing
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, sovalidate_mcp_server_for_projectcan still detect matching/non‑matching servers via URL inspection. Usingclient.base_urlinstead 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_twoandupdated_config_oneall 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, andget_project_sse_urlnormalizehost/port, avoid0.0.0.0as a connect address, and delegate WSL rewriting toget_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_configsafely decryptsproject.auth_settingsand raises a clearValueErrorwhen missing.get_composer_streamable_http_urlthen validatesoauth_host/oauth_portand reusesget_url_by_osfor 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 lifecyclePatching
StreamableHTTPSessionManagerso that:
run()returns an async context manager, andhandle_requestis anAsyncMock,matches how
ensure_session_manager_runningand_dispatch_project_streamable_httpinteract with the manager.test_handle_project_messages_successassertinghandle_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_creationandtest_init_mcp_serversnow validate:
get_project_mcp_serverpopulatesproject_mcp_serverswithProjectMCPServerinstances, andinit_mcp_serversinitializes 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 compatibilityThe service now:
- Tracks
streamable_http_url/legacy_sse_urlper project,- Derives an
effective_legacy_sse_url = legacy_sse_url or f"{streamable_http_url.rstrip('/')}/sse", and- Starts
mcp-composerwith:"--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 soundKey pieces line up:
ProjectMCPServernow:
- Applies
_configure_server_notification_defaultsso change notifications are advertised by default.- Instantiates a
StreamableHTTPSessionManagerand exposesensure_session_manager_running/stop_session_managerguarded by anAsyncExitStackand lock._dispatch_project_streamable_httpcallsget_project_mcp_server(project_id), awaitsensure_session_manager_running(), injects current user/project/variables into contextvars, then delegates tosession_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 consistentlyBoth
register_project_with_composerandget_or_start_mcp_composernow 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
MethodTypeto overridecreate_initialization_optionsworks, 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
/sseendpoint ensures smooth migration for existing clients.
There was a problem hiding this 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 butupload_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 + raisesrc/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_runningfixture is required to activate the patch but is never referenced in the test bodies. Rename to_mock_ensure_manager_runningin 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._sendrelies 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 withrequest._send.This is the same issue as in
mcp.pyline 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 docstringThe 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_urlvslegacy_sse_urlare expected to be populated and whethererror_messageimplies URLs may beNone. 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 legacysse_urlusageThe type cleanly mirrors the backend
ComposerUrlResponsewhile keepingsse_urloptional for backward compatibility. To avoid confusion later, consider adding a brief comment thatsse_urlis a legacy/compat field and that new backends should preferstreamable_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 onuseComposertooThe
composerConnectionmemo pluscustomGetMCPUrl(projectId, composerConnection)nicely centralize URL selection and give priority tostreamable_http_urlwhile still handling legacysse_url/legacy_sse_url.One nuance:
customGetMCPUrlwill returnlegacySseUrlwhenever it’s set, regardless ofuseComposer, whereas the primary URL path is gated byuseComposer. GivencomposerConnectioncurrently only sourceslegacySseUrlfrom composer metadata, you might optionally tighten this to:
- Either only pass
legacySseUrlwhenuseComposeris true, or- Gate the
legacySseUrlbranch incustomGetMCPUrlonuseComposeras 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 transportSwitching from SSE URLs to
streamable_http_urland"--transport", "streamablehttp"keeps these tests aligned with the new MCP transport and endpoint shape. Building the URL fromclient.base_urlinstead of a hardcoded host is also good for environment independence (and matches prior guidance about avoiding rawlocalhostin 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_urland 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 fallbacksThe
ComposerConnectionOptionsstruct plus the updatedcustomGetMCPUrlgive a clear precedence order: streamable HTTP (when composer is in use), then legacy SSE, then the per-project API URL built fromapi.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
streamableHttpUrlfor 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_keyare difficult to follow. Consider consolidating or adding a comment explaining the logic:
- MCP Composer enabled + project has apikey auth → generate
- MCP Composer disabled + project has apikey auth → generate
- MCP Composer disabled + no project auth + AUTO_LOGIN disabled → generate
- 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_creationbut 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: Validatecomposer_porttype before conversion.Line 265 has a type ignore because
composer_portmay 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 usingAsyncMockdirectly for the async function mock.The patched function
_ensure_streamable_http_manager_runningis async and returnsNone. While the current approach works, usingAsyncMock()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 mocksrc/backend/base/langflow/api/v1/mcp.py (2)
81-103: Consider encapsulating manager state in a dataclass or namespace.Ruff PLW0603 flags the
globalstatements. 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_eventis deprecated in FastAPI; migrate to lifespan context managers for forward compatibility.FastAPI deprecated
on_eventin favor of lifespan event handlers. Router-level events can be migrated to the app-level lifespan context (as already demonstrated inmain.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
📒 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.tssrc/frontend/src/types/mcp/index.tssrc/frontend/src/customization/utils/custom-mcp-url.tssrc/frontend/src/controllers/API/queries/mcp/use-get-composer-url.tssrc/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 withawaitfor async operations in component execution methods
Useasyncio.create_task()for background tasks and implement proper cleanup with try/except forasyncio.CancelledError
Usequeue.put_nowait()for non-blocking queue operations andasyncio.wait_for()with timeouts for controlled get operations
Files:
src/backend/base/langflow/api/v2/mcp.pysrc/backend/tests/unit/api/v1/test_mcp.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/backend/base/langflow/api/v1/mcp.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/base/langflow/api/v1/schemas.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/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.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/backend/base/langflow/api/v1/mcp.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/base/langflow/api/v1/schemas.pysrc/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 insrc/backend/tests/directory, component tests insrc/backend/tests/unit/components/organized by component subdirectory, and integration tests accessible viamake integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g.,my_component.py→test_my_component.py)
Use theclientfixture (FastAPI Test Client) defined insrc/backend/tests/conftest.pyfor API tests; it provides an asynchttpx.AsyncClientwith automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with@pytest.mark.noclient
Inherit from the correctComponentTestBasefamily class located insrc/backend/tests/base.pybased on API access needs:ComponentTestBase(no API),ComponentTestBaseWithClient(needs API), orComponentTestBaseWithoutClient(pure logic). Provide three required fixtures:component_class,default_kwargs, andfile_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.asynciodecorator for async component tests and ensure async methods are properly awaited
Test background tasks usingasyncio.create_task()and verify completion withasyncio.wait_for()with appropriate timeout constraints
Test queue operations using non-blockingqueue.put_nowait()andasyncio.wait_for(queue.get(), timeout=...)to verify queue processing without blocking
Use@pytest.mark.no_blockbustermarker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially usinguv run pytest src/backend/tests/unit/test_database.pyr...
Files:
src/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/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.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/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.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/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.tssrc/backend/base/langflow/api/v2/mcp.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/lfx/tests/unit/services/settings/test_mcp_composer.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.tssrc/lfx/src/lfx/services/mcp_composer/service.pysrc/backend/base/langflow/api/v1/mcp.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/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
0.8.5to0.6.15. This is unusual—version downgrades typically require explicit justification. Please clarify:
- Is this downgrade intentional and necessary for the MCP transport migration?
- Are there any breaking changes or compatibility concerns between these versions?
- Does this change relate to the PR's stated objectives, or is it incidental?
If this downgrade is unintended, consider reverting to
0.8.5or 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 HTTPThe updated
config_one,config_two, andupdated_config_oneusing"--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 ComposerUrlResponseTypeUsing the shared
ComposerUrlResponseTypeforPatchFlowMCPResponse.resultkeeps this hook consistent withuse-get-composer-urland the backendComposerUrlResponsemodel, 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 usefulExporting
get_composer_streamable_http_urlandget_project_streamable_http_urlfrom the MCP utils package keeps the public API aligned with the new transport configuration helpers and mirrors the existing pattern forget_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 newstreamable_http_urlparameterThe Windows-specific MCPComposerService tests now pass
streamable_http_urlvia 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 toComposerUrlResponseTypeis consistent.The type import and usage align with the centralized type definition in
@/types/mcp, which includes the newstreamable_http_urlandlegacy_sse_urlfields alongside the deprecatedsse_urlfor 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_urlandlegacy_sse_urlfields, aligning with the new transport model while maintaining backward compatibility.
230-237: LGTM! Test function calls updated to usestreamable_http_urlparameter.The
_do_start_project_composerinvocations correctly use the newstreamable_http_urlparameter name instead of the deprecatedsse_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_urlfunction correctly:
- Catches
ValueErrorfromurlparse- Handles missing or malformed path segments
- Returns
Noneon any UUID conversion failuresrc/backend/tests/unit/api/v1/test_mcp_projects.py (2)
71-86: LGTM! Mock fixture correctly simulatesStreamableHTTPSessionManager.The fixture properly:
- Patches the class constructor
- Returns an async context manager from
run()- Mocks
handle_requestas an async methodThis aligns with the
ProjectMCPServerimplementation shown in the relevant code snippets.
186-197: LGTM! Test correctly verifies streamable HTTP transport usage.The test properly asserts that
handle_requestis 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_componentshelper follows DRY principles and centralizes the host/port resolution logic. The newget_project_streamable_http_urlcorrectly omits the/ssesuffix 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 streamablehttpflagstreamable_http_url(without/ssesuffix)This completes the transport migration for auto-configured starter projects.
src/lfx/src/lfx/services/mcp_composer/service.py (2)
950-961: API migration tostreamable_http_urlwith backward-compatiblelegacy_sse_urllooks correct.The method signature properly introduces the new
streamable_http_urlparameter while maintaining backward compatibility through the optionallegacy_sse_urlkeyword 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 legacysse_urlalias, ensuring code that still referencessse_urlcontinues to work during migration.src/backend/base/langflow/api/v1/mcp_projects.py (5)
1084-1151: Well-structuredProjectMCPServerclass with proper lifecycle management.The class encapsulates session manager state with proper locking and uses
AsyncExitStackfor clean resource management. The double-checked locking pattern inensure_session_manager_runningis 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 inComposerUrlResponse.The endpoint correctly uses
get_project_streamable_http_urlandget_project_sse_urlhelper 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_urlandlegacy_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-streamAccept 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.
There was a problem hiding this 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_runningfixture 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_errorandtest_mcp_post_endpoint_server_error.Also applies to: 108-110, 120-122
src/backend/base/langflow/api/v1/mcp.py (1)
148-148:request._sendis an undocumented private Starlette attribute.This was flagged in a previous review. The same pattern exists in
mcp_projects.pyline 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:
- If
--headershas fewer than 3 tokens remaining, slicingargs[idx:idx+3]may capture fewer elements than expected.- 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_keyare 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 raiseValueErrorifcomposer_portis 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
globalstatements. 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
📒 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.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/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.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/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.tssrc/frontend/src/types/mcp/index.tssrc/frontend/src/controllers/API/queries/mcp/use-get-composer-url.tssrc/frontend/src/customization/utils/custom-mcp-url.tssrc/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 withawaitfor async operations in component execution methods
Useasyncio.create_task()for background tasks and implement proper cleanup with try/except forasyncio.CancelledError
Usequeue.put_nowait()for non-blocking queue operations andasyncio.wait_for()with timeouts for controlled get operations
Files:
src/backend/base/langflow/api/v2/mcp.pysrc/backend/base/langflow/api/v1/schemas.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/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.pysrc/backend/base/langflow/api/v1/schemas.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/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 insrc/backend/tests/directory, component tests insrc/backend/tests/unit/components/organized by component subdirectory, and integration tests accessible viamake integration_tests
Use same filename as component with appropriate test prefix/suffix (e.g.,my_component.py→test_my_component.py)
Use theclientfixture (FastAPI Test Client) defined insrc/backend/tests/conftest.pyfor API tests; it provides an asynchttpx.AsyncClientwith automatic in-memory SQLite database and mocked environment variables. Skip client creation by marking test with@pytest.mark.noclient
Inherit from the correctComponentTestBasefamily class located insrc/backend/tests/base.pybased on API access needs:ComponentTestBase(no API),ComponentTestBaseWithClient(needs API), orComponentTestBaseWithoutClient(pure logic). Provide three required fixtures:component_class,default_kwargs, andfile_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.asynciodecorator for async component tests and ensure async methods are properly awaited
Test background tasks usingasyncio.create_task()and verify completion withasyncio.wait_for()with appropriate timeout constraints
Test queue operations using non-blockingqueue.put_nowait()andasyncio.wait_for(queue.get(), timeout=...)to verify queue processing without blocking
Use@pytest.mark.no_blockbustermarker to skip the blockbuster plugin in specific tests
For database tests that may fail in batch runs, run them sequentially usinguv run pytest src/backend/tests/unit/test_database.pyr...
Files:
src/backend/tests/unit/api/v1/test_mcp.pysrc/backend/tests/unit/api/v1/test_mcp_projects.pysrc/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.pysrc/frontend/src/pages/MainPage/pages/homePage/hooks/useMcpServer.tssrc/frontend/src/customization/utils/custom-mcp-url.tssrc/backend/base/langflow/api/v2/mcp.pysrc/lfx/tests/unit/services/settings/test_mcp_composer_windows.pysrc/backend/base/langflow/api/utils/mcp/__init__.pysrc/backend/tests/unit/api/utils/test_config_utils.pysrc/backend/base/langflow/api/utils/mcp/config_utils.pysrc/lfx/src/lfx/services/mcp_composer/service.pysrc/backend/base/langflow/api/v1/mcp_projects.pysrc/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
0.8.5to0.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:
- Whether this downgrade is intentional and necessary for the MCP migration.
- Whether this version change aligns with the overall PR scope or if it should be reverted.
- 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_urltostreamable_http_urlwith backward-compatiblelegacy_sse_urlparameter 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/bashFirst, 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/bashFind 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/bashSearch 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/bashSearch 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/bashLook 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/bashSearch 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/bashSearch 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/bashList 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-urlflags are all supported by mcp-composer. The comment accurately reflects that--sse-urlis 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_urlandlegacy_sse_urlkeys, matching the updated service implementation.
232-237: LGTM! Test invocations updated correctly.All test calls to
_do_start_project_composernow usestreamable_http_urlparameter instead ofsse_url, consistent with the updated signature.src/backend/base/langflow/api/v1/schemas.py (1)
463-470: LGTM! Well-structured response model.The
ComposerUrlResponsemodel 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
apiUrldependencies have been changed from[folderName, apiKeyFromStore, ...]to[projectId, composerConnection]. This appears correct sincecustomGetMCPUrlnow takesprojectIdandcomposerConnectionas parameters.However, ensure that changes to
folderNameorapiKeyFromStorethat 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 streamablehttpflag, aligning with the transport migration.
464-471: The transport flag value "streamablehttp" is correct and valid.According to mcp-proxy documentation, the
--transportflag 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
ComposerUrlResponseTypedefinition correctly mirrors the backendComposerUrlResponsemodel 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
ComposerUrlResponsetoComposerUrlResponseType, 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_urlandget_project_streamable_http_urlare properly exported. The imports and__all__list are alphabetically sorted, following the project's conventions.Based on learnings, alphabetically sorted imports in
__init__.pyfiles 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_urltostreamable_http_url, matching the new API signature inMCPComposerService. 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
ComposerConnectionOptionstype 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:
- New streamable HTTP transport is preferred when the composer is active
- Legacy SSE URLs remain functional for backward compatibility
- 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/ssesuffix) is properly designed. It uses the Accept header to determine response type:
- When
Accept: text/event-streamis present, it routes to_dispatch_project_streamable_httpfor streaming- Otherwise, it returns JSON metadata
The frontend correctly passes this URL to mcp-proxy, which handles content-negotiation. The
/sseendpoint (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
Noneappropriately.
402-404: Transport migration logic is correct.Stripping
/ssesuffix and setting--transport streamablehttpcorrectly 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
ComposerUrlResponseTypefrom@/types/mcpensures 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.0bind address conversion tolocalhostand 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
/ssesuffix 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_LOGINis enabled butSUPERUSERis 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 streamablehttpand use the newstreamable_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
StreamableHTTPSessionManagerat the class level, matching howProjectMCPServerinstantiates it. Thehandle_requestmock and async context manager setup align with the production usage.
186-197: LGTM!Test correctly verifies that
handle_requestis invoked on theStreamableHTTPSessionManager, 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_serversinitializesProjectMCPServerinstances 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_serverscontinues 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_defaultsfunction inmcp_projects.py.
161-169: LGTM!The route handlers correctly delegate to the common dispatch function, and the
/ssebackward-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.pyand 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._sendusage is a known concern flagged in a previous review.
467-476: LGTM!The response correctly includes both
streamable_http_urlandlegacy_sse_urlfor client compatibility. Based on learnings, dynamic URL computation ensures compatibility across deployment environments.
1084-1151: LGTM!The
ProjectMCPServerclass 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
ComposerUrlResponsemodel 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
63eb353 to
386fa99
Compare
e6168d8 to
9afc891
Compare
21c123a to
07909c4
Compare
|
@coderabbitai update the walkthrough, it is outdated |
|
@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 performedSummary 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
043e96b to
1c2f734
Compare
There was a problem hiding this 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
StreamableHTTPSessionManagerwith new/streamableendpoints - 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.
|
@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:
Does it make sense to you? |
@jinzishuai, |
66729b0 to
0c709b3
Compare
dac5be9 to
b943b6e
Compare
c5b4e94 to
d458fc2
Compare
664fca4 to
b973b41
Compare
* port #10727 * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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/ DELETESummary by CodeRabbit
New Features
Breaking Changes
✏️ Tip: You can customize this high-level summary in your review settings.