Skip to content

Commit 4a02802

Browse files
test(sdk): cover project MCP env expansion and loading
Use TestLLM fixtures and add explicit coverage for ${VAR} and default-value expansion in project-level .mcp.json. Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 6d2eecf commit 4a02802

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

tests/sdk/mcp/test_project_mcp_config.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
"""Tests for project-level .mcp.json discovery and merge behavior."""
22

33
import json
4+
import logging
45
from pathlib import Path
56

67
import pytest
7-
from pydantic import SecretStr
88

9-
from openhands.sdk import Agent, LLM
9+
from openhands.sdk import Agent
1010
from openhands.sdk.conversation.impl.local_conversation import LocalConversation
11-
from openhands.sdk.mcp.project_config import find_project_mcp_json, try_load_project_mcp_config
12-
from openhands.sdk.plugin import PluginSource, merge_mcp_configs
11+
from openhands.sdk.mcp.merge import merge_mcp_configs
12+
from openhands.sdk.mcp.project_config import (
13+
_find_project_mcp_json,
14+
load_project_mcp_config,
15+
)
16+
from openhands.sdk.plugin import PluginSource
17+
from openhands.sdk.testing import TestLLM
1318

1419

1520
def _minimal_mcp_file() -> dict:
@@ -26,7 +31,7 @@ def test_find_project_mcp_json_prefers_openhands_dir(tmp_path: Path) -> None:
2631
preferred["mcpServers"]["proj"]["args"] = ["preferred"]
2732
(oh / ".mcp.json").write_text(json.dumps(preferred))
2833

29-
found = find_project_mcp_json(root)
34+
found = _find_project_mcp_json(root)
3035
assert found == oh / ".mcp.json"
3136

3237

@@ -35,7 +40,7 @@ def test_find_project_mcp_json_falls_back_to_root(tmp_path: Path) -> None:
3540
root.mkdir()
3641
(root / ".mcp.json").write_text(json.dumps(_minimal_mcp_file()))
3742

38-
assert find_project_mcp_json(root) == root / ".mcp.json"
43+
assert _find_project_mcp_json(root) == root / ".mcp.json"
3944

4045

4146
def test_merge_mcp_configs_overlay_wins() -> None:
@@ -47,15 +52,37 @@ def test_merge_mcp_configs_overlay_wins() -> None:
4752

4853

4954
@pytest.fixture
50-
def mock_llm():
51-
return LLM(model="test/model", api_key=SecretStr("test-key"))
55+
def mock_llm() -> TestLLM:
56+
return TestLLM.from_messages([])
5257

5358

5459
@pytest.fixture
55-
def basic_agent(mock_llm):
60+
def basic_agent(mock_llm: TestLLM) -> Agent:
5661
return Agent(llm=mock_llm, tools=[])
5762

5863

64+
def test_load_project_mcp_config_expands_env_vars(
65+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
66+
) -> None:
67+
ws = tmp_path / "ws"
68+
ws.mkdir()
69+
monkeypatch.setenv("OH_PROJECT_MCP_TEST", "expanded-cmd")
70+
cfg = {
71+
"mcpServers": {
72+
"s": {
73+
"command": "${OH_PROJECT_MCP_TEST}",
74+
"args": ["${MISSING:-default-arg}"],
75+
}
76+
}
77+
}
78+
(ws / ".mcp.json").write_text(json.dumps(cfg))
79+
80+
loaded = load_project_mcp_config(ws)
81+
assert loaded is not None
82+
assert loaded["mcpServers"]["s"]["command"] == "expanded-cmd"
83+
assert loaded["mcpServers"]["s"]["args"] == ["default-arg"]
84+
85+
5986
def test_trust_project_mcp_merges_under_user_config(
6087
tmp_path: Path, basic_agent: Agent, monkeypatch: pytest.MonkeyPatch
6188
) -> None:
@@ -148,15 +175,13 @@ def test_project_mcp_layer_before_plugin(
148175
conv.close()
149176

150177

151-
def test_try_load_project_mcp_config_invalid_json_logs(
178+
def test_load_project_mcp_config_invalid_json_logs(
152179
tmp_path: Path, caplog: pytest.LogCaptureFixture
153180
) -> None:
154181
ws = tmp_path / "ws"
155182
ws.mkdir()
156183
(ws / ".mcp.json").write_text("not json")
157184

158-
import logging
159-
160185
with caplog.at_level(logging.WARNING):
161-
assert try_load_project_mcp_config(ws) is None
186+
assert load_project_mcp_config(ws) is None
162187
assert "Ignoring invalid project MCP config" in caplog.text

0 commit comments

Comments
 (0)