Session delete and ID lookup bug corrections
NousResearch/hermes-agent · cluster 1412 · apps/desktop
Data as of
Nothing open on GitHub. Fix merged in #2102. Check it covers your case.
The short answer
- Known?
- Yes. 7 related items grouped here, 3 issues already closed.
- Fix event
- A fix was merged to main in PR #2102. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PR #2102. That is separate from the fix event. - Still open
- Nothing open on GitHub.
Multiple bugs around deleting sessions by ID were addressed, including missing owner/profile context passed to endpoints and session ID not being looked up before delete operations. Related changes also cover session listing and MCP schema normalization.
Open issues0
Open PRs0
Fixes on record
-
#2102 fix(tools,cli): normalise MCP schemas + expand session list columns
files:
hermes_cli/main.py,tests/tools/test_mcp_tool.py,tools/mcp_tool.pyShow the diff
-
hermes_cli/main.py@@ -3721,20 +3721,20 @@ def cmd_sessions(args): return has_titles = any(s.get("title") for s in sessions) if has_titles: - print(f"{'Title':<22} {'Preview':<40} {'Last Active':<13} {'ID'}") - print("─" * 100) + print(f"{'Title':<32} {'Preview':<40} {'Last Active':<13} {'ID'}") + print("─" * 110) else: print(f"{'Preview':<50} {'Last Active':<13} {'Src':<6} {'ID'}") - print("─" * 90) + print("─" * 95) for s in sessions: last_active = _relative_time(s.get("last_active")) preview = s.get("preview", "")[:38] if has_titles else s.get("preview", "")[:48] if has_titles: - title = (s.get("title") or "—")[:20] - sid = s["id"][:20] - print(f"{title:<22} {preview:<40} {last_active:<13} {sid}") + title = (s.get("title") or "—")[:30] + sid = s["id"] + print(f"{title:<32} {preview:<40} {last_active:<13} {sid}") else: - sid = s["id"][:20] + sid = s["id"] print(f"{preview:<50} {last_active:<13} {s['source']:<6} {sid}") elif action == "export": -
tests/tools/test_mcp_tool.py@@ -106,6 +106,18 @@ def test_empty_input_schema_gets_default(self): assert schema["parameters"]["type"] == "object" assert schema["parameters"]["properties"] == {} + def test_object_schema_without_properties_gets_normalized(self): + from tools.mcp_tool import _convert_mcp_schema + + mcp_tool = _make_mcp_tool( + name="ask", + description="Ask Crawl4AI", + input_schema={"type": "object"}, + ) + schema = _convert_mcp_schema("crawl4ai", mcp_tool) + + assert schema["parameters"] == {"type": "object", "properties": {}} + def test_tool_name_prefix_format(self): from tools.mcp_tool import _convert_mcp_schema @@ -1893,6 +1905,33 @@ def test_system_prompt_prepended(self): messages = call_args.kwargs["messages"] assert messages[0] == {"role": "system", "content": "Be helpful"} + def test_server_tools_with_object_schema_are_normalized(self): + """Server-provided tools should gain empty properties for object schemas.""" + fake_client = MagicMock() + fake_client.chat.completions.create.return_value = _make_llm_response() + server_tool = SimpleNamespace( + name="ask", + description="Ask Crawl4AI", + inputSchema={"type": "object"}, + ) + + with patch( + "agent.auxiliary_client.call_llm", + return_value=fake_client.chat.completions.create.return_value, + ) as mock_call: + params = _make_sampling_params(tools=[server_tool]) + asyncio.run(self.handler(None, params)) + + tools = mock_call.call_args.kwargs["tools"] + assert tools == [{ + "type": "function", + "function": { + "name": "ask", + "description": "Ask Crawl4AI", + "parameters": {"type": "object", "properties": {}}, + }, + }] + def test_length_stop_reason(self): """finish_reason='length' maps to stopReason='maxTokens'.""" fake_client = MagicMock() -
tools/mcp_tool.py@@ -605,7 +605,9 @@ async def __call__(self, context, params): "function": { "name": getattr(t, "name", ""), "description": getattr(t, "description", "") or "", - "parameters": getattr(t, "inputSchema", {}) or {}, + "parameters": _normalize_mcp_input_schema( + getattr(t, "inputSchema", None) + ), }, } for t in server_tools @@ -1213,6 +1215,17 @@ def _check() -> bool: # Discovery & registration # --------------------------------------------------------------------------- +def _normalize_mcp_input_schema(schema: dict | None) -> dict: + """Normalize MCP input schemas for LLM tool-calling compatibility.""" + if not schema: + return {"type": "object", "properties": {}} + + if schema.get("type") == "object" and "properties" not in schema: + return {**schema, "properties": {}} + + return schema + + def _convert_mcp_schema(server_name: str, mcp_tool) -> dict: """Convert an MCP tool listing to the Hermes registry schema format. @@ -1231,10 +1244,7 @@ def _convert_mcp_schema(server_name: str, mcp_tool) -> dict: return { "name": prefixed_name, "description": mcp_tool.description or f"MCP tool {mcp_tool.name} from {server_name}", - "parameters": mcp_tool.inputSchema if mcp_tool.inputSchema else { - "type": "object", - "properties": {}, - }, + "parameters": _normalize_mcp_input_schema(mcp_tool.inputSchema), }
-
3 PRs closed without merging
From the close comments: superseded by another PR 1 · other 2
Closed issues · 3
How this cluster was grouped
-
Link evidence (
partly structural links): 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.22: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- apps/desktop
- Merged PRs
- #2102
- Closed issues
- 3
- Attempts
- 3 PRs closed without merging
- Fix link checked
- 20 Mar 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.