fix(mcp): scope list config reads to active profile (#5619)

This commit is contained in:
Rod Boev authored and t committed 2026-07-08 16:18:52 +00:00
1 parent 07b9708cde
commit 3c8d9f851e
3 files changed
+32 -10

No files matched your search

+3 -2
View File
@@ -2822,6 +2822,7 @@ from api.config import (
_load_yaml_config_file,
_save_yaml_config_file,
reload_config,
get_config_for_profile_home,
_cfg_lock,
PENDING_BG_TASK_COMPLETIONS,
)
@@ -24181,7 +24182,7 @@ def _mcp_tools_from_registry(server_summaries):
def _handle_mcp_tools_list(handler):
"""List known MCP tools from already-available runtime inventory only."""
cfg = get_config()
cfg = get_config_for_profile_home(get_active_hermes_home())
servers = cfg.get("mcp_servers", {})
if not isinstance(servers, dict):
servers = {}
@@ -24695,7 +24696,7 @@ def _handle_notes_item(handler, parsed):
def _handle_mcp_servers_list(handler):
"""List configured MCP servers with safe, read-only runtime visibility."""
cfg = get_config()
cfg = get_config_for_profile_home(get_active_hermes_home())
servers = cfg.get("mcp_servers", {})
if not isinstance(servers, dict):
servers = {}
+24 -6
View File
@@ -42,17 +42,33 @@ SAMPLE_MCP = {
class TestMcpList:
"""GET /api/mcp/servers — list with masked secrets."""
@patch('api.routes.get_config')
def test_returns_servers_list(self, mock_cfg):
@patch('api.routes.get_config_for_profile_home')
@patch('api.routes.get_active_hermes_home')
def test_returns_servers_list(self, mock_home, mock_cfg):
mock_home.return_value = sentinel_home = object()
mock_cfg.return_value = {'mcp_servers': SAMPLE_MCP}
h = _make_handler()
_handle_mcp_servers_list(h)
assert h.send_response.called
status = h.send_response.call_args[0][0]
assert status == 200
mock_cfg.assert_called_once_with(sentinel_home)
@patch('api.routes.get_config')
def test_empty_config(self, mock_cfg):
@patch('api.routes.get_config_for_profile_home')
@patch('api.routes.get_active_hermes_home')
def test_reads_active_profile_home_for_servers(self, mock_home, mock_cfg):
mock_home.return_value = sentinel_home = object()
mock_cfg.return_value = {'mcp_servers': {'active': SAMPLE_MCP['searxng']}}
h = _make_handler()
_handle_mcp_servers_list(h)
payload = _json_payload(h)
assert [srv['name'] for srv in payload['servers']] == ['active']
mock_cfg.assert_called_once_with(sentinel_home)
@patch('api.routes.get_config_for_profile_home')
@patch('api.routes.get_active_hermes_home')
def test_empty_config(self, mock_home, mock_cfg):
mock_home.return_value = object()
mock_cfg.return_value = {}
h = _make_handler()
_handle_mcp_servers_list(h)
@@ -65,8 +81,10 @@ class TestMcpList:
assert payload['reload_required'] is True
@patch('api.routes._mcp_runtime_status_by_name')
@patch('api.routes.get_config')
def test_list_payload_includes_status_tool_counts_and_safe_invalid_config(self, mock_cfg, mock_runtime):
@patch('api.routes.get_config_for_profile_home')
@patch('api.routes.get_active_hermes_home')
def test_list_payload_includes_status_tool_counts_and_safe_invalid_config(self, mock_home, mock_cfg, mock_runtime):
mock_home.return_value = object()
mock_cfg.return_value = {
'mcp_servers': {
'searxng': {'command': 'mcp-searxng', 'args': ['--port', '8888']},
+5 -2
View File
@@ -29,8 +29,10 @@ def _read(relative_path: str) -> str:
class TestMcpToolInventoryApi:
@patch("api.routes._mcp_runtime_status_by_name")
@patch("api.routes.get_config")
def test_endpoint_returns_sanitized_registered_mcp_tools(self, mock_cfg, mock_runtime):
@patch("api.routes.get_config_for_profile_home")
@patch("api.routes.get_active_hermes_home")
def test_endpoint_returns_sanitized_registered_mcp_tools(self, mock_home, mock_cfg, mock_runtime):
mock_home.return_value = sentinel_home = object()
mock_cfg.return_value = {
"mcp_servers": {
"web-reader": {"url": "http://localhost:3001/mcp", "headers": {"Authorization": "Bearer secret-token"}},
@@ -72,6 +74,7 @@ class TestMcpToolInventoryApi:
{"name": "url", "type": "string", "required": True, "description": "URL to fetch"},
{"name": "limit", "type": "integer", "required": False, "description": "Maximum bytes"},
]
mock_cfg.assert_called_once_with(sentinel_home)
raw = json.dumps(payload)
assert "secret-token" not in raw
assert "default" not in raw