Skip to content

Update classes to inherit from FastMCPBaseModel instead of BaseModel#2739

Merged
jlowin merged 6 commits intomainfrom
update-basemodel-to-fastmcpbasemodel
Dec 26, 2025
Merged

Update classes to inherit from FastMCPBaseModel instead of BaseModel#2739
jlowin merged 6 commits intomainfrom
update-basemodel-to-fastmcpbasemodel

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 26, 2025

Replace pydantic.BaseModel with FastMCPBaseModel for FastMCP classes to ensure extra='forbid' by default, preventing silent acceptance of incorrect initialization kwargs.

Changes

Updated 12 classes across 6 files to inherit from FastMCPBaseModel:

  • Configuration classes: Deployment, MCPServerConfig
  • Tool classes: ToolResult
  • Sampling classes: SamplingTool (preserves arbitrary_types_allowed=True)
  • Caching classes: CachableResourceContent, CachableResourceResult, CachableToolResult, ResponseCachingStatistics
  • Event store classes: EventEntry, StreamEventList
  • Resource classes: ResourceContent, ResourceResult

Benefits

This change ensures that these classes will reject unknown kwargs, helping catch typos and invalid parameters early:

# Before: This would silently accept the typo
result = ToolResult(contnet=[...])  # No error!

# After: This will raise a ValidationError
result = ToolResult(contnet=[...])  # ValidationError: extra inputs not permitted

Replace pydantic.BaseModel with FastMCPBaseModel for FastMCP classes
to ensure extra='forbid' by default, preventing silent acceptance
of incorrect initialization kwargs.

Updated classes:
- Deployment, MCPServerConfig (config classes)
- ToolResult (tool classes)
- SamplingTool (sampling classes, preserves arbitrary_types_allowed)
- CachableResourceContent, CachableResourceResult, CachableToolResult,
  ResponseCachingStatistics (caching classes)
- EventEntry, StreamEventList (event store classes)
- ResourceContent, ResourceResult (resource classes)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 26, 2025

Walkthrough

This PR removes **kwargs from several constructor signatures (Message, PromptResult, ResourceContent, ResourceResult) to enforce strict parameter validation. It replaces Pydantic BaseModel with FastMCPBaseModel across multiple data model classes (EventEntry, StreamEventList, and various cachable wrappers). The caching layer is enhanced with wrap/unwrap helper methods for converting between cached and uncached representations, new meta fields, and additional statistics tracking. The FunctionPrompt.add_to_docket method is updated to unpack arguments as keyword arguments rather than passing them as a single positional parameter.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: updating classes to inherit from FastMCPBaseModel instead of BaseModel.
Description check ✅ Passed The description provides clear context, lists all affected classes, explains the benefits with a concrete example, and follows the repository template structure.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch update-basemodel-to-fastmcpbasemodel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@marvin-context-protocol marvin-context-protocol Bot added the enhancement Improvement to existing functionality. For issues and smaller PR improvements. label Dec 26, 2025
- Remove **kwargs from ResourceContent, ResourceResult, Message, PromptResult, ToolResult __init__ methods (custom __init__ already prevents extra kwargs)
- Revert these classes to pydantic.BaseModel (FastMCPBaseModel not needed)
- Fix CachableMessage and CachablePromptResult to use FastMCPBaseModel (were broken after BaseModel import removal)
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

marvin-context-protocol Bot commented Dec 26, 2025

Test Failure Analysis

Summary: The workflow run #20515579176 failed due to a test timeout on Windows, which is a known flaky test issue unrelated to the PR changes.

Root Cause: The test tests/server/auth/providers/test_auth0.py::TestAuth0Provider::test_init_with_explicit_params timed out on Windows when trying to create a SQLite database connection via diskcache. The timeout occurred at:

File "D:\a\fastmcp\fastmcp\src\fastmcp\server\auth\oauth_proxy.py", line 821, in __init__
    key_value=DiskStore(directory=settings.home / "oauth-proxy"),

This is a flaky test that occasionally times out on Windows runners, likely due to disk I/O performance issues or SQLite locking on Windows.

Resolution: ✅ The issue has already been resolved - subsequent workflow runs have passed successfully:

  • Run #20515579177SUCCESS
  • The PR changes themselves are working correctly

This timeout is not caused by the PR changes (which update classes to inherit from FastMCPBaseModel). The timeout is a known environmental issue with Windows CI runners.

Detailed Analysis

Failed Test

  • tests/server/auth/providers/test_auth0.py::TestAuth0Provider::test_init_with_explicit_params

Failure Type

  • Test timeout (not a code error or assertion failure)

Stack Trace

The timeout occurred while initializing DiskStore which creates a SQLite database:

File "oauth_proxy.py", line 821
    key_value=DiskStore(directory=settings.home / "oauth-proxy")
File "diskcache/core.py", line 623
    con = self._local.con = sqlite3.connect(...)
+++ Timeout +++

Why This Is Not PR-Related

  1. The PR only changes class inheritance from BaseModel to FastMCPBaseModel
  2. The PR removes **kwargs from __init__ methods (intentional with extra="forbid")
  3. These changes affect validation behavior, not I/O operations or database connections
  4. The timeout occurs in third-party library code (diskcache) during SQLite initialization
  5. Subsequent runs of the same code passed successfully

