fix(tools): enable platform-native toolsets when their composite is explicitly configured (#35527)

When a user explicitly configures a platform with its native composite
(e.g. platform_toolsets.discord: [hermes-discord]), the discord and
discord_admin toolsets were silently stripped by _DEFAULT_OFF_TOOLSETS
even though the composite contains those tools. The strip could not tell
an explicit composite opt-in apart from the unconfigured default.

Track whether the platform was explicitly configured and, when it was,
exempt toolsets that are both default-off and platform-restricted to the
current platform from the strip. Only discord/discord_admin are affected
(the sole entries in both _DEFAULT_OFF_TOOLSETS and
_TOOLSET_PLATFORM_RESTRICTIONS). Unconfigured and empty-list platforms
keep the security default-off behaviour.
This commit is contained in:
Tranquil-Flow 2026-07-02 00:11:33 +02:00 committed by Teknium
parent b062083d0a
commit 8a726e91ba
2 changed files with 107 additions and 0 deletions

View file

@ -1612,6 +1612,28 @@ def enabled_mcp_server_names(config: dict) -> Set[str]:
}
def _exempt_explicit_platform_native(
default_off: Set[str], platform: str, *, explicitly_configured: bool
) -> None:
"""Let platform-native default-off toolsets through on explicit config.
Toolsets that are both in ``_DEFAULT_OFF_TOOLSETS`` and restricted to
``platform`` via ``_TOOLSET_PLATFORM_RESTRICTIONS`` (currently
``discord``/``discord_admin`` on the discord platform) are the platform's
own native tools. They are kept off for *unconfigured* platforms (security
opt-in), but once a user explicitly saves a toolset list for the platform
the composite they chose (e.g. ``hermes-discord``, which contains those
tools) is an opt-in stripping them silently defeats the explicit
configuration (#35527). Mutates ``default_off`` in place.
"""
if not explicitly_configured:
return
for ts in list(default_off):
allowed = _TOOLSET_PLATFORM_RESTRICTIONS.get(ts)
if allowed is not None and platform in allowed:
default_off.discard(ts)
def _get_platform_tools(
config: dict,
platform: str,
@ -1623,6 +1645,11 @@ def _get_platform_tools(
platform_toolsets = config.get("platform_toolsets") or {}
toolset_names = platform_toolsets.get(platform)
# Track whether the user explicitly saved a toolset list for this platform
# (vs. falling back to the platform default). An explicit composite (e.g.
# ``hermes-discord``) is an opt-in to the platform's native default-off
# toolsets — see _exempt_explicit_platform_native (#35527).
explicitly_configured = isinstance(toolset_names, list)
if toolset_names is None or not isinstance(toolset_names, list):
plat_info = PLATFORMS.get(platform)
@ -1686,6 +1713,9 @@ def _get_platform_tools(
default_off.remove(platform)
if "homeassistant" in default_off and os.getenv("HASS_TOKEN"):
default_off.remove("homeassistant")
_exempt_explicit_platform_native(
default_off, platform, explicitly_configured=explicitly_configured
)
expanded -= default_off
enabled_toolsets |= expanded
@ -1748,6 +1778,9 @@ def _get_platform_tools(
# strip the entry we just added.
if x_search_auto_enabled and "x_search" in default_off:
default_off.remove("x_search")
_exempt_explicit_platform_native(
default_off, platform, explicitly_configured=explicitly_configured
)
enabled_toolsets -= default_off
# Recover non-configurable platform toolsets (e.g. discord, feishu_doc,

View file

@ -243,6 +243,80 @@ def test_get_platform_tools_x_search_auto_enabled_when_xai_oauth_present(monkeyp
assert "x_search" in enabled, f"x_search missing for {plat}"
# ─── #35527: platform-restricted default-off toolsets (discord/discord_admin)
# are stripped by _DEFAULT_OFF_TOOLSETS even when the user explicitly opts in
# via the platform's native composite. The composite ``hermes-discord``
# contains both ``discord`` and ``discord_admin`` tools, so configuring it is
# an explicit opt-in that should survive the default-off strip. ───────────────
def test_discord_composite_only_enables_discord_toolsets():
"""Layer 1: ``platform_toolsets.discord: [hermes-discord]`` is an explicit
opt-in to the full Discord bundle (which includes the ``discord`` and
``discord_admin`` tools). They must not be silently stripped."""
config = {"platform_toolsets": {"discord": ["hermes-discord"]}}
enabled = _get_platform_tools(config, "discord")
assert "discord" in enabled, "discord toolset missing from hermes-discord composite"
assert "discord_admin" in enabled, "discord_admin toolset missing from composite"
def test_discord_composite_plus_configurable_enables_discord_toolsets():
"""Layer 2: mixing the composite with a configurable key (e.g. spotify)
still opts into the Discord toolsets carried by the composite."""
config = {"platform_toolsets": {"discord": ["hermes-discord", "spotify"]}}
enabled = _get_platform_tools(config, "discord")
assert "discord" in enabled
assert "discord_admin" in enabled
def test_discord_composite_plus_partial_explicit_enables_sibling():
"""Layer 3: ``[hermes-discord, discord]`` lists discord explicitly but
discord_admin arrives only via the composite. Both must survive."""
config = {"platform_toolsets": {"discord": ["hermes-discord", "discord"]}}
enabled = _get_platform_tools(config, "discord")
assert "discord" in enabled
assert "discord_admin" in enabled
def test_discord_unconfigured_keeps_discord_toolsets_off():
"""Layer 4 (guard): an unconfigured discord platform keeps the platform
toolsets OFF by default explicit configuration is required to opt in."""
enabled = _get_platform_tools({}, "discord")
assert "discord" not in enabled
assert "discord_admin" not in enabled
def test_discord_empty_list_keeps_discord_toolsets_off():
"""Layer 4 (guard): an explicit empty list means 'nothing' — the Discord
toolsets must not be auto-added even though the fix keys off explicit
configuration."""
config = {"platform_toolsets": {"discord": []}}
enabled = _get_platform_tools(config, "discord")
assert "discord" not in enabled
assert "discord_admin" not in enabled
def test_discord_toolsets_do_not_leak_to_other_platforms():
"""Layer 4 (guard): discord/discord_admin are platform-restricted — they
must never appear on a non-discord platform even when that platform is
explicitly configured."""
config = {"platform_toolsets": {"telegram": ["hermes-telegram", "discord"]}}
enabled = _get_platform_tools(config, "telegram")
assert "discord" not in enabled
assert "discord_admin" not in enabled
def test_discord_explicit_workaround_still_works():
"""Regression guard: the documented workaround of listing toolsets
explicitly must keep working after the fix."""
config = {
"platform_toolsets": {"discord": ["hermes-discord", "discord", "discord_admin"]}
}
enabled = _get_platform_tools(config, "discord")
assert "discord" in enabled
assert "discord_admin" in enabled
def test_get_platform_tools_x_search_auto_enabled_when_xai_api_key_present(monkeypatch):
"""x_search toolset auto-enables when XAI_API_KEY is set, even without
OAuth tokens the API-key path is a supported credential source."""