Files
basicmachines-co-basic-memory/docs/metadata-search.md
T
phernandez 73413486bc feat: merge search_by_metadata into search_notes with optional query
Make `query` optional in `search_notes` so it becomes the single search tool.
Remove `search_by_metadata` entirely — it was unreleased and redundant since
`search_notes` already supports `metadata_filters`, `tags`, and `status`.

- 🔧 `query` param is now `Optional[str] = None`
- 🛡️ Added None guards for project detection and URL resolution
-  Added `no_criteria()` validation with helpful error message
- 🗑️ Deleted `search_by_metadata` tool, imports, tests, and contract entry
- 📝 Updated docs, README, and v0.19.0 release notes

Closes #605

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-02-25 13:30:42 -06:00

7.1 KiB

Metadata Search Reference

Basic Memory automatically indexes custom frontmatter fields so you can query them with structured filters. Any YAML key in a note's frontmatter beyond the standard set (title, type, tags, permalink, schema) is stored as entity_metadata and becomes searchable.

Querying with search_notes

search_notes is the single search tool for all queries — text, metadata filters, or both. The query parameter is optional, so you can use metadata filters alone without passing an empty string.

Filter Syntax

Filters are a JSON dictionary where each key targets a frontmatter field and the value specifies the match condition. Multiple keys combine with AND logic — every filter must match.

Equality

Match a single value exactly.

{"status": "active"}

Finds notes whose frontmatter contains status: active.

Array Contains (all)

Pass a list to require all listed values to be present in the field.

{"tags": ["security", "oauth"]}

Finds notes tagged with both security and oauth.

$in (any of)

Match if the field equals any value in the list.

{"priority": {"$in": ["high", "critical"]}}

$gt, $gte, $lt, $lte

Numeric and text comparisons. Numeric values use numeric comparison; strings use lexicographic comparison.

{"confidence": {"$gt": 0.7}}
{"score": {"$lte": 100}}

$between

Range filter (inclusive). Takes a [min, max] pair.

{"score": {"$between": [0.3, 0.8]}}

Nested Access (dot notation)

Access nested frontmatter values using dots.

{"schema.version": "2"}

This queries the version key inside a schema object in frontmatter.

Summary Table

Operator Syntax Example
Equality {"field": "value"} {"status": "active"}
Array contains (all) {"field": ["a", "b"]} {"tags": ["security", "oauth"]}
$in (any of) {"field": {"$in": [...]}} {"priority": {"$in": ["high", "critical"]}}
$gt / $gte {"field": {"$gt": N}} {"confidence": {"$gt": 0.7}}
$lt / $lte {"field": {"$lt": N}} {"score": {"$lt": 0.5}}
$between {"field": {"$between": [min, max]}} {"score": {"$between": [0.3, 0.8]}}
Nested access {"a.b": "value"} {"schema.version": "2"}

Key rules:

  • Filter keys must match [A-Za-z0-9_-]+ (dots separate nesting levels).
  • Each operator dict must contain exactly one operator.
  • $in and array-contains require non-empty lists.
  • $between requires exactly two values [min, max].

MCP Tool — search_notes

search_notes is the single search tool for text queries, metadata filters, or both. The query parameter is optional.

Relevant parameters:

Parameter Type Description
query string (optional) Text search query. Omit for filter-only searches.
metadata_filters dict Structured filter dict (see syntax above)
tags list[str] Convenience shorthand — merged into metadata_filters["tags"]
status string Convenience shorthand — merged into metadata_filters["status"]

Merging rules: tags and status are convenience shortcuts. They are merged into metadata_filters using setdefault — if the same key already exists in metadata_filters, the explicit filter wins.

Examples:

# Text search filtered by metadata
await search_notes("authentication", metadata_filters={"status": "draft"})

# Filter-only search (no query needed)
await search_notes(metadata_filters={"type": "spec"})

# Combine text, tags shortcut, and metadata
await search_notes(
    "oauth flow",
    tags=["security"],
    metadata_filters={"confidence": {"$gt": 0.7}},
)

# Convenience shortcuts
await search_notes("planning", status="active")
await search_notes(tags=["tier1", "alpha"])

Tag Search Shortcuts

The tag: prefix in a search query is a shorthand for tag-based metadata filtering. When search_notes receives a query starting with tag:, it converts the query into a tags filter and clears the text query.

# These are equivalent:
await search_notes("tag:tier1")
await search_notes("", tags=["tier1"])

# Multiple tags (comma or space separated) — all must be present:
await search_notes("tag:tier1,alpha")
await search_notes("tag:tier1 alpha")

CLI Access

The bm tool search-notes command exposes metadata filtering via --meta and --filter flags.

--meta — simple key=value filters

Repeatable flag for equality filters on frontmatter fields.

# Single filter
bm tool search-notes "my query" --meta status=draft

# Multiple filters (AND logic)
bm tool search-notes "" --meta status=active --meta priority=high

--filter — advanced JSON filters

Pass a full JSON filter dictionary for operator-based queries.

# Range filter
bm tool search-notes "" --filter '{"score": {"$between": [0.3, 0.8]}}'

# $in filter
bm tool search-notes "" --filter '{"priority": {"$in": ["high", "critical"]}}'

--tag and --status — convenience shortcuts

bm tool search-notes "query" --tag security --tag oauth
bm tool search-notes "" --status draft

Combined example

bm tool search-notes "authentication" --tag security --meta status=draft --type spec

Practical Examples

Example notes with custom frontmatter

specs/auth-design.md:

---
title: Auth Design
type: spec
tags: [security, oauth]
status: in-progress
priority: high
confidence: 0.85
---

# Auth Design

## Observations
- [decision] Use OAuth 2.1 with PKCE for all client types #security
- [requirement] Token refresh must be transparent to the user

## Relations
- implements [[Security Requirements]]

specs/search-redesign.md:

---
title: Search Redesign
type: spec
tags: [search, performance]
status: draft
priority: medium
confidence: 0.6
---

# Search Redesign

## Observations
- [goal] Sub-100ms search response times #performance
- [approach] Hybrid FTS + vector retrieval

## Relations
- depends_on [[Database Schema]]

Queries that find them

# Find all in-progress specs
await search_notes(metadata_filters={"status": "in-progress", "type": "spec"})
# → Auth Design

# Find high-confidence specs
await search_notes(metadata_filters={"confidence": {"$gt": 0.7}})
# → Auth Design (confidence: 0.85)

# Find specs with priority high or medium
await search_notes(metadata_filters={"priority": {"$in": ["high", "medium"]}})
# → Auth Design, Search Redesign

# Find specs in a confidence range
await search_notes(metadata_filters={"confidence": {"$between": [0.5, 0.9]}})
# → Auth Design (0.85), Search Redesign (0.6)

# Find notes tagged with security
await search_notes("tag:security")
# → Auth Design

# Combined: text search + metadata filter
await search_notes("OAuth", metadata_filters={"status": "in-progress"})
# → Auth Design

CLI equivalents

bm tool search-notes "" --meta status=in-progress --type spec
bm tool search-notes "" --filter '{"confidence": {"$gt": 0.7}}'
bm tool search-notes "OAuth" --meta status=in-progress
bm tool search-notes --tag security