Basic Memory
What's New

Upgrade to v0.24

Breaking changes, migrations, and required actions when upgrading Basic Memory from v0.23 to v0.24.

v0.24 applies its database migrations automatically, and most local setups need no manual action. Install v0.24.1 or later: v0.24.0 can fail on a fresh install with a FastAPI import error (see Troubleshooting). MCP clients, scripts, and cloud sync workflows do need a review. Work through the sections below before you rely on existing automation.

Update MCP clients

Refused writes are tool errors

A refused write_note, edit_note, delete_note, or move_note now returns an MCP tool error (isError: true) instead of an ordinary result. That includes:

  • write_note to an existing note without overwrite
  • A revision conflict on expected_checksum (NOTE_REVISION_CONFLICT)
  • A note that moved (NOTE_PATH_CONFLICT)
  • A directory outside the project
  • A concurrent-edit conflict on edit_note
  • A refusal from a read-only API key or a locked note

Nothing is written in any of these cases. In JSON mode the error message carries the same payload as before (action: "conflict", error: "NOTE_ALREADY_EXISTS", and so on). Clients that parsed the result text for "Edit Failed" or "Move Failed" should check isError instead. Retry logic should treat a refused append as not written: it never is.

Six more tools are visible

The POSIX read tools cat, grep, ls, find, tail, and man are on by default, so clients see six more tools, and the server instructions describe projects as mount points. To hide them:

bm config set enable_posix_tools false

Other MCP contract changes

  • edit_note(operation="replace_section") fails when the exact heading isn't in the note, instead of appending a duplicate section. Give headings in full (## Open items #urgent). Use append to add a new section on purpose
  • build_context(compact=True) no longer loads observations, and compact JSON no longer has an observations key. A memory:// title URL that matches more than one note is refused. Pass the permalink instead
  • Boolean searches (AND, OR, NOT) without an explicit search_type now run as full-text, so NOT excludes a term found anywhere in the note
  • read_content resolves only exact paths, permalinks, titles, and ids. A miss is "Resource not found" rather than a fuzzy match on another file
  • No session project. A call without project always uses the default project, locally and in the cloud. write_note and edit_note now end with [Project: 'name']
  • delete_project on a cloud-routed project always deletes the project's cloud files. delete_notes applies only to local projects
  • read_content images. Only PNG, JPEG, and WebP images are returned. GIF, TIFF, BMP, and other image files return "Unsupported image format" instead of a JPEG. Text, JSON, and other binary files are unchanged
  • Traversal limits. build_context and recent_activity accept depth from 1 to 5 (default 1) and refuse larger values with an error naming the limit. The /memory/recent API caps page_size and max_related at 100 and returns 422 beyond that

Update CLI automation

  • Missing notes exit 1. Reading a note that doesn't exist exits with status 1 in every output mode, and JSON reads carry error: "NOTE_NOT_FOUND". Scripts that checked for null fields with exit status 0 must check the exit status
  • bm import document resolves a relative path against the project root, not the current directory, matching bm ls and bm cat
  • bm project add now indexes a local project before it returns. Pass --no-wait to keep the old behavior
  • bm project add --cloud --visibility private is refused. Cloud projects are workspace or shared

bm project remove on cloud projects

bm project remove on a cloud-routed project now always deletes the project's cloud files. There is no longer a way to keep them. The files can be recovered only from a cloud snapshot (bm cloud snapshot list). The command asks for confirmation, which --yes skips.

It also no longer deletes the local sync directory as a side effect of --delete-notes. Pass the new --delete-local-files flag for that. Local projects behave as before.

Move off bm cloud sync and bisync

bm cloud sync, bm cloud bisync, and bm cloud bisync-reset are deprecated and will be removed in a future release. They still work on Personal workspaces, print a deprecation notice on every run, and exit with that notice on Team workspaces. Switch to the additive push/pull workflow:

bm cloud pull --name my-project   # bring cloud changes down
bm cloud push --name my-project   # send local changes up

Push and pull never delete files on the destination, and --on-conflict controls what happens when a file differs on both sides. See Cloud Sync.

Update configuration

These settings are retired. An existing value is ignored rather than rejected, so nothing breaks, but you should remove them:

SettingWhat changed
semantic_search_enabled / BASIC_MEMORY_SEMANTIC_SEARCH_ENABLEDSemantic search is always on. Installs that had turned it off now download the embedding model and build vectors
disable_permalinks / BASIC_MEMORY_DISABLE_PERMALINKSMarkdown notes always get a permalink. Notes indexed without one are backfilled during ordinary scans, which writes permalink into their frontmatter

Other configuration changes:

  • Environment overrides stay out of config.json. A one-off BASIC_MEMORY_* variable used to become permanent the next time anything saved config. A save now keeps the file's own value. Commands that name a setting (bm config set, bm project default, and so on) still save it
  • BASIC_MEMORY_PROJECT_ROOT refuses project names that contain / (such as Research/2026), so each project under a shared root stays one top-level directory
  • EmbeddingStatus gains vector_runtime_available. semantic_search_enabled is still emitted as a deprecated alias of it, so older CLIs keep working against upgraded servers
Where sqlite-vec can't load (the python.org Python build on macOS), search still falls back to keyword-only. A vector or hybrid query then says semantic search is unavailable and points at the log.

Check the HTTP transport

basic-memory mcp --transport streamable-http (or sse) now binds to 127.0.0.1 by default instead of 0.0.0.0. The unauthenticated HTTP server is no longer reachable from the network unless you ask for it:

basic-memory mcp --transport streamable-http --host 0.0.0.0

A non-loopback --host prints a warning. On a loopback bind, both HTTP transports reject requests whose Host or Origin header is foreign (DNS-rebinding protection). Set FASTMCP_HTTP_ALLOWED_HOSTS and FASTMCP_HTTP_ALLOWED_ORIGINS to allow more. The official Docker image already passes --host 0.0.0.0 and needs no change. See Docker.

Let migrations finish

About a dozen revisions run automatically on first start. The ones you might notice:

  • Section index. A new note_section table backs heading-level cat reads
  • Time index. A new table indexes observation time qualifiers for valid_at and valid_overlaps filters
  • Script n-grams. Full-text search gains unigrams and bigrams for CJK and other unsegmented scripts. Existing notes gain this recall after a reindex (bm reindex --search)
  • Project-partitioned vectors. The sqlite-vec table gains a project_id partition key. Existing vectors are carried over without re-embedding
  • Relations survive deletes. relation.to_id becomes ON DELETE SET NULL. Relations lost to the old cascade stay lost. Re-index the affected notes to bring them back as unresolved
  • Search uniqueness by row kind, readiness timestamps (last_indexed_at), deferred-embedding tracking, and a sync_checksum that stops one-way sync tools from triggering endless re-indexing

Markdown files remain the source of truth and the index is rebuildable. If you want a pre-migration snapshot, back up memory.db before upgrading.

Postgres deployments

On Postgres, vector nearest-neighbour queries now run on the HNSW index with an iterative scan, which requires pgvector 0.8 or later. An older extension is reported as a dependency error instead of quietly returning short result pages. Vector storage is created when the database is initialized, never while serving.

Continue setup