memory-metadata-search
Structured metadata search for Basic Memory: query notes by custom frontmatter fields using equality, range, array, and nested filters. Use when finding notes by status, priority, confidence, or any custom YAML field rather than free-text content.
By basicmachines-co · 788 installs
npx skills add basicmachines-co/basic-memory --skill memory-metadata-search
Source repository · Upstream listing
Memory Metadata Search
Find notes by their structured frontmatter fields instead of (or in addition to) free text content. Any custom YAML key in a note's frontmatter beyond the standard set ( title , type , tags , permalink , schema ) is automatically indexed as entity metadata and becomes queryable.
Project scope
If the project is unknown, discover it with bm project(command="list") .
Pass a nonempty project on every note, folder, context and schema call, even
with one project or a qualified memory:// URL. Use the chosen project's name,
workspace/project , or external ID; <project below is a placeholder for it.
Recent activity also requires project . Search accepts project for one
project, projects for selected projects, or all projects=True explicitly.
Discovery and bundled manuals need no project. Keep the same scope when following
returned paths, paginating, or recovering from a refusal.
When to Use
Filtering by status or priority — find all notes with status: draft or priority: high
Querying custom fields — any frontmatter key you invent is searchable
Range queries — find notes with confidence 0.7 or score between 0.3 and 0.8
Combining text + metadata — narrow a text search with structured constraints
Tag based filtering — find notes tagged with specific frontmatter tags
Schema aware queries — filter by nested schema fields using dot notation
The Tool
Use bm search for ranked results or bm find for file discovery with selected fields. Pass frontmatter conditions via filters , including tags and status. Omit query (or pass None ) for filter only searches.
Filter Syntax
Filters are a JSON dictionary. Each key targets a frontmatter field; the value specifies the match condition. Multiple keys combine with AND logic.
Equality
Array Contains (all listed values must be present)
$in (match any value in list)
Comparisons ( $gt , $gte , $lt , $lte )
Numeric values use numeric comparison; strings use lexicographic comparison.
$between (inclusive range)
Null (field missing or explicitly null)
Matches notes with no owner key and notes whose owner is explicitly null.
Null works only as a plain equality value — inside $in , $between , an
array contains list, or a comparison it is rejected, because those compare
against the value and a comparison with null is never true.
Nested Access (dot notation)
Quick Reference
Operator Syntax Example
Equality {"field": "value"} {"status": "active"}
Is null {"field": null} {"owner": null}
Array contains {"field": ["a", "b"]} {"tags": ["security", "oauth"]}
$in {"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": [lo, hi]}} {"score": {"$between": [0.3, 0.8]}}
$contains {"field": {"$contains": value}} {"tags": {"$contains": "security"}}
$exists {"field": {"$exists": true}} {"owner": {"$exists": false}}
Nested {"a.b": "value"} {"schema.version": "2"}
Rules:
Keys must match [A Za z0 9 ]+ (dots separate nesting levels)
Several operators on one field AND together: {"started": {"$gte": "2026 01 01", "$lt": "2026 02 01"}}
$in and array contains require non empty lists
$between requires exactly [min, max]
null is an is null match and only valid as a plain equality value
Comparison and $between bounds must be finite numbers — a magnitude no float
can hold (a 400 digit integer, which JSON keeps as an ordinary int ) is
refused rather than compared against an infinite bound
Metadata filters match Markdown notes only — indexed PDFs, images and other
regular files carry no frontmatter and are never hits, not even for null
The leading $ is optional ( gte works), but write it for clarity.
Frontmatter fields say when a note was filed or reviewed. To ask what a note
claims was true on a date, use observation qualifiers (see memory notes )
and bm search(valid at="2026 07 01", project="<project ") or
bm search(valid overlaps="[2026 07 01,2026 08 01)", project="<project ") .
Using bm search with Metadata
Pass filters to bm search . Omit query for filter only searches, or combine text and filters together.
Use one filters object for all frontmatter conditions; its keys combine with AND.
Tag Search Shorthand
The tag: prefix in a query converts to a tag filter automatically:
Example: Custom Frontmatter in Practice
A note with custom fields:
Queries that find it:
Guidelines
Use metadata search for structured queries. If you're looking for notes by a known field value (status, priority, type), metadata filters are more precise than text search.
Use text search for content queries. If you're looking for notes about something, text search is better. Combine both when you need precision.
Custom fields are free. Any YAML key you put in frontmatter becomes queryable — no schema or configuration required.
Multiple filters are AND. {"status": "active", "priority": "high"} requires both conditions.
Omit query for filter only searches. bm search(filters={"status": "active"}, project="<project ") works without a text query.
Dot notation for nesting. Access nested YAML structures with dots: {"schema.version": "2"} queries the version key inside a schema object.
Use the same filters for file discovery. bm find(filters={"status": "active"}, fields=["title", "priority"], project="<project ") returns selected frontmatter values without reading each note.