Flaky Test Pattern

This test has timed out before on Windows runners. It's a known issue with:

  • Windows filesystem/SQLite performance
  • CI runner disk I/O variability
  • DiskCache initialization on Windows
Related Files
  • tests/server/auth/providers/test_auth0.py:104 - Test that timed out
  • src/fastmcp/server/auth/oauth_proxy.py:821 - Where DiskStore is initialized
  • Not related to PR changes in: prompts/prompt.py, resources/resource.py, server/event_store.py, etc.

Updated: 2025-12-26 - Workflow run #20515579176 analysis

These classes use model_dump/reconstruction patterns that conflict
with extra='forbid' when aliased fields like schema_ are involved.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/fastmcp/server/sampling/sampling_tool.py (1)

16-44: model_config override loses extra="forbid" from FastMCPBaseModel.

When SamplingTool overrides model_config with only arbitrary_types_allowed=True, it replaces the entire ConfigDict inherited from FastMCPBaseModel, losing the extra="forbid" setting. This defeats the purpose of the base class migration.

🔎 Proposed fix
-    model_config = ConfigDict(arbitrary_types_allowed=True)
+    model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
🧹 Nitpick comments (1)
src/fastmcp/server/middleware/caching.py (1)

100-104: Consider using Literal["user", "assistant"] for the role field.

The Message class uses Literal["user", "assistant"] for its role field, but CachableMessage uses str. Using the same type would provide better type safety and consistency.

🔎 Proposed fix
+from typing import Any, Literal, TypedDict
+
 class CachableMessage(FastMCPBaseModel):
     """A wrapper for Message that can be cached."""

-    role: str
+    role: Literal["user", "assistant"]
     content: mcp.types.TextContent | mcp.types.EmbeddedResource
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a6f715 and 0c58cfa.

📒 Files selected for processing (6)
  • src/fastmcp/prompts/prompt.py
  • src/fastmcp/resources/resource.py
  • src/fastmcp/server/event_store.py
  • src/fastmcp/server/middleware/caching.py
  • src/fastmcp/server/sampling/sampling_tool.py
  • src/fastmcp/tools/tool.py
🧰 Additional context used
📓 Path-based instructions (1)
src/fastmcp/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/fastmcp/**/*.py: Python ≥ 3.10 with full type annotations required
Prioritize readable, understandable code - clarity over cleverness. Avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code implementation
Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types
Never use bare except - be specific with exception types

Files:

  • src/fastmcp/server/event_store.py
  • src/fastmcp/server/sampling/sampling_tool.py
  • src/fastmcp/server/middleware/caching.py
  • src/fastmcp/tools/tool.py
  • src/fastmcp/prompts/prompt.py
  • src/fastmcp/resources/resource.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Follow existing patterns and maintain consistency in code implementation
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required

Applied to files:

  • src/fastmcp/server/event_store.py
  • src/fastmcp/server/sampling/sampling_tool.py
  • src/fastmcp/server/middleware/caching.py
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Follow existing patterns and maintain consistency in code implementation

Applied to files:

  • src/fastmcp/server/sampling/sampling_tool.py
🧬 Code graph analysis (1)
src/fastmcp/server/sampling/sampling_tool.py (1)
src/fastmcp/utilities/types.py (1)
  • FastMCPBaseModel (38-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run tests: Python 3.10 on windows-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (11)
src/fastmcp/tools/tool.py (1)

119-121: LGTM!

Formatting change consolidates the super().__init__() call to a single line. No functional impact.

src/fastmcp/prompts/prompt.py (3)

90-90: LGTM!

Removal of **kwargs passthrough enforces stricter parameter validation, preventing unknown kwargs from being silently accepted.


159-159: LGTM!

Consistent with the PR objective of enforcing explicit parameter handling by removing **kwargs passthrough.


561-584: LGTM!

The change to splat arguments as **kwargs aligns with the docstring and mirrors the pattern in FunctionTool.add_to_docket. The or {} safely handles None arguments.

src/fastmcp/server/event_store.py (1)

21-37: LGTM!

Clean migration of EventEntry and StreamEventList to FastMCPBaseModel. Both are simple data models with no model_config overrides, so they correctly inherit extra="forbid".

src/fastmcp/resources/resource.py (2)

88-88: LGTM!

Removal of **kwargs passthrough enforces stricter parameter validation for ResourceContent.


168-168: LGTM!

Consistent with the PR objective of removing **kwargs passthrough from ResourceResult.

src/fastmcp/server/middleware/caching.py (4)

38-76: LGTM!

Clean migration to FastMCPBaseModel with well-structured wrap/unwrap helpers for converting between domain and cachable representations.


79-97: LGTM!

CachableToolResult properly migrated with wrap/unwrap helpers that delegate to ToolResult's constructor.


107-135: LGTM!

CachablePromptResult properly migrated with comprehensive wrap/unwrap helpers that handle description and meta fields.


172-178: LGTM!

ResponseCachingStatistics properly migrated to FastMCPBaseModel with expanded fields for comprehensive cache statistics tracking.

@jlowin jlowin merged commit a39ab07 into main Dec 26, 2025
14 of 17 checks passed
@jlowin jlowin deleted the update-basemodel-to-fastmcpbasemodel branch December 26, 2025 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement to existing functionality. For issues and smaller PR improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant