Skip to content

chore: bump fastmcp floor to 4.0 GA, drop the 3.4.x straddle - #308

Merged
blarghmatey merged 2 commits into
mainfrom
fastmcp-4-bump
Sep 1, 2026
Merged

chore: bump fastmcp floor to 4.0 GA, drop the 3.4.x straddle#308
blarghmatey merged 2 commits into
mainfrom
fastmcp-4-bump

Conversation

@blarghmatey

Copy link
Copy Markdown
Member

What are the relevant tickets?

N/A — tracked in the internal witan task graph as tk-move-the-fastmcp-floor-to-4-when-4-0-goes-ga-454f78 (closed by this PR), no GitHub issue.

Description (What does it do?)

fastmcp 4.0.0 went GA. This drops the fastmcp>=3.4.2,<5 straddle the workspace carried while 4.0 was still a pre-release:

  • witan-core (0.32.2 → 0.33.0), witan-council (0.29.1 → 0.30.0), and witan-code (0.16.0 → 0.17.0) now floor fastmcp>=4,<5 (witan-code's optional tasks extra: fastmcp-tasks>=4,<5), with CHANGELOG entries.
  • Workspace-root [tool.uv] prerelease = "allow" — needed only to resolve the pre-release fastmcp-slim pin — is removed.
  • Removes the version-sniffing shims witan-core carried for the straddle: the inputSchema/nextCursor camelCase fallback and conditional mcp_types import in remote/proxy.py / elicit.py, and the FastMCP.__init__ signature probe in caching.py — plus the tests standing in for versions CI never installed.
  • Trims the now-resolved straddle rationale from ADR-0009 (mirrored doc regenerated via just docs-gen).
  • Updates renovate.json's fastmcp automerge-block description, which cited the removed prerelease flag.
  • ol-agent-kit needed no version bump — it floors the two servers open-ended.

How can this be tested?

  • Ran each package's full pytest suite in an isolated .venv (not the shared workspace venv, to rule out cross-package import contamination):
    • witan-core: 583 passed, 2 skipped
    • witan-code: 580 passed
    • witan-council: 1009 passed, 1 skipped
  • just check-versions passes (pyproject version / bumpversion config / CHANGELOG heading agree across all 5 packages)
  • Acceptance test from the tracked task's playbook: built all 5 wheels (uv build), then uv tool install --find-links <wheels> ol-agent-kit with NO --prerelease flag against an isolated UV_TOOL_DIR/UV_TOOL_BIN_DIR — resolved fastmcp 4.0.0 cleanly and installed working witan, witan-code, agent-kit executables (verified --version on each)

Additional Context

Evaluated the fastmcp 4.0 release notes for other functionality worth adopting (UserSession/SessionId, @mcp.completion, SEP-990 identity assertion). None had an obvious low-risk fit in this PR, so logged three follow-up tasks in the witan "service quality and maintenance" project instead of guessing at a redesign here.

Copilot AI balanced review requested due to automatic review settings August 31, 2026 20:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Raises FastMCP dependencies to 4.x following GA and removes obsolete 3.4.x compatibility code.

Changes:

  • Raises package versions and FastMCP dependency floors.
  • Removes prerelease configuration and compatibility shims.
  • Updates tests, changelogs, ADRs, and Renovate guidance.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated no comments.

Show a summary per file
File Description
uv.lock Locks FastMCP 4.0 GA and package versions.
renovate.json Updates FastMCP review rationale.
pyproject.toml Removes workspace prerelease resolution.
packages/witan-core/witan_core/remote/proxy.py Uses SDK v2 fields directly.
packages/witan-core/witan_core/elicit.py Requires modern MCP types.
packages/witan-core/witan_core/caching.py Always emits cache hints.
packages/witan-core/tests/test_remote_proxy.py Removes legacy-shape tests.
packages/witan-core/tests/test_elicit.py Runs MRTR tests unconditionally.
packages/witan-core/pyproject.toml Releases 0.33.0 with FastMCP 4 floor.
packages/witan-core/CHANGELOG.md Documents the core release.
mcp/servers/witan/pyproject.toml Releases council 0.30.0.
mcp/servers/witan/docs/adr/0009-stateless-mcp-protocol-era.md Records GA transition.
mcp/servers/witan/CHANGELOG.md Documents the council release.
mcp/servers/witan-code/witan_code/server.py Updates tasks-extra rationale.
mcp/servers/witan-code/pyproject.toml Releases code 0.17.0 with GA floors.
mcp/servers/witan-code/CHANGELOG.md Documents the code release.
docs/explanation/decisions/0009-stateless-mcp-protocol-era.md Regenerates the mirrored ADR.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

feoh added a commit that referenced this pull request Aug 31, 2026
…ge` tag

`edge` is force-updated on every push to upstream main, so a pinned digest
describes a tarball that stops being downloadable the moment upstream
merges. It broke twice on 2026-08-31 alone: `witan-code (code graph)` — the
one job deliberately left uncached to report exactly this — was red on #300
and #302 at 11:32Z, went green when #305 refreshed the pin at 14:44Z, and
was red again on #308/#309 by 20:13Z after four upstream pushes republished
the assets. The refresh held about six hours. A fresh `witan setup` fails
the same way, for the same reason.

Upstream cut v0.10.0 at 21:29Z tagging a625748c8b, which is the same commit
the then-current `edge` build came from, so this pins the same source under
a name that cannot move rather than adopting a new one. That also ends the
"temporarily on edge for a re-test" note above the version pin: the build
under test now has a release.

Four upstream commits arrive relative to the pinned ac620eea87. Only one
carries operator consequence: #581 upgrades Lance to 11, which upstream
says changes English stemming, so an index built by Lance 10 read with the
Lance 11 analyzer silently under-returns. Every search()/bm25() query in
read.gq sits on such an index. _OMNIGRAPH_INTERNAL_SCHEMA stays 6 and the
storage-format gate stays green, so nothing catches this automatically —
existing graphs need an explicit, per-branch
`omnigraph rebuild-full-text-indexes`. Recorded in full above the digests.

Verified against the v0.10.0 binary: witan-core 584, witan-council 1009,
witan-code 580 — 2173 passed, no failures. The history block records how to
run that honestly, since hermetic.py prepends the real ~/.local/bin and a
PATH override alone silently tests the machine's existing binary instead.
blarghmatey pushed a commit that referenced this pull request Aug 31, 2026
…ge` tag (#311)

* chore(witan-core): pin omnigraph to the released v0.10.0, off the `edge` tag

`edge` is force-updated on every push to upstream main, so a pinned digest
describes a tarball that stops being downloadable the moment upstream
merges. It broke twice on 2026-08-31 alone: `witan-code (code graph)` — the
one job deliberately left uncached to report exactly this — was red on #300
and #302 at 11:32Z, went green when #305 refreshed the pin at 14:44Z, and
was red again on #308/#309 by 20:13Z after four upstream pushes republished
the assets. The refresh held about six hours. A fresh `witan setup` fails
the same way, for the same reason.

Upstream cut v0.10.0 at 21:29Z tagging a625748c8b, which is the same commit
the then-current `edge` build came from, so this pins the same source under
a name that cannot move rather than adopting a new one. That also ends the
"temporarily on edge for a re-test" note above the version pin: the build
under test now has a release.

Four upstream commits arrive relative to the pinned ac620eea87. Only one
carries operator consequence: #581 upgrades Lance to 11, which upstream
says changes English stemming, so an index built by Lance 10 read with the
Lance 11 analyzer silently under-returns. Every search()/bm25() query in
read.gq sits on such an index. _OMNIGRAPH_INTERNAL_SCHEMA stays 6 and the
storage-format gate stays green, so nothing catches this automatically —
existing graphs need an explicit, per-branch
`omnigraph rebuild-full-text-indexes`. Recorded in full above the digests.

Verified against the v0.10.0 binary: witan-core 584, witan-council 1009,
witan-code 580 — 2173 passed, no failures. The history block records how to
run that honestly, since hermetic.py prepends the real ~/.local/bin and a
PATH override alone silently tests the machine's existing binary instead.

* chore(witan-core): record the FTS-rebuild task slug in the pin history

* fix(witan-core): classify omnigraph 0.10.0's full-text rebuild 409 as terminal

Copilot review on #311, and it corrects the premise of the pin commit.

omnigraph 0.10.0 does not ship raw Lance 11's silent under-return. Upstream
#581 added a guard: an index whose analyzer generation cannot be proven
compatible raises FullTextIndexRebuildRequired — HTTP 409 with a
`full_text_index_rebuild_required` detail — and their doc comment is
explicit that ordinary reads stay available and a partial indexed result is
never returned. So search fails closed rather than degrading quietly. The
history block said the opposite and is corrected.

That makes the client the problem. `classify_status` treats a bare 409 as
RETRYABLE on the status alone — deliberately, so a lost CAS race is
legible — and nothing above it matched this message. Every refused
search()/bm25() would therefore be retried the full budget and surface as a
timeout-shaped error, hiding the remedy the server already printed.

Adds FULL_TEXT_REBUILD_REQUIRED, classified ahead of the 409 catch-all on
the structured detail key and on the server's own prose, terminal on the
first attempt on both transports. Not NEEDS_REPAIR: the graph is not
damaged and `omnigraph repair --force` would be the wrong hammer.

Regression tests cover both transports, that the detail key alone suffices,
and that an ordinary lost-race 409 is still RETRYABLE. All four fail
without the classification.

witan-core 588, witan-council 1009, witan-code 580 — 2177 passed against
the v0.10.0 binary.
fastmcp 4.0.0 went GA. witan-core (0.33.0), witan-council (0.30.0), and
witan-code (0.17.0) now floor fastmcp>=4,<5 (fastmcp-tasks>=4,<5), and the
workspace-root prerelease="allow" needed only to resolve the pre-release
fastmcp-slim pin is removed.

Drops the version-sniffing shims witan-core carried for the 3.4.x/4.x
straddle: the inputSchema/nextCursor camelCase fallback and conditional
mcp_types import in remote/proxy.py and elicit.py, and the
FastMCP.__init__ signature probe in caching.py — plus the tests that
stood in for the version CI never exercised. Trims the straddle
rationale from ADR-0009.

Closes tk-move-the-fastmcp-floor-to-4-when-4-0-goes-ga-454f78, whose
playbook this follows, including its acceptance test: built all 5
wheels and ran `uv tool install --find-links <wheels> ol-agent-kit`
with no --prerelease flag against an isolated tool dir — resolves
fastmcp 4.0.0 and installs working witan/witan-code/agent-kit
executables.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T5Avh1vjbEWnb9PhecHLuM
The three release headings were stamped 2026-08-31, the day the branch
was cut; they now name the day the release actually lands.

Only the headings move. "FastMCP 4.0 went GA 2026-08-31" in ADR-0009 is
a fact about upstream's release, not ours, and uv.lock's upload-time
fields are PyPI's own stamps — both stay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Km1QJYB4oH2H1W29FpfWKP
@blarghmatey
blarghmatey merged commit 47bb80a into main Sep 1, 2026
17 checks passed
@blarghmatey
blarghmatey deleted the fastmcp-4-bump branch September 1, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants