Metadata Search
Every note in Basic Memory has YAML frontmatter at the top. Metadata search lets you find notes by those fields — status, type, tags, custom fields, anything you put there. It's how you ask things like "show me all in-progress specs tagged security" or "find decisions made in the last quarter with priority high or critical" without scanning text.
Metadata search is a parameter on the same search_notes tool you use for text and semantic search. You can use it on its own, or combine it with a text query and tag filters.
type: meeting, status: open, priority: high, and attendees: [...] is reachable from many more angles than one with just a title.What's in your frontmatter
Anything you put in the YAML block at the top of a note becomes searchable:
---
title: Auth Design
type: spec
tags: [security, oauth]
status: in-progress
priority: high
confidence: 0.85
---
A few fields have shortcuts in search_notes (tags, status, type), but all custom fields work the same way through metadata_filters. You can also reach into nested objects with dot notation — schema.confidence targets the confidence key inside a schema object.
A-Z a-z 0-9 _ - (with . for nesting). Field values can be strings, numbers, booleans, dates, or lists.Filter syntax
metadata_filters is a JSON object. Each key is a field name; each value is either a literal (for equality) or an operator object.
Multiple top-level keys combine with AND — all conditions must match.
Equality
{"status": "in-progress"}
Array contains (all values present)
For list-valued fields like tags, an array means "all of these":
{"tags": ["security", "oauth"]}
$in — any of
Match if the field equals any value in the list. This is how you express OR within a single field:
{"priority": {"$in": ["high", "critical"]}}
Comparison — $gt, $gte, $lt, $lte
Numeric or lexicographic comparison; boundaries on $gte/$lte are inclusive.
{"confidence": {"$gt": 0.7}}
{"score": {"$lte": 100}}
$between — inclusive range
Both ends included; the value must be exactly two items:
{"confidence": {"$between": [0.5, 0.9]}}
Nested fields (dot notation)
{"schema.confidence": {"$gt": 0.7}}
Operator reference
| Operator | Value | Behavior |
|---|---|---|
| (scalar) | string / number / bool / date | Exact match |
| (array) | list of scalars | All values must be present (for list fields) |
$in | non-empty list | Any of |
$gt, $gte, $lt, $lte | number | Comparison (gte/lte inclusive) |
$between | [min, max] | Inclusive range, exactly 2 values |
{"$gt": 0.5, "$lt": 1.0} is invalid — use {"$between": [0.5, 1.0]} instead. There's no $ne, $nin, $regex, or $exists; combine multiple top-level keys with AND, and use $in for OR within a field.Convenience shortcuts
search_notes exposes a few common metadata filters as top-level parameters:
| Shortcut | Equivalent filter |
|---|---|
tags=["security"] | {"tags": ["security"]} |
status="draft" | {"status": "draft"} |
note_types=["spec", "decision"] | type filter (any of) |
You can also write a tag filter directly in the query string:
# All equivalent
search_notes("tag:security")
search_notes(tags=["security"])
search_notes(metadata_filters={"tags": ["security"]})
Multiple tags in the shorthand mean all of them:
search_notes("tag:tier1,alpha") # comma-separated
search_notes("tag:tier1 alpha") # space-separated
# Both require BOTH tags to be present.
If a key appears in both a shortcut and metadata_filters, the explicit filter wins.
Combining with text and semantic search
Metadata filters compose with everything else search_notes does. The text query and the filters apply together (AND):
# OAuth-related notes that are still in progress
search_notes("OAuth", metadata_filters={"status": "in-progress"})
# Pure metadata — no text query
search_notes(metadata_filters={"type": "spec", "status": "active"})
# Text + tag shortcut + metadata
search_notes("auth", tags=["security"], metadata_filters={"priority": {"$in": ["high", "critical"]}})
See Semantic Search for how text and vector modes interact.
From the CLI
bm tool search-notes mirrors the same surface:
# Simple equality
bm tool search-notes "" --meta status=draft
# Multiple --meta flags AND together
bm tool search-notes "" --meta status=active --meta priority=high
# Full JSON filter
bm tool search-notes "" --filter '{"confidence": {"$between": [0.3, 0.8]}}'
# Convenience shortcuts
bm tool search-notes "auth" --tag security --tag oauth --status draft --type spec
See CLI Reference for every flag.
Worked examples
Given a few notes like this:
---
title: Auth Design
type: spec
status: in-progress
priority: high
tags: [security, oauth]
confidence: 0.85
---
---
title: Search Redesign
type: spec
status: planning
priority: medium
tags: [search, performance]
confidence: 0.6
---
| You want… | metadata_filters |
|---|---|
| All in-progress specs | {"status": "in-progress", "type": "spec"} |
| Specs with confidence above 0.7 | {"type": "spec", "confidence": {"$gt": 0.7}} |
| Anything high or critical priority | {"priority": {"$in": ["high", "critical"]}} |
| Specs in confidence range 0.5, 0.9 | {"type": "spec", "confidence": {"$between": [0.5, 0.9]}} |
Notes tagged both security AND oauth | {"tags": ["security", "oauth"]} |
| "OAuth" matches in still-open work | use search_notes("OAuth", metadata_filters={"status": "in-progress"}) |
Anything with nested schema.confidence ≥ 0.7 | {"schema.confidence": {"$gte": 0.7}} |
Type handling
- Strings match exactly (case-sensitive).
- Numbers — comparison operators detect numeric values automatically, so
"0.5"and0.5compare the same way. - Booleans are compared as the strings
"True"and"False". - Dates and datetimes are normalized to ISO strings.
- Missing fields never match — filtering on a key the note doesn't have returns no result for that note (not a null match).
Limits and caveats
- AND only at the top level. Combine multiple keys to AND; use
$infor OR within a single field. There's no top-level$or. - No full-text matching inside metadata. Use exact, range, or
$inmatching — for free-text search across note bodies, use text or semantic search. - Lists in filters must be non-empty.
{"tags": []}is rejected. $betweenrequires exactly two values.- No
$exists,$ne,$nin,$regex. They're not in the operator set.
Related
- Search Notes (MCP Tools Reference)
- Semantic Search — text, vector, and hybrid modes
- Schema System — make required frontmatter fields explicit
- Knowledge Format — the structure of a note
- CLI:
bm tool search-notes

