1.x¶
March 9 to May 3, 2026, from the first tagged release to v1.28.0.
Reconstructed after the fact, and thinner than the pages after it
This page is a best-effort backfill written in August 2026 (#1058). It is not contemporaneous, and the record it was reconstructed from is thin: most of this period predates the pull-request discipline that links every change to an issue. Where the evidence supports only a factual statement, this page makes only a factual statement. Where it supports nothing, the page says so rather than guessing. Do not use it as upgrade guidance for a specific 1.x version.
What this page covers¶
One page for the whole 1.x line: 42 stable releases across 27 minor versions, 801 commits, in eight weeks. Writing 27 separate pages would have produced 27 thin ones, and nobody upgrades from v1.7.0 today. What is useful now is the shape of the arc: the server's capabilities at the start of the line, its capabilities at the end, and the places where the version numbers skip.
Individual releases in this range have no page of their own and will not get one.
This page is final. The 1.x line closed at v1.28.0 and will receive no further releases, so no patch section will ever be appended below.
Evidence, and its limits¶
The measurable gap between this page and the ones after it is how often a commit in the range carries a link to an issue explaining it. Counted per stable-to-stable range and recorded in #1058:
| Range | Ranges counted | Commits with a linked issue |
|---|---|---|
| v1.0.0 to v1.28.0 | 41 | mostly 0% to 40%, seven ranges at 0% |
| v1.28.0 to v3.0.0 | 1 | 78% |
| v3.0.0 to v3.1.0 | 5 | 50% to 100% |
Those percentages are the ones recorded in the issue, counted by its own rule.
Recounting the v1.28.0 to v3.0.0 range against one rule stated in full, that
the commit message carries a closing keyword (closes, fixes, resolves,
refs) next to an issue number, gives 76%: 199 of the 261 commits. Two rules,
one body of evidence. The figures differ because they count different things,
and either one leaves the 1.x ranges far behind.
Seven ranges in this period contain no linked issue at all:
v1.8.0..v1.8.1 (5 commits), v1.18.0..v1.18.1 (9), v1.19.0..v1.19.1 (4),
v1.19.1..v1.20.0 (8), v1.23.1..v1.25.0 (10), v1.25.0..v1.27.0 (8) and
v1.27.0..v1.27.1 (42). The largest of them, v1.27.0..v1.27.1, is accounted
for in
the April section
below, where the maintainer names what it was. For the other six, nothing
beyond the code itself records what the change was for.
The narrative below is anchored on artifacts that can be checked rather than on inferred motives: the published tool reference at each tag, the configuration reference at each tag, and the issues that do exist. Each claim links what it rests on.
The arc¶
A working server, then a deployable one (March 9 to 13)¶
The first tagged release exposed 13 tools: search, read, write, edit,
delete, rename, the three list_* calls, reindex, stats,
build_embeddings and embeddings_status. Hybrid full-text and semantic
search, frontmatter-aware indexing and git-backed writes were all there on day
one.
The next four days were about running it somewhere other than a laptop. Git
writes moved behind a GitWriteStrategy with deferred push
(PR #64), a
streamable HTTP transport arrived for container deployments
(PR #70), and the
server moved to FastMCP 3
(PR #72).
Authentication came in two forms, OIDC through a proxy
(PR #76) and a
simple bearer token
(PR #167), with a
multi-auth mode accepting either
(PR #237) shortly
after.
Two changes from this stretch still shape how the server behaves. Optimistic
concurrency landed: reads return an etag and writes accept if_match
(PR #125,
PR #126). And the
default embedding backend changed twice for the same reason. First from
sentence-transformers to fastembed
(PR #144), then to
a smaller default model, in both cases to stop the container running out of
memory
(#306).
The project also became findable in this window: GitHub topics, PyPI metadata
and a server.json manifest for the official MCP registry, which the issue
behind it calls "the single highest-leverage action" because downstream
aggregators consume it automatically
(#112).
The vault became a graph (March 14 to 16)¶
The largest single capability jump in 1.x. Links were extracted and stored in
the index
(PR #194), and on
top of that came get_backlinks, get_outlinks and get_broken_links
(PR #195),
get_similar
(PR #196),
get_recent
(PR #197), the
get_context dossier
(PR #200),
get_orphan_notes and get_most_linked
(PR #205), and
get_connection_path, a breadth-first shortest path between two notes
(PR #227).
Because links were now first-class, renaming a note started rewriting the links pointing at it (PR #206).
The same days brought the two extension points the workflow guides later build on: MCP resources and prompts (PR #97) and user-defined prompts loaded from a mounted directory, overriding the built-in ones (PR #226).
Visual views, packaging and Obsidian semantics (March 18 to 31)¶
The published tool reference goes from 13 tools at v1.6.0 to 26 at v1.18.0. Nine of the thirteen new entries are the graph work above; the rest arrived here.
MCP Apps support was built as a single-page application with tab routing rather than a resource per view. That was an explicit architecture decision, chosen for a single sidebar panel with tabbed navigation, at the cost of a more complex page (#273). Three views followed on the same shell: the Note Context Card (#274), the Graph Explorer (#275) and the Vault Browser (#276), with cross-view navigation and send-to-conversation standardised across them (#277).
Two tools for getting bytes in and out arrived alongside: fetch, which
downloads a URL into the vault
(PR #259), and
create_download_link, a one-time HTTP endpoint for an artifact
(PR #261).
Deployment surfaces multiplied: systemd units
(PR #250), .deb
and .rpm packages built with nfpm
(PR #251), and a
persistent event store so HTTP sessions survive a restart
(PR #279).
Obsidian compatibility was sharpened twice: wikilinks resolve vault-wide rather than relative to the containing folder (PR #234), and frontmatter aliases resolve as link targets (PR #319). Pull conflicts stopped being fatal: a failed fast-forward rebases (PR #230) and an unresolvable conflict is written out as a sibling file, the way Syncthing handles one (PR #232).
Edit reliability, git history, and the move to shared machinery (April)¶
The edit tool was rebuilt around a failure mode specific to LLM callers. Its
issue describes the round trip precisely: file to MCP response to context
window to model output to MCP request to string comparison, where "any
transformation anywhere in that chain breaks the match"
(#325). Three
changes followed. An optional line-range mode covers large block
replacements. A normalised-matching fallback survives smart quotes and dash
substitution while preserving the original bytes everywhere except the
replaced span. Diagnostics name the diverging line and character offset, so
the model can correct itself instead of retrying blindly.
Git history became readable through the tools: get_history and get_diff
(PR #337) took the
published tool count to 28, where it stayed for the rest of 1.x.
Distribution widened again with a Claude Code plugin and an mcpb bundle
(PR #350).
The last three weeks were mostly structural, and they are the reason later
releases look the way they do. Collection was split into managers with
dependency injection
(PR #378), then
seven consecutive refactors moved configuration, authentication, the
middleware stack, logging, the event store, the artifact store and the command
line onto a shared library, fastmcp-pvl-core
(PR #396 through
PR #402), and the
project was re-based on a copier template that owns the release and CI
machinery
(PR #405).
[per the maintainer] the 42 commits between v1.27.0 and v1.27.1, the
largest block in 1.x with no linked issue behind it, are the rest of that same
separation: the split of shared infrastructure out of this repository into two
upstreams, the fastmcp-server-template copier template and the
fastmcp-pvl-core library. It ran as many small ports back and forth between
the repositories rather than as one tracked feature, so the issue record for it
is thin. Per-change detail for this stretch lives in the pull request bodies,
not in issues.
The pull requests in the range show the shape of that work. Sentinel markers
were carved into config.py and CLAUDE.md so template updates and
project-owned content stop overwriting each other
(PR #417,
PR #419), the
server module was renamed to the template's server.py and make_server
spelling
(PR #416),
template-owned files that had drifted were converged back
(PR #421), and
the weekly copier update workflow that keeps them converging was bootstrapped
(PR #415). That
last body records why the backfill had to be done by hand: a literal update
from the template flipped the license and replaced the command line, so the
repository had to be reshaped before the automation could be trusted.
The line closes with the search work that the 3.0 release then had to correct. v1.28.0 introduced four ranking knobs at once: a per-document cap on result slots, a length downweight, snippet truncation, and an adaptive chunker that descends heading levels when a chunk runs over budget (PR #433, closing #432). The adaptive chunker is what later caused several chunks of one document to crowd the top of a result list, which 3.0 fixed by collapsing results per file.
Version numbers that were never released¶
[verified: git tag] The stable line skips 1.24 and 1.26 entirely, and never
reaches 2.x: v1.23.2, v1.24.0, v1.26.0 and v2.0.0 exist only as
pre-release tags. v1.25.0 and v1.27.0 are stable tags carrying nothing but
their own release commit, because everything they contain was already tagged
on the release candidate immediately before them.
[per the maintainer] these are unintended bumps from two release-workflow
defects, both since fixed upstream, and not abandoned feature work. Nothing
was being built under those numbers, so there is no story to reconstruct:
- fastmcp-server-template#154
reports that a forced version bump compounded on the pre-release's series
target instead of naming the release kind. Dispatching a major bump at
v2.0.0-rc.5producedv3.0.0, and 2.0.0 was never cut. Fixed by template#165. - fastmcp-server-template#235
reports that release-candidate detection matched an abandoned series and cut
an
-rc.1from a stable base, which is wherev1.23.2-rc.1,v1.24.0-rc.1andv1.26.0-rc.1come from. Fixed by template#236 and adopted here in #854.
Both fixes are in this repository. The framing above is recorded on #1054; from the tags alone the obvious reading is an abandoned release series, and that reading is wrong.
What could not be reconstructed¶
Stated plainly, because a gap left unmarked reads as an absence of change:
- Six of the seven zero-density ranges listed above have no recoverable
rationale. The seventh and largest,
v1.27.0..v1.27.1at 42 commits, is the upstream split described in the April section; even there the account is a shape rather than a change-by-change record, because the detail sits in pull request bodies rather than in issues. - Patch releases in this line are not individually described. Several exist only because a release-workflow run produced them, and separating those from real fixes is not possible from the record.
- No upgrade or breaking-change analysis is offered for any step inside 1.x. The import surface guard that makes such an analysis mechanical (template#352) did not exist yet, and reconstructing one release-by-release from source would be inference presented as fact.
All changes¶
See CHANGELOG.md for the full commit-level list, or the v1.0.0 to v1.28.0 comparison.