mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(acp): include MCP toolsets in ACP sessions
This commit is contained in:
parent
8a1e247c6c
commit
dfc5563641
4 changed files with 81 additions and 5 deletions
|
|
@ -60,7 +60,7 @@ from acp_adapter.events import (
|
||||||
make_tool_progress_cb,
|
make_tool_progress_cb,
|
||||||
)
|
)
|
||||||
from acp_adapter.permissions import make_approval_callback
|
from acp_adapter.permissions import make_approval_callback
|
||||||
from acp_adapter.session import SessionManager, SessionState
|
from acp_adapter.session import SessionManager, SessionState, _expand_acp_enabled_toolsets
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -287,7 +287,11 @@ class HermesACPAgent(acp.Agent):
|
||||||
try:
|
try:
|
||||||
from model_tools import get_tool_definitions
|
from model_tools import get_tool_definitions
|
||||||
|
|
||||||
enabled_toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
|
enabled_toolsets = _expand_acp_enabled_toolsets(
|
||||||
|
getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"],
|
||||||
|
mcp_server_names=[server.name for server in mcp_servers],
|
||||||
|
)
|
||||||
|
state.agent.enabled_toolsets = enabled_toolsets
|
||||||
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
|
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
|
||||||
state.agent.tools = get_tool_definitions(
|
state.agent.tools = get_tool_definitions(
|
||||||
enabled_toolsets=enabled_toolsets,
|
enabled_toolsets=enabled_toolsets,
|
||||||
|
|
@ -754,7 +758,9 @@ class HermesACPAgent(acp.Agent):
|
||||||
def _cmd_tools(self, args: str, state: SessionState) -> str:
|
def _cmd_tools(self, args: str, state: SessionState) -> str:
|
||||||
try:
|
try:
|
||||||
from model_tools import get_tool_definitions
|
from model_tools import get_tool_definitions
|
||||||
toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
|
toolsets = _expand_acp_enabled_toolsets(
|
||||||
|
getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]
|
||||||
|
)
|
||||||
tools = get_tool_definitions(enabled_toolsets=toolsets, quiet_mode=True)
|
tools = get_tool_definitions(enabled_toolsets=toolsets, quiet_mode=True)
|
||||||
if not tools:
|
if not tools:
|
||||||
return "No tools available."
|
return "No tools available."
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,24 @@ def _register_task_cwd(task_id: str, cwd: str) -> None:
|
||||||
logger.debug("Failed to register ACP task cwd override", exc_info=True)
|
logger.debug("Failed to register ACP task cwd override", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_acp_enabled_toolsets(
|
||||||
|
toolsets: List[str] | None = None,
|
||||||
|
mcp_server_names: List[str] | None = None,
|
||||||
|
) -> List[str]:
|
||||||
|
"""Return ACP toolsets plus explicit MCP server toolsets for this session."""
|
||||||
|
expanded: List[str] = []
|
||||||
|
for name in list(toolsets or ["hermes-acp"]):
|
||||||
|
if name and name not in expanded:
|
||||||
|
expanded.append(name)
|
||||||
|
|
||||||
|
for server_name in list(mcp_server_names or []):
|
||||||
|
toolset_name = f"mcp-{server_name}"
|
||||||
|
if server_name and toolset_name not in expanded:
|
||||||
|
expanded.append(toolset_name)
|
||||||
|
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
|
||||||
def _clear_task_cwd(task_id: str) -> None:
|
def _clear_task_cwd(task_id: str) -> None:
|
||||||
"""Remove task-specific cwd overrides for an ACP session."""
|
"""Remove task-specific cwd overrides for an ACP session."""
|
||||||
if not task_id:
|
if not task_id:
|
||||||
|
|
@ -537,9 +555,18 @@ class SessionManager:
|
||||||
elif isinstance(model_cfg, str) and model_cfg.strip():
|
elif isinstance(model_cfg, str) and model_cfg.strip():
|
||||||
default_model = model_cfg.strip()
|
default_model = model_cfg.strip()
|
||||||
|
|
||||||
|
configured_mcp_servers = [
|
||||||
|
name
|
||||||
|
for name, cfg in (config.get("mcp_servers") or {}).items()
|
||||||
|
if not isinstance(cfg, dict) or cfg.get("enabled", True) is not False
|
||||||
|
]
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"platform": "acp",
|
"platform": "acp",
|
||||||
"enabled_toolsets": ["hermes-acp"],
|
"enabled_toolsets": _expand_acp_enabled_toolsets(
|
||||||
|
["hermes-acp"],
|
||||||
|
mcp_server_names=configured_mcp_servers,
|
||||||
|
),
|
||||||
"quiet_mode": True,
|
"quiet_mode": True,
|
||||||
"session_id": session_id,
|
"session_id": session_id,
|
||||||
"model": model or default_model,
|
"model": model or default_model,
|
||||||
|
|
|
||||||
|
|
@ -904,9 +904,15 @@ class TestRegisterSessionMcpServers:
|
||||||
]
|
]
|
||||||
|
|
||||||
with patch("tools.mcp_tool.register_mcp_servers", return_value=["mcp_srv_search"]), \
|
with patch("tools.mcp_tool.register_mcp_servers", return_value=["mcp_srv_search"]), \
|
||||||
patch("model_tools.get_tool_definitions", return_value=fake_tools):
|
patch("model_tools.get_tool_definitions", return_value=fake_tools) as mock_defs:
|
||||||
await agent._register_session_mcp_servers(state, [server])
|
await agent._register_session_mcp_servers(state, [server])
|
||||||
|
|
||||||
|
mock_defs.assert_called_once_with(
|
||||||
|
enabled_toolsets=["hermes-acp", "mcp-srv"],
|
||||||
|
disabled_toolsets=None,
|
||||||
|
quiet_mode=True,
|
||||||
|
)
|
||||||
|
assert state.agent.enabled_toolsets == ["hermes-acp", "mcp-srv"]
|
||||||
assert state.agent.tools == fake_tools
|
assert state.agent.tools == fake_tools
|
||||||
assert state.agent.valid_tool_names == {"mcp_srv_search", "terminal"}
|
assert state.agent.valid_tool_names == {"mcp_srv_search", "terminal"}
|
||||||
# _invalidate_system_prompt should have been called
|
# _invalidate_system_prompt should have been called
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,43 @@ class TestListAndCleanup:
|
||||||
class TestPersistence:
|
class TestPersistence:
|
||||||
"""Verify that sessions are persisted to SessionDB and can be restored."""
|
"""Verify that sessions are persisted to SessionDB and can be restored."""
|
||||||
|
|
||||||
|
def test_create_session_includes_registered_mcp_toolsets(self, tmp_path, monkeypatch):
|
||||||
|
captured = {}
|
||||||
|
|
||||||
|
def fake_resolve_runtime_provider(requested=None, **kwargs):
|
||||||
|
return {
|
||||||
|
"provider": "openrouter",
|
||||||
|
"api_mode": "chat_completions",
|
||||||
|
"base_url": "https://openrouter.example/v1",
|
||||||
|
"api_key": "***",
|
||||||
|
"command": None,
|
||||||
|
"args": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
def fake_agent(**kwargs):
|
||||||
|
captured.update(kwargs)
|
||||||
|
return SimpleNamespace(model=kwargs.get("model"), enabled_toolsets=kwargs.get("enabled_toolsets"))
|
||||||
|
|
||||||
|
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {
|
||||||
|
"model": {"provider": "openrouter", "default": "test-model"},
|
||||||
|
"mcp_servers": {
|
||||||
|
"olympus": {"command": "python", "enabled": True},
|
||||||
|
"exa": {"url": "https://exa.ai/mcp"},
|
||||||
|
"disabled": {"command": "python", "enabled": False},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||||
|
fake_resolve_runtime_provider,
|
||||||
|
)
|
||||||
|
db = SessionDB(tmp_path / "state.db")
|
||||||
|
|
||||||
|
with patch("run_agent.AIAgent", side_effect=fake_agent):
|
||||||
|
manager = SessionManager(db=db)
|
||||||
|
manager.create_session(cwd="/work")
|
||||||
|
|
||||||
|
assert captured["enabled_toolsets"] == ["hermes-acp", "mcp-olympus", "mcp-exa"]
|
||||||
|
|
||||||
def test_create_session_writes_to_db(self, manager):
|
def test_create_session_writes_to_db(self, manager):
|
||||||
state = manager.create_session(cwd="/project")
|
state = manager.create_session(cwd="/project")
|
||||||
db = manager._get_db()
|
db = manager._get_db()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue