Fix MCP settings apply NameError

Use the active settings dictionary when scheduling MCP config updates so applying global MCP servers no longer references an undefined config variable. Add a focused regression test covering the settings apply path and document the deferred MCP update contract in settings.py DOX.

Alessandro committed Jun 22, 2026 at 10:25 UTC 6dcaab2f5c58b531d1349b12e332001b55e63136
3 files changed +83 -1
helpers/settings.py
+1 -1
@@ -648,7 +648,7 @@ def _apply_settings(previous: Settings | None, browser_timezone: str | None = No
648 )
649
650 task2 = defer.DeferredTask().start_task(
651 - update_mcp_settings, config.mcp_servers
651 + update_mcp_settings, _settings["mcp_servers"]
652 ) # TODO overkill, replace with background task
653
654 # update token in mcp server
helpers/settings.py.dox.md
+1
@@ -63,6 +63,7 @@
63
64 - Important called helpers/classes observed in the source: `TypeVar`, `files.get_abs_path`, `dotenv.get_dotenv_value`, `opts.insert`, `str.strip`, `_is_valid_timezone`, `str.strip.lower`, `_normalize_timezone_setting`, `SettingsOutput`, `get_default_settings`, `_ensure_option_present`, `_resolve_runtime_timezone`, `get_default_secrets_manager`, `get_settings`, `normalize_settings`, `_load_sensitive_settings`, `settings.copy`, `_write_settings_file`, `reload_settings`, `set_settings`, `initialize_agent`.
65 - Applying settings refreshes active context configs while preserving each subordinate agent's own profile.
66 +- Applying settings starts a deferred `MCPConfig.update(...)` with the current `mcp_servers` string when global MCP server settings change.
67 - Keep request/response, tool, or helper semantics documented here at the same time as source changes.
68
69 ## Work Guidance
tests/test_settings_mcp.py new
+81
@@ -0,0 +1,81 @@
1 +import asyncio
2 +import sys
3 +from pathlib import Path
4 +from types import ModuleType
5 +
6 +PROJECT_ROOT = Path(__file__).resolve().parents[1]
7 +if str(PROJECT_ROOT) not in sys.path:
8 + sys.path.insert(0, str(PROJECT_ROOT))
9 +
10 +import helpers.settings as settings_module
11 +
12 +
13 +def test_apply_settings_updates_mcp_from_current_settings(monkeypatch):
14 + base_settings = settings_module.get_default_settings()
15 + previous_mcp_servers = '{"mcpServers": {}}'
16 + current_mcp_servers = '{"mcpServers": {"deepwiki": {"url": "https://mcp.deepwiki.com/mcp"}}}'
17 + previous = {
18 + **base_settings,
19 + "mcp_servers": previous_mcp_servers,
20 + "mcp_server_token": "unchanged-token",
21 + }
22 + current = {
23 + **base_settings,
24 + "mcp_servers": current_mcp_servers,
25 + "mcp_server_token": "unchanged-token",
26 + }
27 + received_mcp_servers: list[str] = []
28 +
29 + class FakeDeferredTask:
30 + def start_task(self, func, *args, **kwargs):
31 + asyncio.run(func(*args, **kwargs))
32 + return self
33 +
34 + class FakePrintStyle:
35 + def __init__(self, *args, **kwargs):
36 + pass
37 +
38 + def print(self, *args, **kwargs):
39 + pass
40 +
41 + class FakeMCPConfig:
42 + @classmethod
43 + def get_instance(cls):
44 + return cls()
45 +
46 + @classmethod
47 + def update(cls, mcp_servers):
48 + received_mcp_servers.append(mcp_servers)
49 +
50 + def model_dump_json(self):
51 + return "{}"
52 +
53 + agent_stub = ModuleType("agent")
54 + agent_stub.Agent = object
55 +
56 + class FakeAgentContext:
57 + @staticmethod
58 + def all():
59 + return []
60 +
61 + agent_stub.AgentContext = FakeAgentContext
62 +
63 + initialize_stub = ModuleType("initialize")
64 + initialize_stub.initialize_agent = lambda override_settings=None: None
65 +
66 + mcp_handler_stub = ModuleType("helpers.mcp_handler")
67 + mcp_handler_stub.MCPConfig = FakeMCPConfig
68 +
69 + monkeypatch.setitem(sys.modules, "agent", agent_stub)
70 + monkeypatch.setitem(sys.modules, "initialize", initialize_stub)
71 + monkeypatch.setitem(sys.modules, "helpers.mcp_handler", mcp_handler_stub)
72 + monkeypatch.setattr(settings_module, "_settings", current)
73 + monkeypatch.setattr(settings_module, "_apply_timezone_setting", lambda *args, **kwargs: None)
74 + monkeypatch.setattr(settings_module.defer, "DeferredTask", FakeDeferredTask)
75 + monkeypatch.setattr(settings_module, "PrintStyle", FakePrintStyle)
76 + monkeypatch.setattr(settings_module.NotificationManager, "send_notification", lambda **kwargs: None)
77 + monkeypatch.setattr(settings_module, "create_auth_token", lambda: "unchanged-token")
78 +
79 + settings_module._apply_settings(previous)
80 +
81 + assert received_mcp_servers == [current_mcp_servers]