hermes - 💡(How to fix) Fix [Bug]: discord/discord_admin tools silently missing when platform_toolsets uses hermes-discord composite

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The bug has three interacting factors, all of which must be present:

Fix Action

Fix / Workaround

Workaround (immediate)

Code Example

platform_toolsets:
     discord:
     - hermes-discord

---

if platform in default_off and platform not in _TOOLSET_PLATFORM_RESTRICTIONS:
    default_off.remove(platform)
enabled_toolsets -= default_off

---

# tools_config.py, CONFIGURABLE_TOOLSETS includes "discord" and "discord_admin"

---

skip = configurable_keys | plugin_ts_keys | platform_default_keys

---

# Recover non-configurable platform toolsets (e.g. discord, feishu_doc,
# feishu_drive).  These are part of the platform's default composite but
# absent from CONFIGURABLE_TOOLSETS

---

>>> _get_platform_tools(config, 'discord')
# CONFIG A: [hermes-discord]
# After reverse-mapping:    discord=True,  discord_admin=True# After default_off strip:  discord=False, discord_admin=False  ❌ stripped
# Recovery pass added:      ['kanban']                           (not discord)
# FINAL: discord=False, discord_admin=False

# CONFIG B: [hermes-discord, discord, discord_admin]
# After explicit extraction: discord=True, discord_admin=True    (branch if)
# After composite expansion: discord=True, discord_admin=True   ✅ survive
# FINAL: discord=True, discord_admin=True

---

# Before:
if platform in default_off and platform not in _TOOLSET_PLATFORM_RESTRICTIONS:
    default_off.remove(platform)

# After:
if platform in default_off:
    restricted_to = _TOOLSET_PLATFORM_RESTRICTIONS.get(platform, set())
    if platform not in restricted_to:
        default_off.remove(platform)

---

platform_toolsets:
  discord:
  - hermes-discord
  - discord
  - discord_admin
RAW_BUFFERClick to expand / collapse

Bug Description

When platform_toolsets.discord is set to [hermes-discord] in config.yaml, the discord and discord_admin tools are not loaded in Discord gateway sessions, even after hermes gateway restart and /reset.

The composite toolset hermes-discord resolves its constituent toolsets (discord, discord_admin) correctly via reverse-mapping, but they are then silently removed by the _DEFAULT_OFF_TOOLSETS filter. The recovery pass at lines 1198-1222 cannot rescue them because discord and discord_admin are in CONFIGURABLE_TOOLSETS (unlike feishu_doc/feishu_drive), which puts them in the skip set.

Steps to Reproduce

  1. Set the following in ~/.hermes/config.yaml:
    platform_toolsets:
      discord:
      - hermes-discord
  2. Run hermes gateway restart
  3. Run /reset in the Discord channel
  4. Try to use any discord tool action (e.g. create_thread, fetch_messages)

Expected: The discord and discord_admin tools should be available in the Discord gateway session.

Actual: They are not available.

Root Cause

The bug has three interacting factors, all of which must be present:

Factor 1 — Name collision in _get_platform_tools() (line ~1179)

In hermes_cli/tools_config.py, the else branch (no explicit config):

if platform in default_off and platform not in _TOOLSET_PLATFORM_RESTRICTIONS:
    default_off.remove(platform)
enabled_toolsets -= default_off

platform = "discord" is in default_off (because "discord"_DEFAULT_OFF_TOOLSETS). But "discord" is also a key in _TOOLSET_PLATFORM_RESTRICTIONS (as a toolset name, not a platform name). So the condition platform not in _TOOLSET_PLATFORM_RESTRICTIONS is False, "discord" stays in default_off, and gets stripped by enabled_toolsets -= default_off.

Factor 2 — discord is in CONFIGURABLE_TOOLSETS

Unlike feishu_doc and feishu_drive (which are NOT in CONFIGURABLE_TOOLSETS), both discord and discord_admin ARE configurable toolsets:

# tools_config.py, CONFIGURABLE_TOOLSETS includes "discord" and "discord_admin"

This means they end up in configurable_keys, which feeds into the recovery pass skip set:

skip = configurable_keys | plugin_ts_keys | platform_default_keys

"discord"skip → recovery pass never considers it.

Factor 3 — Stale comment (line 1192)

The recovery pass comment says:

# Recover non-configurable platform toolsets (e.g. discord, feishu_doc,
# feishu_drive).  These are part of the platform's default composite but
# absent from CONFIGURABLE_TOOLSETS

discord is NOT absent from CONFIGURABLE_TOOLSETS. The comment is stale/incorrect. Only feishu_doc and feishu_drive are actually non-configurable and rescued by the recovery pass.

Confirmation by execution

>>> _get_platform_tools(config, 'discord')
# CONFIG A: [hermes-discord]
# After reverse-mapping:    discord=True,  discord_admin=True   ✅
# After default_off strip:  discord=False, discord_admin=False  ❌ stripped
# Recovery pass added:      ['kanban']                           (not discord)
# FINAL: discord=False, discord_admin=False

# CONFIG B: [hermes-discord, discord, discord_admin]
# After explicit extraction: discord=True, discord_admin=True   ✅ (branch if)
# After composite expansion: discord=True, discord_admin=True   ✅ survive
# FINAL: discord=True, discord_admin=True

Proposed Fix

Option A — Remove discord/discord_admin from _DEFAULT_OFF_TOOLSETS

Since these toolsets are already restricted to the discord platform via _TOOLSET_PLATFORM_RESTRICTIONS, making them default-off is redundant. Remove them from _DEFAULT_OFF_TOOLSETS and let the platform restriction do the gating.

Option B — Fix the condition at line 1179

Instead of checking whether the platform name appears as a key in _TOOLSET_PLATFORM_RESTRICTIONS, check whether the toolset (same name as platform) is restricted to this platform:

# Before:
if platform in default_off and platform not in _TOOLSET_PLATFORM_RESTRICTIONS:
    default_off.remove(platform)

# After:
if platform in default_off:
    restricted_to = _TOOLSET_PLATFORM_RESTRICTIONS.get(platform, set())
    if platform not in restricted_to:
        default_off.remove(platform)

This still requires the recovery pass to work (Factor 2), so both fixes may be needed.

Option C — Also fix the recovery pass

If discord/discord_admin remain in CONFIGURABLE_TOOLSETS, the recovery pass skip set could be adjusted to allow platform-restricted toolsets through even when they're configurable. For example, exclude keys present in _TOOLSET_PLATFORM_RESTRICTIONS from the configurable_keys portion of skip when the current platform is an allowed platform.

Workaround (immediate)

Replace the composite hermes-discord with explicit toolset names in platform_toolsets.discord. This routes through the has_explicit_config branch where explicit extraction (line 1122: enabled_toolsets = {ts for ts in toolset_names if ts in configurable_keys}) captures them before _DEFAULT_OFF_TOOLSETS filtering can strip them:

platform_toolsets:
  discord:
  - hermes-discord
  - discord
  - discord_admin

This is confirmed working.

Environment

  • Hermes version: v2026.5.16
  • OS: Linux 6.8.0-117-generic
  • Gateway: Discord
  • Code: hermes_cli/tools_config.py lines 1179, 1207-1209, 1192

Related

  • PR #15091 — covers a similar toolset loading issue but in the has_explicit_config branch; does not address this specific case

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: discord/discord_admin tools silently missing when platform_toolsets uses hermes-discord composite