Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/backend/base/langflow/api/v1/mcp_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,9 +629,10 @@ async def install_mcp_config(
raise HTTPException(status_code=500, detail=error_detail) from e

# For OAuth/MCP Composer, use the special format
settings = get_settings_service().settings
command = "uvx"
args = [
"mcp-composer",
f"mcp-composer{settings.mcp_composer_version}",
"--mode",
"stdio",
"--sse-url",
Expand Down
5 changes: 3 additions & 2 deletions src/lfx/src/lfx/services/mcp_composer/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,10 @@ async def _start_project_composer_process(
startup_delay: float = 2.0,
) -> subprocess.Popen:
"""Start the MCP Composer subprocess for a specific project."""
settings = get_settings_service().settings
cmd = [
"uvx",
"mcp-composer",
f"mcp-composer{settings.mcp_composer_version}",
"--mode",
"sse",
"--sse-url",
Expand Down Expand Up @@ -447,7 +448,7 @@ async def _start_project_composer_process(
"oauth_server_url": "OAUTH_SERVER_URL",
"oauth_callback_path": "OAUTH_CALLBACK_PATH",
"oauth_client_id": "OAUTH_CLIENT_ID",
"oauth_client_secret": "OAUTH_CLIENT_SECRET",
"oauth_client_secret": "OAUTH_CLIENT_SECRET", # pragma: allowlist secret
"oauth_auth_url": "OAUTH_AUTH_URL",
"oauth_token_url": "OAUTH_TOKEN_URL",
"oauth_mcp_scope": "OAUTH_MCP_SCOPE",
Expand Down
31 changes: 31 additions & 0 deletions src/lfx/src/lfx/services/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ class Settings(BaseSettings):
# MCP Composer
mcp_composer_enabled: bool = True
"""If set to False, Langflow will not start the MCP Composer service."""
mcp_composer_version: str = "~=0.1.0.7"
"""Version constraint for mcp-composer when using uvx. Uses PEP 440 syntax.
~=0.1.0.7 allows patch updates (0.1.0.x) but prevents minor/major version changes."""

# Public Flow Settings
public_flow_cleanup_interval: int = Field(default=3600, gt=600)
Expand Down Expand Up @@ -348,6 +351,34 @@ def set_user_agent(cls, value):
logger.debug(f"Setting user agent to {value}")
return value

@field_validator("mcp_composer_version", mode="before")
@classmethod
def validate_mcp_composer_version(cls, value):
"""Ensure the version string has a version specifier prefix.

If a bare version like '0.1.0.7' is provided, prepend '~=' to allow patch updates.
Supports PEP 440 specifiers: ==, !=, <=, >=, <, >, ~=, ===
"""
if not value:
return "~=0.1.0.7" # Default

# Check if it already has a version specifier
# Order matters: check longer specifiers first to avoid false matches
specifiers = ["===", "==", "!=", "<=", ">=", "~=", "<", ">"]
if any(value.startswith(spec) for spec in specifiers):
return value

# If it's a bare version number, add ~= prefix
# This regex matches version numbers like 0.1.0.7, 1.2.3, etc.
import re

if re.match(r"^\d+(\.\d+)*", value):
logger.debug(f"Adding ~= prefix to bare version '{value}' -> '~={value}'")
return f"~={value}"

# If we can't determine, return as-is and let uvx handle it
return value

@field_validator("variables_to_get_from_environment", mode="before")
@classmethod
def set_variables_to_get_from_environment(cls, value):
Expand Down
Loading