AI Assistant Guide
https://docs.basicmemory.com/llms.txt for an index or https://docs.basicmemory.com/llms-full.txt for the complete docs. See AI-Friendly Documentation for details.This guide explains how to use Basic Memory tools effectively when working with users through the Model Context Protocol.
Overview
Basic Memory creates a semantic knowledge graph from markdown files stored locally on the user's computer. Knowledge persists across sessions, enabling continuity in conversations and collaborative development.
Architecture:
- Plain-Text: Markdown files as source of truth
- SQLite Index: Fast search and navigation
- MCP Integration: Tools for AI interaction
- Semantic Graph: Observations and relations create connections
Your role: Help users build structured knowledge that outlasts any particular AI model or session. Focus on creating observations and relations that provide lasting value.
Project Management
All tools require explicit project specification. No implicit project context is maintained between calls.
Discovery and Selection
Start conversations by discovering available projects:
# List all projects
projects = await list_memory_projects()
# Response includes:
# - name
# - path
# - is_default
# - note_count
# - last_synced
Recommended workflow:
- Call
list_memory_projects()at conversation start - Identify active project from metadata
- Ask user which project to use
- Store choice for the session
- Pass project parameter to all tool calls
Cross-Project Operations
Some tools work across all projects when project parameter is omitted:
# Recent activity across all projects
activity = await recent_activity(timeframe="7d")
# Recent activity for specific project
activity = await recent_activity(timeframe="7d", project="main")
Tools supporting cross-project mode:
recent_activity()list_memory_projects()
Knowledge Graph Structure
The knowledge graph consists of three core elements: entities, observations, and relations.
Entities
Each markdown file represents an entity with:
- Title: Unique identifier
- Permalink: Auto-generated from title
- Frontmatter: YAML metadata (tags, type, dates)
- Observations: Categorized facts
- Relations: Links to other entities
Note types (set via the type frontmatter field):
note- General knowledge (default)person- People and contactsproject- Projects and initiativesmeeting- Meeting notesdecision- Documented decisionsspec- Technical specifications
Observations
Observations are categorized facts with optional tags.
Syntax: - [category] content #tag1 #tag2
Common categories:
[fact]- Objective information[idea]- Thoughts and concepts[decision]- Choices made[technique]- Methods and approaches[requirement]- Needs and constraints[problem]- Issues identified[solution]- Resolutions[insight]- Key realizations
Examples:
## Observations
- [decision] Use JWT tokens for authentication #security
- [technique] Hash passwords with bcrypt before storage #best-practice
- [requirement] Support OAuth 2.0 providers #auth
- [fact] Session timeout set to 24 hours #configuration
Relations
Relations are directional links between entities.
Syntax: - relation_type [[Target Entity]]
Common relation types:
relates_to- General connectionimplements- Implementation relationshiprequires- Dependencyextends- Enhancementpart_of- Hierarchical membershipcontrasts_with- Alternative approachleads_to- Sequential relationshipcaused_by- Causal relationship
Examples:
## Relations
- implements [[Authentication Spec v2]]
- requires [[User Database Schema]]
- extends [[Base Security Model]]
- part_of [[API Backend Services]]
- contrasts_with [[API Key Authentication]]
Forward References
Reference entities that don't exist yet - relations resolve automatically when targets are created:
# Create note with forward reference
await write_note(
title="API Implementation",
content="## Relations\n- implements [[API Specification]]",
directory="api",
project="main"
)
# Forward reference created
# Later, create referenced entity
await write_note(
title="API Specification",
content="# API Specification\n...",
directory="specs",
project="main"
)
# Forward reference automatically resolved
Core Tools
Writing Knowledge
Create new notes:
await write_note(
title="Topic",
content="# Topic\n## Observations\n- [category] fact\n## Relations\n- relates_to [[Other]]",
directory="notes",
project="main"
)
Parameters:
title(required) - Note titlecontent(required) - Markdown contentdirectory(required) - Destination foldertags(optional) - List of tagsnote_type(optional) - Note type (default:note)metadata(optional) - Extra frontmatter fieldsoverwrite(optional) - Defaultfalse. Must betrueto replace an existing noteproject(optional) - Usesdefault_projectfallback when configured
write_note is non-destructive by default. If a note already exists at the target path, the call returns an error instead of silently overwriting. This prevents accidental data loss.- To update an existing note incrementally, use
edit_note - To fully replace an existing note, pass
overwrite=True - Always search before writing to check whether the note already exists
Reading Knowledge
Read notes with knowledge graph context:
# By title
note = await read_note("Topic", project="main")
# By folder and title
note = await read_note("specs/Authentication System", project="main")
# By memory:// URL
note = await read_note("memory://specs/auth", project="main")
Response includes:
- Content
- Observations (categorized)
- Relations (typed, with targets)
- Metadata (dates, tags, type)
Searching
Find notes across the knowledge base:
results = await search_notes(
query="authentication",
project="main",
page_size=10
)
Filter by note type:
# Find all specs about authentication
specs = await search_notes(
query="authentication",
note_types=["spec"],
project="main"
)
# Find all meeting notes and decisions
results = await search_notes(
note_types=["meeting", "decision"],
project="main"
)
Filter by date:
recent = await search_notes(
query="api",
after_date="2025-01-01",
project="main"
)
Search by tags:
# tag: shorthand in the query string
results = await search_notes(query="tag:security", project="main")
results = await search_notes(query="tag:coffee AND tag:brewing", project="main")
# tags parameter shorthand
results = await search_notes(tags=["security"], project="main")
results = await search_notes(tags=["security", "oauth"], project="main")
Search by frontmatter metadata:
The query parameter is optional — you can search purely by frontmatter fields. Use metadata_filters for structured queries against any frontmatter field, or convenience shorthands for common fields.
# Find all notes with a specific status
results = await search_notes(
metadata_filters={"status": "in-progress"},
project="main"
)
# Find high-priority items
results = await search_notes(
metadata_filters={"priority": {"$in": ["high", "critical"]}},
project="main"
)
# Filter by team
results = await search_notes(
metadata_filters={"team": "backend"},
project="main"
)
# Combine text search with metadata filters
results = await search_notes(
query="authentication",
metadata_filters={"status": "draft"},
project="main"
)
# Convenience shorthands for common fields
results = await search_notes(status="draft", project="main")
results = await search_notes(tags=["security"], project="main")
Supported filter operators:
| Operator | Example | Meaning |
|---|---|---|
| exact match | {"status": "draft"} | Field equals value |
$in | {"priority": {"$in": ["high", "critical"]}} | Field is one of the values |
$contains | {"tags": {"$contains": "security"}} | Array field contains value |
Semantic search:
Semantic search is enabled by default. hybrid mode (combining keyword and meaning-based retrieval) is the default search type — no opt-in needed. You can also use pure semantic search:
# Pure semantic similarity — finds related concepts even with different wording
results = await search_notes(
query="how to speed up the application",
search_type="vector",
project="main"
)
# Hybrid — combines keyword precision with semantic recall (recommended for most queries)
results = await search_notes(
query="authentication security",
search_type="hybrid",
project="main"
)
# Adjust similarity threshold for tighter or looser results
results = await search_notes(
query="error handling patterns",
search_type="hybrid",
min_similarity=0.7,
project="main"
)
When to use which search mode:
hybrid(default) — General-purpose. Start here for most queries.text— You know the exact terms. Best for names, identifiers, boolean queries.vector— Conceptual discovery. The note may not contain your exact words.
Building Context
Navigate the knowledge graph. build_context defaults to JSON output (unlike other tools which default to text).
context = await build_context(
url="memory://specs/auth",
project="main",
depth=2,
timeframe="1 week"
)
Parameters:
url(required) - memory:// URLdepth(optional) - Traversal depth (default: 1)timeframe(optional) - Time window (e.g., "7d", "1 week")output_format(optional) - Default"json". Pass"text"for human-readable outputproject(optional) - Usesdefault_projectfallback when configured
Depth control:
depth=1- Immediate connections onlydepth=2- Two levels of relationsdepth=3+- Comprehensive (may be slow)
Editing Notes
Edit existing notes incrementally:
# Append content
await edit_note(
identifier="Topic",
operation="append",
content="\n## New Section\nContent here",
project="main"
)
# Prepend content
await edit_note(
identifier="Topic",
operation="prepend",
content="## Update\nImportant note\n\n",
project="main"
)
# Find and replace
await edit_note(
identifier="Topic",
operation="find_replace",
find_text="old text",
content="new text",
expected_replacements=1,
project="main"
)
# Replace section
await edit_note(
identifier="Topic",
operation="replace_section",
section="## Status",
content="## Status\nUpdated status here",
project="main"
)
Operations:
append- Add to endprepend- Add to beginningfind_replace- Replace specific textreplace_section- Replace markdown section
Moving Notes
Organize notes by moving them between folders:
await move_note(
identifier="Note Title",
destination_path="archive/note-title.md",
project="main"
)
Behavior:
- Folders created automatically
- Database updated automatically
- Relations preserved
- Both
.mdextension and without work
memory:// URL Format
Basic Memory uses special URLs to reference entities. Generated permalinks include the project slug by default, so most URLs you encounter will be project-prefixed.
By title:
memory://title
By folder and title:
memory://folder/title
Project-prefixed (default format):
memory://project-slug/folder/title
By permalink:
memory://permalink
All in folder:
memory://folder/*
Underscores converted to hyphens automatically - memory://my_note finds entity with permalink my-note.
Recording Conversations
Capture conversations to enable long-term context and knowledge accumulation.
Permission and Transparency
Always ask before recording:
AI: "Would you like me to save our discussion about API authentication
to Basic Memory? This helps us continue this conversation later."
User: "Yes, please"
AI: [Saves to Basic Memory]
"I've saved our discussion as 'API Authentication Discussion'."
Principles:
- Ask permission before saving
- Confirm after saving
- Explain what was saved
- Describe how it helps future conversations
What to Record
Good candidates:
- Decisions and Rationales
- Important Discoveries
- Action Items and Plans
- Connected Topics
Recording Patterns
Conversation summary:
await write_note(
title=f"Conversation: {topic} - {date}",
content=f"""# Conversation: {topic}
## Summary
{brief_summary}
## Key Points
{key_points}
## Observations
{categorized_observations}
## Relations
{relevant_relations}
""",
directory="conversations",
tags=["conversation"],
project="main"
)
Decision record:
await write_note(
title=f"Decision: {decision_title}",
content=f"""# Decision: {decision_title}
## Context
{why_decision_needed}
## Decision
{what_was_decided}
## Rationale
{reasoning}
## Relations
{related_entities}
""",
directory="decisions",
note_type="decision",
project="main"
)
Schemas
Schemas define what a "good" note of a particular type should contain. They formalize observation categories and relation types so you can validate notes against a consistent structure. Schemas are optional — you can use Basic Memory without ever defining one — but they help maintain consistency as a knowledge base grows and give you a guide for creating well-structured notes.
A schema is just a regular note with type: schema in frontmatter:
---
title: Person
type: schema
entity: person
version: 1
schema:
name: string, full legal name
role?: string, current job title
works_at?: Organization, primary employer
expertise?(array): string, areas of knowledge
settings:
validation: warn
---
Fields without ? are required. Fields with ? are optional. Capitalized types like Organization indicate entity references (wiki-link relations). Array fields expect multiple observations of the same category.
Schema tools
Three tools are available for working with schemas:
schema_infer— Analyze existing notes of a type and suggest a schema based on common patterns. Use this when the user wants to create a schema from their existing notes.schema_validate— Check notes against their schema. Passnote_typeto validate all notes of a type, oridentifierto validate a single note.schema_diff— Compare a schema against actual note usage to detect drift. Shows which fields are defined but unused, and which undeclared fields appear in practice.
Creating notes with schemas
When a schema exists for a note type, use it as a creation guide:
- Look up the schema before creating a note of a known type:
# Check what fields a "person" note should have
schema = await schema_infer(note_type="person")
- Create the note following the schema's field definitions:
await write_note(
title="Grace Hopper",
content="""# Grace Hopper
## Observations
- [name] Grace Hopper
- [role] Computer Scientist and Navy Admiral
- [expertise] Compiler design
- [expertise] Programming languages
## Relations
- works_at [[US Navy]]
- works_at [[Harvard University]]
""",
directory="people",
note_type="person",
metadata={"status": "historical"},
project="main"
)
- Validate after creation to confirm conformance:
result = await schema_validate(identifier="people/grace-hopper", project="main")
Discovering and evolving schemas
Users may ask you to help create or refine schemas. The typical workflow:
# 1. Infer a schema from existing notes
schema = await schema_infer(note_type="meeting", project="main")
# 2. Review the suggested schema with the user, then write it
await write_note(
title="Meeting",
content="...", # schema frontmatter + description
directory="schemas",
note_type="schema",
project="main"
)
# 3. Validate existing notes against the new schema
results = await schema_validate(note_type="meeting", project="main")
# 4. Later, check for drift as note patterns evolve
drift = await schema_diff(note_type="meeting", project="main")
Using note_type and metadata
The note_type parameter sets the type frontmatter field, which controls schema resolution and search filtering. Use it whenever creating notes of a specific type:
await write_note(
title="Sprint Planning 2026-02-15",
content="...",
directory="meetings",
note_type="meeting",
metadata={"sprint": 12, "team": "backend"},
project="main"
)
The metadata parameter accepts any key-value pairs that get merged into the note's frontmatter. Use it for custom fields like status, priority, due_date, or team — these become searchable via metadata_filters in search_notes.
See the full Schema System guide for syntax details and validation modes.
Best Practices
1. Project Discovery
Always start with discovery:
# First action in conversation
projects = await list_memory_projects()
# Ask user which to use
# Store for session
# Use consistently
2. Rich Knowledge Graphs
Every note should have:
- Clear, descriptive title
- 3-5 observations minimum
- 2-3 relations minimum
- Appropriate categories and tags
Good structure:
---
title: Clear Descriptive Title
tags: [relevant, tags]
type: note
---
# Title
## Context
Brief background
## Observations
- [category] Specific fact #tag1 #tag2
- [category] Another fact #tag3
- [category] Third fact #tag4
## Relations
- relation_type [[Related Entity 1]]
- relation_type [[Related Entity 2]]
3. Search Before Creating
Always search first to avoid duplicates. write_note returns an error if a note already exists at the target path, so searching first prevents failed writes and lets you choose the right tool:
# Before writing new note
existing = await search_notes(
query="topic name",
project="main"
)
if existing["total"] > 0:
# Update existing with edit_note
await edit_note(
identifier=existing["results"][0]["permalink"],
operation="append",
content=new_information,
project="main"
)
else:
# Create new
await write_note(...)
4. Exact Entity Titles in Relations
Use exact titles when creating relations:
# Search for exact title
results = await search_notes(query="Authentication System", project="main")
exact_title = results["results"][0]["title"]
# Use in relation
content = f"## Relations\n- relates_to [[{exact_title}]]"
5. Meaningful Categories and Relations
Use semantic categories:
[decision]for choices made[fact]for objective information[technique]for methods[requirement]for needs[insight]for realizations
Use specific relation types:
implementsfor implementationrequiresfor dependenciespart_offor hierarchyextendsfor enhancementcontrasts_withfor alternatives
Avoid generic: Don't overuse relates_to - be specific about relationships.
6. Progressive Elaboration
Build knowledge over time:
# Session 1: Create foundation
await write_note(
title="Topic",
content="Basic structure with initial observations",
directory="notes",
project="main"
)
# Session 2: Add details
await edit_note(
identifier="Topic",
operation="append",
content="Additional observations",
project="main"
)
# Session 3: Add relations
await edit_note(
identifier="Topic",
operation="append",
content="Relations to related topics",
project="main"
)
7. Consistent Organization
Folder structure:
specs/- Specificationsdecisions/- Decision recordsmeetings/- Meeting notesconversations/- AI conversationsimplementations/- Code/implementationsdocs/- Documentation
8. Confirm Destructive Actions
Always ask before calling delete_note or move_note on folders.
Error Handling
Note Already Exists
The most common error. write_note returns an error when a note already exists at the target path. This is intentional — it prevents accidental overwrites.
Solution:
# Option 1: Update the existing note incrementally
await edit_note(
identifier="existing-note-path",
operation="append",
content="New content to add",
project="main"
)
# Option 2: Explicitly replace the note
await write_note(
title="Topic",
content="Completely new content",
directory="notes",
overwrite=True,
project="main"
)
Missing Project Parameter
Solution:
try:
results = await search_notes(query="test")
except:
projects = await list_memory_projects()
# Ask user which to use
results = await search_notes(query="test", project="main")
Entity Not Found
Solution:
try:
note = await read_note("Nonexistent Note", project="main")
except:
results = await search_notes(query="Note", project="main")
# Suggest alternatives to user
Forward References
Not an error: Forward references resolve automatically when targets are created. No action needed.
Tool Reference
Content Management
| Tool | Purpose |
|---|---|
write_note | Create new markdown notes (errors on existing path unless overwrite=True) |
read_note | Read notes with knowledge graph context |
edit_note | Edit notes incrementally |
move_note | Move notes to new locations |
delete_note | Delete notes from knowledge base |
view_note | View notes as formatted artifacts |
read_content | Read raw file content |
Knowledge Graph Navigation
| Tool | Purpose |
|---|---|
build_context | Navigate knowledge graph |
recent_activity | Get recent changes |
list_directory | Browse directory contents |
Search & Discovery
| Tool | Purpose |
|---|---|
search_notes | Search across knowledge base (text, vector, hybrid modes) |
Schema Tools
| Tool | Purpose |
|---|---|
schema_validate | Validate notes against schema definitions |
schema_infer | Analyze notes and suggest schema patterns |
schema_diff | Detect drift between schema and actual usage |
Project Management
| Tool | Purpose |
|---|---|
list_memory_projects | List all available projects |
create_memory_project | Create new project |
delete_project | Delete project from configuration |
list_workspaces | List available cloud workspaces |
Visualization
| Tool | Purpose |
|---|---|
canvas | Create Obsidian canvas |

