Skip to content

Commit bcaca9f

Browse files
committed
Handle missing config file in MCP client availability detection
1 parent 699e356 commit bcaca9f

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,13 @@ async def check_installed_mcp_servers(
10131013
project_sse_url,
10141014
list(config_data.get("mcpServers", {}).keys()),
10151015
)
1016+
except FileNotFoundError:
1017+
await logger.adebug(
1018+
"%s config file not found at %s (directory exists, app installed but not configured)",
1019+
client_name,
1020+
config_path,
1021+
)
1022+
# available stays True, installed stays False — app is installed but not yet configured
10161023
except json.JSONDecodeError:
10171024
await logger.awarning("Failed to parse %s config JSON at: %s", client_name, config_path)
10181025
# available is True but installed remains False due to parse error

src/backend/tests/unit/api/v1/test_mcp_projects.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,3 +977,73 @@ async def test_mcp_longterm_token_fails_without_superuser():
977977
async with session_scope() as session:
978978
with pytest.raises(HTTPException, match="Auto login required to create a long-term token"):
979979
await create_user_longterm_token(session)
980+
981+
982+
def _prepare_installed_check_env(monkeypatch, tmp_path):
983+
"""Set up environment for check_installed_mcp_servers tests.
984+
985+
Creates per-client config directories under tmp_path so that
986+
``get_config_path`` returns paths whose *parent* directories exist
987+
but whose config *files* may or may not exist.
988+
"""
989+
client_paths = {
990+
"cursor": tmp_path / "cursor" / "mcp.json",
991+
"windsurf": tmp_path / "windsurf" / "mcp_config.json",
992+
"claude": tmp_path / "claude" / "claude_desktop_config.json",
993+
}
994+
# Create parent directories (simulating installed applications)
995+
for path in client_paths.values():
996+
path.parent.mkdir(parents=True, exist_ok=True)
997+
998+
async def fake_get_config_path(client_name):
999+
return client_paths[client_name]
1000+
1001+
monkeypatch.setattr("langflow.api.v1.mcp_projects.get_config_path", fake_get_config_path)
1002+
monkeypatch.setattr("langflow.api.v1.mcp_projects.should_use_mcp_composer", lambda project: False) # noqa: ARG005
1003+
1004+
async def fake_streamable(project_id):
1005+
return f"https://langflow.local/api/v1/mcp/project/{project_id}/streamable"
1006+
1007+
async def fake_sse(project_id):
1008+
return f"https://langflow.local/api/v1/mcp/project/{project_id}/sse"
1009+
1010+
monkeypatch.setattr("langflow.api.v1.mcp_projects.get_project_streamable_http_url", fake_streamable)
1011+
monkeypatch.setattr("langflow.api.v1.mcp_projects.get_project_sse_url", fake_sse)
1012+
1013+
return client_paths
1014+
1015+
1016+
async def test_should_report_available_true_when_app_directory_exists_but_config_file_missing(
1017+
client: AsyncClient,
1018+
user_test_project,
1019+
logged_in_headers,
1020+
tmp_path,
1021+
monkeypatch,
1022+
):
1023+
"""Bug: FileNotFoundError when config file is missing marks client as unavailable.
1024+
1025+
GIVEN: App directories exist (e.g. ~/.cursor/) but config files don't exist yet
1026+
WHEN: GET /mcp/project/{id}/installed is called
1027+
THEN: Each client should have available=True (app is installed) and installed=False (not configured)
1028+
"""
1029+
_prepare_installed_check_env(monkeypatch, tmp_path)
1030+
1031+
response = await client.get(
1032+
f"/api/v1/mcp/project/{user_test_project.id}/installed",
1033+
headers=logged_in_headers,
1034+
)
1035+
1036+
assert response.status_code == 200
1037+
results = response.json()
1038+
1039+
# All three clients should be reported
1040+
assert len(results) == 3
1041+
1042+
for entry in results:
1043+
assert entry["available"] is True, (
1044+
f"{entry['name']} should be available (directory exists) "
1045+
f"even when config file is missing"
1046+
)
1047+
assert entry["installed"] is False, (
1048+
f"{entry['name']} should not be installed (config file doesn't exist)"
1049+
)

0 commit comments

Comments
 (0)