diff --git a/tools/mcp_tool_registration.py b/tools/mcp_tool_registration.py index 2170d8120a..5a16e82201 100644 --- a/tools/mcp_tool_registration.py +++ b/tools/mcp_tool_registration.py @@ -10,6 +10,7 @@ from dataclasses import dataclass from types import SimpleNamespace from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional from tools.mcp_tool_common import _parse_boolish, _core, _resolve_tool_timeout, mcp_field, mcp_server_enabled +from tools import mcp_tool_config as _config from tools import mcp_tool_handlers as _handlers from tools import mcp_tool_schema as _schema from tools.mcp_tool_handlers import ( @@ -461,13 +462,24 @@ def _register_connected_into_current_scope(servers: dict) -> int: if scope is None: return 0 + # Callers that connect a subset (plugin go-live, a connector, orphan re-registration) pass only + # those names. A name they omit is judged against this profile's own config, or connecting one + # server would strip every other server's tools from the profile while their connections live on. + with _core._lock: + omitted = {_key_name(key) for key, scopes in _core._server_tool_scopes.items() + if scope in scopes and _key_name(key) not in servers} + profile_servers = _config._load_mcp_config() if omitted else {} + with _core._lock: stale = [] for key, scopes in _core._server_tool_scopes.items(): if scope not in scopes: continue + name = _key_name(key) + if name not in servers and name not in omitted: + continue # attached after the config read; the next pass judges it server = _core._servers.get(key) - config = servers.get(_key_name(key)) + config = servers[name] if name in servers else profile_servers.get(name) cross_profile = _key_scope(key) != scope if (config is None or not mcp_server_enabled(config) or server is None or getattr(server, "session", None) is None