Conversation
FastMCP 4.x bundles MCP SDK v2, which renamed the ToolAnnotations hint fields from camelCase (readOnlyHint, ...) to snake_case (read_only_hint, ...). The old camelCase attributes still exist but raise a deprecation error on access, so the instrumentation was reading .readOnlyHint, raising, getting swallowed by the try/except in on_call_tool, and silently dropping all read_only/idempotent/external/destructive tags. Add a version-agnostic _annotation_hint() helper that reads snake_case first, falls back to camelCase, and guards every access so a renamed or removed field can never raise out of the instrumentation. Works for dict- and object/pydantic-model-style annotations across FastMCP 2.x/3.x (camelCase) and 4.x (snake_case). Extend the unit tests to cover both camelCase and snake_case object annotations. Verified: full fastmcp test suite passes under fastmcp 2.14.7, 3.4.7, and 4.0.3, plus an end-to-end 4.x app with ScoutMiddleware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
flake8 6.0.0 bundles an old pyflakes that crashes on Python 3.12+ with
"module ast has no attribute Str" (ast.Str was removed), breaking the
pre-commit CI check on all recent Python. Bump flake8 to 7.3.0
(pyflakes 3.4.0), which is 3.12/3.13/3.14 safe.
flake8 7.x surfaces two pre-existing violations the old pin missed:
* rq.py: remove two unused `global installed` declarations (F824;
the name is only read in those scopes, never assigned).
* tests/.../test_commands.py: rewrite `type(a) == type(b)` as
`isinstance(a, type(b))` (E721).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix FastMCP tool annotation tags under FastMCP 4.x (MCP SDK v2)
Problem
CI (and any app on the latest FastMCP) stopped capturing the tool behavioral
annotation tags —
read_only/idempotent/external/destructive.tests/integration/test_fastmcp.py::test_tool_with_metadatafails:Root cause
tox.inipinsfastmcp>=2.9.0with no upper bound, so CI now resolvesFastMCP 4.x, which bundles MCP SDK v2. v2 renamed the
ToolAnnotationshint fields from camelCase to snake_case (
readOnlyHint→read_only_hint,etc.). The old camelCase attributes still exist (
hasattrisTrue) butraise a deprecation error when accessed:
The instrumentation read
annotations.readOnlyHint, which raised, got swallowedby the
try/exceptinon_call_tool, and silently dropped every annotationtag (not just returning wrong values — none at all).
Fix
Add a version-agnostic
_annotation_hint()helper that reads snake_casefirst, then falls back to camelCase, handling both dict- and object/pydantic-
model-style annotations, and guards every access so a renamed/removed field
can never raise out of the instrumentation. This keeps FastMCP 2.x/3.x
(camelCase) working and fixes 4.x (snake_case).
Verification (live, all three majors)
fastmcp 2.14.7— full fastmcp suite 16/16 passfastmcp 3.4.7— 16/16 passfastmcp 4.0.3— reproduced the failure, then 16/16 pass after the fixScoutMiddlewarecalls an annotatedtool → no exception, and
read_only/idempotent/externalare capturedcorrectly.
Unit tests extended to cover both camelCase (2.x/3.x) and snake_case (4.x)
object annotations, with
spec'd mocks so this can't silently drift again.Note: this is independent of the
ca_certwork; it fixes the pre-existing CIfailure on
mastercaused by FastMCP 4.x.