Bug
Write a note tagged ['alpha','beta']. search_notes(query="tag:alpha,beta") matches it (the tool's tag: parser splits commas). The same intent via the parameter search_notes(tags="alpha,beta") returns ZERO results because coerce_list wraps the string as the single literal tag ['alpha,beta'], which matches nothing. Within one tool, comma-string tags behave two different ways. It is also inconsistent with write_note, whose documented tags convention splits comma-strings. (CLI search-notes is unaffected — it uses repeatable --tag and builds a real list.)
Root cause
src/basic_memory/mcp/tools/search.py:679-682 applies BeforeValidator(coerce_list) to tags. coerce_list (src/basic_memory/utils.py:559-572) JSON-parses array strings but for any other string returns [v] (wraps without splitting). Meanwhile the tool's own tag: query parser at search.py:882-884 explicitly splits comma-separated lists ('tag:coffee,brewing'), creating the internal inconsistency. The same coerce_list non-split also affects note_types/entity_types/categories comma-strings (search.py:646/653/661).
Suggested fix
Use a tag-aware coercer for the search_notes tags parameter that splits bare comma-strings (mirroring parse_tags / the tag: shorthand), e.g. replace BeforeValidator(coerce_list) on tags with BeforeValidator(parse_tags) (parse_tags already handles list, JSON-array string, and comma-string consistently and returns [] for None — adjust to return None when empty if needed). For note_types/entity_types/categories, decide whether comma-strings are supported; if so, apply the same comma-splitting coercer so all list filters behave consistently.
Repro (failing integration test, no mocks)
This test asserts the correct behavior and fails on current main (FAILS on HEAD: AssertionError: tags='alpha,beta' param must behave like the tag: shorthand (both split commas). query_hit=True param_hit=False. (Companion test test_search_notes_tags_comma_string_matc). It was produced by an automated integration-test bug hunt and can be dropped into test-int/ as the regression test once fixed.
"""Bug hunt: search_notes internal inconsistency — the `tag:` query shorthand
splits comma-separated tags (search.py:882-884), but the `tags=` parameter does
NOT (coerce_list wraps the whole string). Same comma string, two different results
within ONE tool.
"""
import pytest
from fastmcp import Client
@pytest.mark.asyncio
async def test_tags_param_vs_tag_query_comma_consistency(mcp_server, app, test_project):
async with Client(mcp_server) as client:
# Note tagged alpha + beta
await client.call_tool(
"write_note",
{
"project": test_project.name,
"title": "Tag Shorthand Note",
"directory": "tag-shorthand",
"content": "# Tag Shorthand Note\n\nTagShorthandToken body",
"tags": ["alpha", "beta"],
},
)
# Path A: tag: query shorthand with comma list -> splits, matches
via_query = await client.call_tool(
"search_notes",
{
"project": test_project.name,
"query": "tag:alpha,beta",
"search_type": "text",
},
)
query_hit = "Tag Shorthand Note" in via_query.content[0].text
# Path B: tags= parameter with the SAME comma string
via_param = await client.call_tool(
"search_notes",
{
"project": test_project.name,
"query": "TagShorthandToken",
"search_type": "text",
"tags": "alpha,beta",
},
)
param_hit = "Tag Shorthand Note" in via_param.content[0].text
assert query_hit, "tag: query shorthand should match (sanity)"
assert param_hit == query_hit, (
"tags='alpha,beta' param must behave like the tag: shorthand "
f"(both split commas). query_hit={query_hit} param_hit={param_hit}"
)
🤖 Found via automated integration-test bug hunt (Claude Code)
Bug
Write a note tagged ['alpha','beta'].
search_notes(query="tag:alpha,beta")matches it (the tool's tag: parser splits commas). The same intent via the parametersearch_notes(tags="alpha,beta")returns ZERO results because coerce_list wraps the string as the single literal tag ['alpha,beta'], which matches nothing. Within one tool, comma-string tags behave two different ways. It is also inconsistent with write_note, whose documented tags convention splits comma-strings. (CLI search-notes is unaffected — it uses repeatable --tag and builds a real list.)Root cause
src/basic_memory/mcp/tools/search.py:679-682 applies BeforeValidator(coerce_list) to
tags. coerce_list (src/basic_memory/utils.py:559-572) JSON-parses array strings but for any other string returns[v](wraps without splitting). Meanwhile the tool's own tag: query parser at search.py:882-884 explicitly splits comma-separated lists ('tag:coffee,brewing'), creating the internal inconsistency. The same coerce_list non-split also affects note_types/entity_types/categories comma-strings (search.py:646/653/661).Suggested fix
Use a tag-aware coercer for the search_notes
tagsparameter that splits bare comma-strings (mirroring parse_tags / the tag: shorthand), e.g. replace BeforeValidator(coerce_list) ontagswith BeforeValidator(parse_tags) (parse_tags already handles list, JSON-array string, and comma-string consistently and returns [] for None — adjust to return None when empty if needed). For note_types/entity_types/categories, decide whether comma-strings are supported; if so, apply the same comma-splitting coercer so all list filters behave consistently.Repro (failing integration test, no mocks)
This test asserts the correct behavior and fails on current
main(FAILS on HEAD: AssertionError: tags='alpha,beta' param must behave like the tag: shorthand (both split commas). query_hit=True param_hit=False. (Companion test test_search_notes_tags_comma_string_matc). It was produced by an automated integration-test bug hunt and can be dropped intotest-int/as the regression test once fixed.🤖 Found via automated integration-test bug hunt (Claude Code)