-
Notifications
You must be signed in to change notification settings - Fork 270
fix(mcp): split comma-separated tags in search_notes tags param #932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| from pydantic import AliasChoices, BeforeValidator, Field | ||
|
|
||
| from basic_memory.config import ConfigManager, has_cloud_credentials | ||
| from basic_memory.utils import build_canonical_permalink, coerce_dict, coerce_list | ||
| from basic_memory.utils import build_canonical_permalink, coerce_dict, coerce_list, parse_tags | ||
| from basic_memory.mcp.async_client import ( | ||
| _explicit_routing, | ||
| _force_local_mode, | ||
|
|
@@ -676,9 +676,13 @@ async def search_notes( | |
| Dict[str, Any] | None, | ||
| BeforeValidator(coerce_dict), | ||
| ] = None, | ||
| # parse_tags, not coerce_list: tags="a,b" must split into ["a", "b"] to match the | ||
| # tag: query shorthand below and write_note's documented tags convention (#910). | ||
| # coerce_list would wrap the comma string as the single literal tag ["a,b"], | ||
| # which matches nothing. | ||
| tags: Annotated[ | ||
| List[str] | None, | ||
| BeforeValidator(coerce_list), | ||
| BeforeValidator(parse_tags), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because the comma splitting lives only in the Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in follow-up PR #941: |
||
| ] = None, | ||
| status: Optional[str] = None, | ||
| min_similarity: Annotated[ | ||
|
|
@@ -795,7 +799,9 @@ async def search_notes( | |
| observations whose category matches exactly. | ||
| after_date: Optional date filter for recent content (e.g., "1 week", "2d", "2024-01-01") | ||
| metadata_filters: Optional structured frontmatter filters (e.g., {"status": "in-progress"}) | ||
| tags: Optional tag filter (frontmatter tags); shorthand for metadata_filters["tags"] | ||
| tags: Optional tag filter (frontmatter tags); shorthand for metadata_filters["tags"]. | ||
| Accepts a list (["a", "b"]) or a comma-separated string ("a,b"), matching the | ||
| write_note tags convention and the tag: query shorthand. | ||
| status: Optional status filter (frontmatter status); shorthand for metadata_filters["status"] | ||
| min_similarity: Optional float to override the global semantic_min_similarity threshold | ||
| for this query. E.g., 0.0 to see all vector results, or 0.8 for high precision. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an MCP client sends a malformed
tagsvalue such as a JSON number or object, this validator now accepts it becauseparse_tagsstringifies unsupported types (for example42becomes["42"]) before Pydantic validatesList[str]. With the previouscoerce_listpath those inputs were left as non-lists and rejected, so this weakens the tool boundary and turns caller mistakes into confusing no-result searches with bogus tag filters. Consider wrappingparse_tagshere so onlystr,list, orNoneare accepted, or tighteningparse_tagswithout breaking its frontmatter callers.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in follow-up PR #941: the
tagsBeforeValidator now uses a newstrict_search_tagswrapper (utils.py) that only normalizesstr/list/Noneviaparse_tagsand passes any other type through unchanged, so Pydantic rejectstags=42/tags={"a": 1}with a clear validation error instead of stringifying them into silent no-result searches.parse_tagsitself is unchanged for its frontmatter/write_note callers.