5.1¶
This release adds one opt-in feature and fixes what the 5.0 surface got
wrong. With MARKDOWN_VAULT_MCP_REPORT_UNRESOLVED_LINKS=true, write,
edit and append name the links they just added that resolve to no
note. A configured embedding provider now turns on search by meaning
without MARKDOWN_VAULT_MCP_EMBEDDINGS_PATH, and the change-tracking file
no longer defaults into the vault. Conventions files reach the model whole
up to 32,768 characters, and every shipped prompt reads them before it
picks a folder. The documentation site is reorganised by what a reader
is trying to do, and its reference pages are generated from the code.
Read Upgrading if you set an embedding provider.
Upgrading¶
- If you set
MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDERbut notMARKDOWN_VAULT_MCP_EMBEDDINGS_PATH: in 5.0 the provider was ignored and the server searched by keyword only. Now it embeds the vault with that provider at start. That includes a Claude Desktop bundle or Claude Code plugin install where you filled in the embedding provider field, and a plaindocker runof the image. WithMARKDOWN_VAULT_MCP_INDEX_PATHset, the vectors are stored beside the index and the vault is embedded once. Without it, the vectors are held in memory and the whole vault is embedded again at every start, which OpenAI and Voyage bill each time. SetMARKDOWN_VAULT_MCP_INDEX_PATHto keep them, or clear the provider to stay on keyword search (#1708). The shippedcompose.ymlsets both paths and is not affected. - If you never set
MARKDOWN_VAULT_MCP_STATE_PATH: nothing is required. WithMARKDOWN_VAULT_MCP_INDEX_PATHset, the first start copies the change-tracking file out of the vault's.markdown_vault_mcp/folder to its new place beside the index. Without it, the state is held in memory and the old folder is simply no longer used. Either way the server logslegacy_state_file_unusedat each start while the folder is there, and the folder is then safe to delete. To keep the old location instead, setMARKDOWN_VAULT_MCP_STATE_PATHto it (#1693). - If you run the shipped
compose.yml: check that.envstill setsMARKDOWN_VAULT_MCP_SOURCE_DIR. In 5.0 Compose refused to start without it; it now starts and serves an empty./vaultfolder besidecompose.yml(#1707).
Clients: nothing to do beyond reconnecting. No tool, prompt or argument
was renamed or removed. A client picks up the changed prompt text and tool
descriptions when it reconnects after the restart. On a read-only server the example zettelkasten prompt is no longer
listed.
State: kept. The text index is not rebuilt: the rules that turn notes
into index rows did not change. The change-tracking file moves beside the
index (step 2), so under Compose it is now
/data/state/index.db.state.json on the state-data volume. A deployment
that starts embedding under step 1 also rebuilds its keyword index once in
the background, because the recorded embedding model changes.
Security posture: wider in one case, narrower in another. A deployment
in step 1 now sends its notes to the configured embedding provider, which
for OpenAI or Voyage means a third party. A server, read-only ones
included, no longer writes a state folder into the vault by default. The
image's locked urllib3 and pyjwt move past newly published advisories
(#1687);
the server makes its own requests through httpx and does not verify
tokens with pyjwt.
Links a write adds that do not resolve¶
A link to a note that does not exist is easy to write and only shows up
later, when someone runs get_broken_links. This release lets write,
edit and append report such links in the same response that wrote
them.
The request came from @mikebronner, who
also wrote the change. Their
issue
puts it this way: "The writer only finds out later, if someone runs
get_broken_links", and "This matters most when an LLM writes the
note." From "one vault served by this server" it reports that "On
2026-09-30, 19 of the 29 cross-references written that day were broken at
write time." It asks
that "A vault that does not want this sees no change in behaviour."
Turn it on with MARKDOWN_VAULT_MCP_REPORT_UNRESOLVED_LINKS=true. It is
off by default, and then the three responses are exactly as in 5.0. When
it is on, each response carries unresolved_links. It lists the target,
as written, of each link the call added that get_broken_links would
report, once each: inline links first, then reference links, then
wikilinks, each kind in the order it appears. Links the note already held
are left out, so an old broken link is not repeated on every edit. [] means
every new link resolves. null means the check could not run: the
first index build is still going, or the refresh failed or timed out. The write
still succeeds and the server logs unresolved_link_check_skipped.
Each call waits for the index to take in the write before it answers
(#1727).
Python callers pass report_unresolved_links=True to write, edit or
append on the writer facet. The setting is in the
configuration reference and the field in
the writing tools reference.
Search by meaning from the provider alone¶
In 5.0, setting an embedding provider did nothing unless
MARKDOWN_VAULT_MCP_EMBEDDINGS_PATH was also set: "the server runs
keyword-only search and says nothing"
(#1708).
Now an explicit MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDER is enough. The
vectors go to MARKDOWN_VAULT_MCP_EMBEDDINGS_PATH when it is set, beside
an on-disk index otherwise (index-db-embeddings.npy and .json beside
index.db), and into memory when there is no index path. A provider that
fails to load without a path set logs embedding_provider_load_failed and
leaves the server on keyword search. Detecting the provider from the
environment still needs MARKDOWN_VAULT_MCP_EMBEDDINGS_PATH, so an
install that merely has a backend available does not start embedding
(#1731).
The embeddings guide covers the providers.
The search command line, run without MARKDOWN_VAULT_MCP_INDEX_PATH,
printed an empty result "for a word that is in the vault"
(#1691).
It now builds an in-memory index before it queries. With no index path,
a --mode semantic or hybrid query embeds the whole vault for that one
query
(#1726,
command line reference).
Against an on-disk index, which a running server may share, search now
writes nothing. In 5.0 it saved its own shell's ranking weights into the
index, so a shell without MARKDOWN_VAULT_MCP_FTS_WEIGHTS reset a running
server's ranking
(#1758).
A --mode semantic or hybrid search from a shell with another embedding
model rebuilt and saved over the server's stored vectors
(#1734).
It now ranks with the weights stored in the index. Stored vectors that do
not fit the shell's embedding settings, or are damaged, end the search
with an error and stay as they are. With no file at
MARKDOWN_VAULT_MCP_INDEX_PATH, it reports the missing index and exits 1
instead of creating an empty one
(#1757,
#1759).
Nothing written into the vault by default¶
The change-tracking file records each note's content hash, so a restart
re-reads only the notes that changed. In 5.0 it defaulted to
.markdown_vault_mcp/state.json inside the vault, in every mode: "With
MARKDOWN_VAULT_MCP_READ_ONLY=true the server still writes into the
vault"
(#1693).
It now defaults beside the index as index.db.state.json, or into memory
when there is no index path, where it loses nothing because an in-memory
index is rebuilt at every start anyway. The file format is unchanged, and
with an index path the old file is copied over on the first start
(Upgrading step 2;
#1721).
What an upgrade of this server touches describes
where each kind of state lives.
Conventions reach the model whole¶
A vault's conventions file tells the model where notes go and how to link
them. In 5.0, get_conventions cut each file at 4,000 characters, about
60 lines, and so did the conventions key in write, edit, fetch and
get_context results
(#1722).
The limit is now 32,768 characters per file. A longer file is cut there
with a note naming the file to read for the rest, and the server logs
conventions_truncated at WARNING once per file. The limit is fixed, not
a setting
(#1723).
The shipped prompts now read those conventions. In 5.0, "Only two of the
seventeen shipped prompts call that tool"; the others named fixed folders
such as 0-Inbox and Research/
(#1709).
Every built-in prompt, every prompt in the example packs and
create_from_template now call get_conventions first. A prompt that
writes takes its folder from the conventions and asks you when they name
none, so research no longer defaults to Research/. The example
zettelkasten prompt edits notes and is now tagged write, so a
read-only server no longer lists it
(#1717,
#1732). The
prompts reference lists them.
The documentation¶
The site is now organised by what a reader is trying to do: Get started,
Deploy, Use, Reference and Upgrade, with a security model page at the top
and the maintainer pages under Contribute. The epic asked that "Each
question has one page that answers it"
(#1665).
Every page that moved redirects from its old address. The tools, prompts,
resources, command line and configuration references are generated from
the code, and a check in CI fails when they fall behind it. llms.txt takes
its sections from the site's navigation, so a page no longer goes missing
from it (#1668,
#1690). The
Does it fit?
block in the README states what the server assumes, one vault per server, Markdown only
and embeddings held in memory, and what to use when those do not hold
(#1378).
What an upgrade of this server touches is new too.
These published instructions were wrong in 5.0 and are corrected:
- The two Claude Desktop setups labelled read-only did not set
MARKDOWN_VAULT_MCP_READ_ONLY, so they ran a server that could write, edit and delete notes (#1660). - The Python examples failed as written: the write example raised
ReadOnlyError, and the quick start searched before building the index and got[](#1661). - The
readreference left outetag, which the write tools take asif_match, andeditpromised line numbers thatreadnever shows (#1662). get_index_statuswas documented without most of the fields other pages told readers to watch,embeddings_statusreportedavailable: truebefore the build finished, and nothing said what a finished build looks like (#1663).- The 5.0 upgrade notes put server state on the wrong volume and left
MARKDOWN_VAULT_MCP_SOURCE_DIRand client action unresolved; the 5.0 page now carries the corrected section (#1664). - Install pages disagreed on which package runs the server, and the
unquoted
[all]extra failed in zsh (#1667). - Guides linked to
examples/and design pages the site does not serve (#1669).
Smaller fixes¶
- An upload to a note link whose body is not valid UTF-8 answered 500. It now answers 415, and a body the vault refuses for another reason answers 422, with the link still usable for a corrected upload (#1718, transfer links).
- Where OKF is on, the
create_download_linkdescription now names theokf-bundleandokf-bundle:<folder>refs, so a model can find the bundle download (#1720). - The help text for
MARKDOWN_VAULT_MCP_GIT_COMMIT_NAME_CLAIMandMARKDOWN_VAULT_MCP_GIT_COMMIT_EMAIL_CLAIMsaid the claim overrides the committer. It sets the commit author; the committer staysMARKDOWN_VAULT_MCP_GIT_COMMIT_NAME. Only the text changed (#1716). get_server_infogains aprotocolblock with the MCP versions the server supports, the protocol version in use and the client's name and version, and each request's start log line names the protocol version and client too, from fastmcp-pvl-core 10.2.0 (pvl-core#420).