Skip to content

Commit b551b0c

Browse files
jordanrfrazierautofix-ci[bot]edwinjosechittilappillylucaseduolimfortman11
authored
feat: mcp composer integration (#9506)
* encrypt oauth auth settings at rest * [autofix.ci] apply automated fixes * Fix rebase changes and add env to env server config * Correctly unmask secretstr before encryption * update mcp-composer args * [autofix.ci] apply automated fixes * ruff * ruff * ruff * [autofix.ci] apply automated fixes * ruff * catch invalidtoken error * ruff * [autofix.ci] apply automated fixes * ruff * [autofix.ci] apply automated fixes * ruff * ruff * [autofix.ci] apply automated fixes * ruff * [autofix.ci] apply automated fixes * fix test * Add initial mcp composer service and startup * remove token url * Register server on project creation * WARN: fall back to superuser on no auth params, to allow mcp-composer to connect. also fixes race condition in server creatoin * update sse url args * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Add langflow api keys to the server configs * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * add port searching * [autofix.ci] apply automated fixes * Fix for dead servers - use devnull on subprocess to avoid pipe from filling up * uvlock * [autofix.ci] apply automated fixes * Update composer startup behavior re: auth settings * [autofix.ci] apply automated fixes * fix some auth logic, add dynamic fetch of new url * Clean up sse-url parameters * [autofix.ci] apply automated fixes * Only call composer url when composer is enabled * [autofix.ci] apply automated fixes * improve shutdown * starter projects update * [autofix.ci] apply automated fixes * update logging git push * revert hack to auth mcp composer * [autofix.ci] apply automated fixes * Fix 500 on composer-url query * [autofix.ci] apply automated fixes * Update feature flag; update api key addition to aut-install * [autofix.ci] apply automated fixes * Fix composer url and re-add auth * Changed needs_api_key logic * Refactor use-get-composer-url * remove python fallback for now, then pipe stderr to pipe * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Changed api key logic to allow connection if not api key and auto login is off * fix oauth addition to cmd * restart server when auth values change * Restart server on oauth values changes * [autofix.ci] apply automated fixes * Changed project port to be the same as OAuth port * Changed endpoint to provide port_available * add is_port_available prop * Added port_available to request * Edit mutation to not have linting errors * Added port not available state to authentication * [autofix.ci] apply automated fixes * Added port and host to get composer url * Invalidate project composer url queries * Changed to display port and host that is not running * Cleanup old port finding and some mypy fixes * Add print, remove unused env var * Use mcp-composer directly in client and a lot of fixes * changed starter projects * refactor mcp_projects to use always IP generated for WSL * changed to check args -4 too on installed servers * changed to just check if sse url is in args * added member servers in gitignore * add check for ff * Handle secret request response cycle securely and add better logging * Use asycn logger * Add decorator to check if composer is enabled in settings * more logging changes * Much better handling of existing oauth servers when the flag is disabled on restart * Reset oauth projects to apikey or none when composer flag is disabled * fix url for api key auth * Fix auth check; set project auth to api key when auto login disabled * Ruff, comments, cleanup * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Consolidate the auth handling since its used in two endpoints * [autofix.ci] apply automated fixes * Ruff * [autofix.ci] apply automated fixes * last ruff * Update FE env var naming and dont unnecessarily decrypt auth settings at times * update feature flag usage - remove mcp composer * [autofix.ci] apply automated fixes * Update timeout methods to have more reliable startup * more feature flag changes * Attempt to extract helpful user messages * [autofix.ci] apply automated fixes * Added loading on mcp server tab auth * Changed to load on start too * cleanup mcp composer on project deletion * [autofix.ci] apply automated fixes * remove nested retry mech * Ruff * lint * Fix unit tests * [autofix.ci] apply automated fixes * ruff * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Edwin Jose <[email protected]> Co-authored-by: Lucas Oliveira <[email protected]> Co-authored-by: Mike Fortman <[email protected]>
1 parent 3b7601f commit b551b0c

File tree

22 files changed

+1598
-246
lines changed

22 files changed

+1598
-246
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ LANGFLOW_STORE_ENVIRONMENT_VARIABLES=
104104
# Should enable the MCP composer feature in MCP projects
105105
# Values: true, false
106106
# Default: false
107-
LANGFLOW_FEATURE_MCP_COMPOSER=
107+
LANGFLOW_MCP_COMPOSER_ENABLED=
108108

109109
# STORE_URL
110110
# Example: LANGFLOW_STORE_URL=https://api.langflow.store

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,6 @@ src/frontend/temp
277277

278278
.dspy_cache/
279279
*.db
280-
*.mcp.json
280+
*.mcp.json
281+
282+
member_servers.json
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import Any
2+
3+
from pydantic import SecretStr
4+
5+
from langflow.services.auth.mcp_encryption import decrypt_auth_settings, encrypt_auth_settings
6+
from langflow.services.database.models.folder.model import Folder
7+
8+
9+
def handle_auth_settings_update(
10+
existing_project: Folder,
11+
new_auth_settings: dict | Any | None,
12+
) -> dict[str, bool]:
13+
"""Handle auth settings update including encryption/decryption and MCP Composer logic.
14+
15+
Args:
16+
existing_project: The project being updated (modified in-place)
17+
new_auth_settings: New auth settings (could be dict, Pydantic model, or None)
18+
19+
Returns:
20+
Dict containing:
21+
- should_start_composer: bool
22+
- should_stop_composer: bool
23+
"""
24+
# Get current auth type before update
25+
current_auth_type = None
26+
decrypted_current = None
27+
if existing_project.auth_settings:
28+
current_auth_type = existing_project.auth_settings.get("auth_type")
29+
# Only decrypt if we need access to sensitive fields (for preserving masked values)
30+
if current_auth_type in ["oauth", "apikey"]:
31+
decrypted_current = decrypt_auth_settings(existing_project.auth_settings)
32+
33+
if new_auth_settings is None:
34+
# Explicitly set to None - clear auth settings
35+
existing_project.auth_settings = None
36+
# If we were using OAuth, stop the composer
37+
return {"should_start_composer": False, "should_stop_composer": current_auth_type == "oauth"}
38+
39+
# Handle different input types (dict vs Pydantic model)
40+
if isinstance(new_auth_settings, dict):
41+
auth_dict = new_auth_settings.copy()
42+
else:
43+
# Pydantic model - use python mode to get raw values without SecretStr masking
44+
auth_dict = new_auth_settings.model_dump(mode="python", exclude_none=True)
45+
46+
# Handle SecretStr fields
47+
secret_fields = ["api_key", "oauth_client_secret"]
48+
for field in secret_fields:
49+
field_val = getattr(new_auth_settings, field, None)
50+
if isinstance(field_val, SecretStr):
51+
auth_dict[field] = field_val.get_secret_value()
52+
53+
new_auth_type = auth_dict.get("auth_type")
54+
55+
# Handle masked secret fields from frontend
56+
# If frontend sends back "*******" for a secret field, preserve the existing value
57+
if decrypted_current:
58+
secret_fields = ["oauth_client_secret", "api_key"]
59+
for field in secret_fields:
60+
if field in auth_dict and auth_dict[field] == "*******" and field in decrypted_current:
61+
auth_dict[field] = decrypted_current[field]
62+
63+
# Encrypt and store the auth settings
64+
existing_project.auth_settings = encrypt_auth_settings(auth_dict)
65+
66+
# Determine MCP Composer actions
67+
should_start_composer = new_auth_type == "oauth"
68+
should_stop_composer = current_auth_type == "oauth" and new_auth_type != "oauth"
69+
should_handle_composer = current_auth_type == "oauth" or new_auth_type == "oauth"
70+
71+
return {
72+
"should_start_composer": should_start_composer,
73+
"should_stop_composer": should_stop_composer,
74+
"should_handle_composer": should_handle_composer,
75+
}

0 commit comments

Comments
 (0)