fix(mcp): case-insensitive agent name in diary_write/diary_read (#1243)

`tool_diary_write` stored the `agent` metadata verbatim after `sanitize_name`
(which preserves case), while `tool_diary_read` filtered by exact match —
so writing as "Claude" and reading as "claude" silently returned zero rows.

Both endpoints now lowercase `agent_name` immediately after sanitization.
The default per-agent wing slug is also stable across casings since it's
derived from the same normalized form.

Behavior change: entries written prior to this fix under mixed-case agent
names will not match the new lowercase filter; documented under v3.3.5
in CHANGELOG with a `mempalace repair` pointer.

Adds a regression test (`test_diary_read_case_insensitive_agent`) and
updates the existing `test_diary_write_and_read` to assert the new
lowercase agent identity.

Closes #1243

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Igor Lins e Silva
2026-05-02 22:57:09 -03:00
parent d07b730f08
commit e9222b4c7b
3 changed files with 73 additions and 11 deletions
+13 -4
View File
@@ -677,7 +677,7 @@ def tool_check_duplicate(content: str, threshold: float = 0.9):
"vector_disabled": True,
"vector_disabled_reason": _vector_disabled_reason,
"hint": (
"duplicate detection requires vector search; run " "`mempalace repair` to restore"
"duplicate detection requires vector search; run `mempalace repair` to restore"
),
}
try:
@@ -1133,9 +1133,13 @@ def tool_diary_write(agent_name: str, entry: str, topic: str = "general", wing:
This is the agent's personal journal — observations, thoughts,
what it worked on, what it noticed, what it thinks matters.
Note: ``agent_name`` is normalized to lowercase before storage so
that diary reads are case-insensitive (see #1243). "Claude",
"claude", and "CLAUDE" all resolve to the same agent.
"""
try:
agent_name = sanitize_name(agent_name, "agent_name")
agent_name = sanitize_name(agent_name, "agent_name").lower()
entry = sanitize_content(entry)
topic = sanitize_name(topic, "topic")
except ValueError as e:
@@ -1144,7 +1148,7 @@ def tool_diary_write(agent_name: str, entry: str, topic: str = "general", wing:
if wing:
wing = sanitize_name(wing)
else:
wing = f"wing_{agent_name.lower().replace(' ', '_')}"
wing = f"wing_{agent_name.replace(' ', '_')}"
room = "diary"
col = _get_collection(create=True)
if not col:
@@ -1209,9 +1213,14 @@ def tool_diary_read(agent_name: str, last_n: int = 10, wing: str = ""):
written to. Diary writes from hooks land in project-derived wings
(``wing_<project>``), so requiring a specific wing on read would
silo those entries from agent-initiated reads.
Note: ``agent_name`` is normalized to lowercase before filtering so
that reads are case-insensitive (see #1243). Entries written under
pre-fix mixed-case agent names will not match the lowercase filter;
use ``mempalace repair`` to migrate legacy data if needed.
"""
try:
agent_name = sanitize_name(agent_name, "agent_name")
agent_name = sanitize_name(agent_name, "agent_name").lower()
if wing:
wing = sanitize_name(wing)
except ValueError as e: