# NousResearch/hermes-agent · Toolset resolution bugs: composite, alias, platform defaults

## Open now

- Nothing open on GitHub.

These issues and PRs address multiple related bugs where composite toolsets (e.g. hermes-cli, hermes-discord), legacy aliases, and platform-native defaults are silently dropped or ignored during toolset resolution. Several entries also cover Discord and Feishu tool wiring and splitting.

## TL;DR

- 12 member(s): 0 open issue(s), 3 closed issue(s), 0 open PR(s), 3 merged PR(s), 6 PR(s) closed without merging.
- A fix was merged to main in PR #22673. Verify it addresses your case.
- **Recommended:** read PR #22673 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-22673,pr-60553,pr-131906`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.15`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #22601: _get_platform_tools silently drops composite toolset (e.g. hermes-cli) when saved list mixes composite + configurable — entire native toolset disappears
- #35527: \[Bug\]: discord/discord_admin tools silently missing when platform_toolsets uses hermes-discord composite
- #41579: fix: _get_platform_tools() should resolve legacy toolset aliases (hermes → hermes-cli)

## Prior & competing PRs

- **#22673: fix(cli): expand composite toolset when mixed with configurables in platform_toolsets (salvage #22606)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/tools_config.py`, `tests/hermes_cli/test_tools_config.py`
  - fix hunks:
  - `hermes_cli/tools_config.py`

    ```diff
    @@ -972,6 +972,38 @@ def _get_platform_tools(
                 ts for ts in toolset_names
                 if ts in configurable_keys and _toolset_allowed_for_platform(ts, platform)
             }
    +        # Mixed config: composite toolset alongside configurables (e.g.
    +        # ``[hermes-cli, spotify]`` after enabling Spotify via ``hermes
    +        # tools``). Without expansion the composite name is silently dropped,
    +        # leaving sessions with only the configurable opt-ins and no native
    +        # tools. Mirror the else-branch's subset inference, but apply
    +        # _DEFAULT_OFF_TOOLSETS only to the implicit expansion — anything the
    +        # user explicitly listed (e.g. ``spotify``) must survive.
    +        composite_tools = set()
    +        for ts_name in toolset_names:
    +            if ts_name in configurable_keys or ts_name in plugin_ts_keys:
    +                continue
    +            if ts_name not in TOOLSETS:
    +                continue
    +            composite_tools.update(resolve_toolset(ts_name))
    +
    +        if composite_tools:
    +            expanded = set()
    +            for ts_key, _, _ in CONFIGURABLE_TOOLSETS:
    +                if not _toolset_allowed_for_platform(ts_key, platform):
    +                    continue
    +                ts_tools = set(resolve_toolset(ts_key))
    +                if ts_tools and ts_tools.issubset(composite_tools):
    +                    expanded.add(ts_key)
    +
    +            default_off = set(_DEFAULT_OFF_TOOLSETS)
    +            if platform in default_off and platform not in _TOOLSET_PLATFORM_RESTRICTIONS:
    +                default_off.remove(platform)
    +            if "homeassistant" in default_off and os.getenv("HASS_TOKEN"):
    +                default_off.remove("homeassistant")
    +            expanded -= default_off
    +
    +            enabled_toolsets |= expanded
         else:
             # No explicit config — fall back to resolving composite toolset names
             # (e.g. "hermes-cli") to individual tool names and reverse-mapping.
    ```
  - `tests/hermes_cli/test_tools_config.py`

    ```diff
    @@ -119,6 +119,64 @@ def test_get_platform_tools_homeassistant_toolset_off_for_cron_when_hass_token_m
         assert "homeassistant" not in cron_enabled
     
     
    +def test_get_platform_tools_expands_composite_when_mixed_with_configurable():
    +    """``[hermes-cli, spotify]`` (composite + configurable) must keep the full
    +    ``hermes-cli`` toolset alongside the explicit Spotify opt-in. The
    +    has_explicit_config branch used to drop ``hermes-cli`` on the floor,
    +    leaving sessions with only ``{spotify, kanban}``."""
    +    config = {"platform_toolsets": {"cli": ["hermes-cli", "spotify"]}}
    +
    +    enabled = _get_platform_tools(config, "cli", include_default_mcp_servers=False)
    +
    +    # Native tools must reappear.
    +    for ts in ("terminal", "file", "web", "browser", "memory", "delegation",
    +               "code_execution", "todo", "session_search", "skills"):
    +        assert ts in enabled, f"{ts} should be enabled when hermes-cli is listed"
    +    # User explicitly opted into Spotify — must survive _DEFAULT_OFF_TOOLSETS subtraction.
    +    assert "spotify" in enabled
    +
    +
    +def test_get_platform_tools_composite_only_unchanged():
    +    """Composite-only config (no configurable in list) must still take the
    +    else-branch path and produce the full toolset — guards against the new
    +    code accidentally hijacking the composite-only case."""
    +    composite_only = _get_platform_tools(
    +        {"platform_toolsets": {"cli": ["hermes-cli"]}},
    +        "cli",
    +        include_default_mcp_servers=False,
    +    )
    +    default = _get_platform_tools({}, "cli", include_default_mcp_servers=False)
    +
    +    assert composite_only == default
    +
    +
    +def test_get_platform_tools_configurable_only_no_expansion():
    +    """Configurable-only list (no composite) must not pull in unrelated
    +    toolsets — guards against the expansion firing when ``composite_tools``
    +    is empty."""
    +    config = {"platform_toolsets": {"cli": ["terminal", "file"]}}
    +
    +    enabled = _get_platform_tools(config, "cli", include_default_mcp_servers=False)
    +
    +    assert "terminal" in enabled
    +    assert "file" in enabled
    +    # Web shouldn't sneak in via the new expansion path.
    +    assert "web" not in enabled
    +
    +
    +def test_get_platform_tools_mixed_does_not_resurrect_default_off():
    +    """Expansion must subtract _DEFAULT_OFF_TOOLSETS from the implicit
    +    pull-in. Without this, ``hermes-cli`` expansion would re-enable
    +    ``moa`` / ``rl`` / ``homeassistant`` for users who never opted in."""
    +    config = {"platform_toolsets": {"cli": ["hermes-cli", "terminal"]}}
    +
    +    enabled = _get_platform_tools(config, "cli", include_default_mcp_servers=False)
    +
    +    assert "terminal" in enabled
    +    assert "moa" not in enabled
    +    assert "rl" not in enabled
    +
    +
     def test_get_platform_tools_preserves_explicit_empty_selection():
         config = {"platform_toolsets": {"cli": []}}
     
    ```
- **#35536: fix(tools): honor explicit composite toolsets for platform-restricted defaults**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `8a726e91ba2b56974498cc731cc8b51c95af3b80`, `#35527`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#38532: fix(tools): honor explicit composite toolsets for platform-restricted defaults**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#60553: fix(tools): honor explicit platform_toolsets config for platform-native default-off toolsets**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/tools_config.py`, `tests/hermes_cli/test_tools_config.py`
- **#131906: fix(tools): expand legacy "hermes" toolset alias to prevent silent tool loss \[risk 0.30\]**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/tools_config.py`, `tests/hermes_cli/test_tools_config.py`
- **#22606: fix(cli): expand composite toolset when mixed with configurables in platform_toolsets**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#22673`
- **#22608: fix(tools): expand composite platform toolsets alongside explicit configurable keys**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#22673`, `#22606`, `#22606`
- **#22641: fix(tools): expand composite toolsets in has_explicit_config branch**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#22673`, `#22606`, `#22606`
- **#41587: fix(tools): expand legacy "hermes" toolset alias to prevent silent tool loss**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#131906`

## Fix status

- Merged to main in PR #22673 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-10-03`.
- Subsystem: `gateway`
