Skip to content

Move to fastmcp 4.1 and accept its is_error truncation argument - #136

Merged
tony merged 7 commits into
mainfrom
fastmcp-4.1
Oct 10, 2026
Merged

tony merged 7 commits into
mainfrom
fastmcp-4.1

Conversation

@tony

@tony tony commented Oct 10, 2026

Copy link
Copy Markdown
Member

Moves the lock to fastmcp 4.1.0 and adapts TailPreservingResponseLimitingMiddleware to the signature change that release makes. The branch also carries this round's toolchain and lint/type-check bumps (uv, ruff, mypy, ast-serialize) and a bulk lock refresh.

Root cause

fastmcp 4.1.0 (PrefectHQ/fastmcp#5309, fixing #5304) changes ResponseLimitingMiddleware.on_call_tool to call self._truncate_to_result(text, meta=result.meta, is_error=result.is_error), and the base _truncate_to_result(self, text, meta=None, is_error=False) now sets is_error on the ToolResult it builds. Our override in src/libtmux_mcp/middleware.py did not accept is_error, so on 4.1.0:

  • mypy reports an incompatible override, and
  • every truncated tool result raises TypeError: ... got an unexpected keyword argument 'is_error'.

Changes

  • The override accepts is_error: bool = False and passes it to all three ToolResult returns. The class docstring notes that 4.1.0 forwards the flag itself.
  • The on_call_tool restoration stays: the declared floor, fastmcp>=4.0.2, still admits releases whose base class drops the flag.
  • The lock moves to fastmcp and fastmcp-slim 4.1.0, mcp and mcp-types 2.3.0, and openapi-pydantic 0.6.0. fastmcp-slim 4.1.0 adds a Python 3.15-only beartype 0.23.0rc2 fork; 3.10-3.14 stay on 0.22.9. beartype 0.23.0 final clears the 3-day supply-chain cooldown on 2026-10-13, and a relock after that replaces the rc.

The server runs over stdio only, and the source uses no ctx.meta, skills provider, or regex tool search, so the other 4.1.0 and mcp 2.3.0 tightenings (HTTP sessions, auth, OpenAPI, SSE) do not reach it. No tool, resource, prompt, or configuration variable changes, so there is no CHANGES entry.

Test evidence

  • Fix commit on the previous lock (fastmcp 4.0.10): ruff format, ruff check, mypy, and pytest pass (920 passed, 6 skipped).
  • 4.1.0 lock without the fix: mypy reports the incompatible override, and tests/test_middleware.py::test_response_limiter_preserves_error_results fails for oversized_error_keeps_flag and oversized_success_not_marked_error with the TypeError above.
  • 4.1.0 lock with the fix: mypy is clean and those tests pass.
  • Branch tip: ruff format, ruff check, mypy, and pytest pass (920 passed, 6 skipped).

Commits

  • .tool-versions(uv) uv 0.12.21 -> 0.12.23
  • py(deps[dev]) ruff 0.16.9 -> 0.16.10
  • py(deps[dev]) mypy 2.3.1 -> 2.4.0
  • py(deps[dev]) ast-serialize 0.11.2 -> 0.12.1
  • mcp(fix[middleware]): Accept is_error argument
  • py(deps) fastmcp 4.0.10 -> 4.1.0, mcp 2.2.0 -> 2.3.0
  • py(deps) Bump packages

tony added 7 commits October 10, 2026 04:41
why: 0.12.22 records workspace-member default groups and
dependency-group Python requirements in the lockfile, checks unchanged
requirements against the existing lockfile hashes on relock, and adds
CPython 3.13.16 and 3.14.8 (with 3.10.22, 3.11.17, 3.12.15). 0.12.23
rejects alternate sources for workspace members across conflicting
dependency selections, which had produced uninstallable lockfiles. The
span carries no breaking change and no security fix.

CI does not read this pin (setup-uv has no version-file), so it moves
local runs only. `uv lock --dry-run` under 0.12.23 reports no lockfile
changes. 0.12.24 (2026-10-08) and 0.13.0 (2026-10-09, breaking: default
Python 3.15, new cache format) stay excluded by the 3-day cooldown
until 2026-10-11 and 2026-10-12.

what:
- .tool-versions: uv 0.12.21 -> 0.12.23

See also:
- https://github.com/astral-sh/uv/releases/tag/0.12.22
- https://github.com/astral-sh/uv/releases/tag/0.12.23
- https://github.com/astral-sh/uv/blob/0.12.23/CHANGELOG.md
why: 0.16.10's stable changes are lower memory use for diagnostics and
a language-server hardening that stops running `uv format` in
untrusted workspaces; the new UP052 rule is preview-only.

`ruff check --show-settings` shows preview disabled here. `ruff check
.` and `ruff format --check .` produce identical output under 0.16.9
and 0.16.10 in this tree, so no diagnostics or reformatting land.

ruff 0.17.0 (published 2026-10-09T19:46Z) changes the default target
version and default rule set; it stays excluded by the 3-day cooldown
until 2026-10-12, and its floor move is /ruff:bump's job.

what:
- uv.lock: ruff 0.16.9 -> 0.16.10 via `uv lock --upgrade-package
  ruff`; no other entry moves
- the ruff floor in pyproject.toml is untouched

See also: https://github.com/astral-sh/ruff/releases/tag/0.16.10
why: 2.4.0 turns on the native parser by default (built on the Ruff
parser; the legacy parser stays behind --no-native-parser until its
planned removal in early 2027). The native parser ignores `# type:`
comments on for and with targets and infers those types instead.
Parallel checking (-n / num_workers) leaves experimental status but
stays off by default, and Python 3.15 code (PEP 661 sentinels, PEP 810
lazy imports, PEP 798 comprehension unpacking) is now checked. No error
code changes and no Python version is dropped.

`uv run mypy .` reports no issues in 71 source files under both 2.3.1
and 2.4.0. No for/with target here carries a `# type:` comment.

what:
- uv.lock: mypy 2.3.1 -> 2.4.0 via `uv lock --upgrade-package mypy`;
  no other entry moves

See also: https://github.com/python/mypy/blob/v2.4.0/CHANGELOG.md
why: ast-serialize is the mypyc binding behind mypy's native parser.
mypy 2.4.0 accepts >=0.11.2,<1.0.0, so this move is not forced by the
mypy bump and lands on its own as a 0.x minor. 0.12.0 allows a colon in
error codes (#94); 0.12.1 fixes parsing a plain None type inside a
string annotation (#96). The project publishes no release notes; the
compare view is the record.

`uv run mypy .` output is unchanged.

what:
- uv.lock: ast-serialize 0.11.2 -> 0.12.1

See also:
- mypyc/ast_serialize@v0.11.2...v0.12.1
- https://pypi.org/project/ast-serialize/0.12.1/
why: FastMCP 4.1.0 (PrefectHQ/fastmcp#5309) makes
ResponseLimitingMiddleware.on_call_tool call
_truncate_to_result(text, meta=..., is_error=...) so the base class
keeps the error flag on the result it builds. Our override did not
accept is_error, so on 4.1.0 an oversized error result raised a
TypeError and mypy reported an incompatible override. The on_call_tool
restoration stays because the declared floor, fastmcp>=4.0.2, still
admits releases whose base class drops the flag.

what:
- Add is_error: bool = False to
  TailPreservingResponseLimitingMiddleware._truncate_to_result and
  pass it to all three ToolResult returns
- Update the class docstring: 4.1.0 forwards the flag itself, and
  on_call_tool restores it for older releases

See also: PrefectHQ/fastmcp#5309
why: fastmcp is exempt from the cooldown here on purpose, so its
releases land as they ship. 4.0.11 is a security and bug-fix release
(SSE Host/Origin protection, auth on component-manager routes, OpenAPI
components send only declared arguments, bounded schema nesting).
4.1.0 restores full Python 3.15 support and tightens four behaviours:
MultiAuth client IDs are qualified by source, regex tool search uses
Pydantic's regex engine (no lookarounds or backreferences), skill files
must resolve inside their configured directories, OpenAPI path
parameters reject `.` and `..`; HTTP sessions now expire after the MCP
SDK's 30-minute idle default. mcp 2.3.0 requires httpx2>=2.10, rejects
an invalid x-mcp-header annotation at tool registration, stops sending
empty _meta and params (ctx.meta is None rather than {}), and omits
`experimental` from initialize when none is configured.

The server runs over stdio only (server.run(transport="stdio")), so
the HTTP, auth, OpenAPI and SSE items do not reach it, and the source
uses no ctx.meta, skills provider or regex tool search. 4.1.0 also
forwards is_error to ResponseLimitingMiddleware._truncate_to_result;
the previous commit adapts the override in middleware.py. The full
suite passes on this lock.

fastmcp-slim 4.1.0 requires beartype>=0.23.0rc2 on Python >= 3.15, so
the lock gains a 3.15-only fork on beartype 0.23.0rc2 while 3.10-3.14
stay on 0.22.9. beartype 0.23.0 final (published 2026-10-10) clears
the 3-day cooldown on 2026-10-13; a relock after that replaces the rc.
openapi-pydantic 0.5.1 -> 0.6.0 comes with fastmcp-slim[server]; it
drops Python 3.8 and adds OpenAPI 3.1.2, and nothing here imports it.

what:
- uv.lock: fastmcp 4.0.10 -> 4.1.0
- uv.lock: fastmcp-slim 4.0.10 -> 4.1.0
- uv.lock: mcp 2.2.0 -> 2.3.0
- uv.lock: mcp-types 2.2.0 -> 2.3.0
- uv.lock: openapi-pydantic 0.5.1 -> 0.6.0
- uv.lock: beartype 0.23.0rc2 added for python>=3.15

See also:
- https://github.com/PrefectHQ/fastmcp/releases/tag/v4.0.11
- https://github.com/PrefectHQ/fastmcp/releases/tag/v4.1.0
- https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.3.0
- https://github.com/mike-oakley/openapi-pydantic/releases/tag/v0.6.0
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.81%. Comparing base (c098c2d) to head (0297d0b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #136   +/-   ##
=======================================
  Coverage   86.81%   86.81%           
=======================================
  Files          46       46           
  Lines        3899     3899           
  Branches      587      587           
=======================================
  Hits         3385     3385           
  Misses        360      360           
  Partials      154      154           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tony
tony merged commit 3e72eae into main Oct 10, 2026
9 checks passed
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.

2 participants