Skip to content

Refactor MountedProvider into FastMCPProvider + TransformingProvider#2653

Merged
jlowin merged 2 commits intomainfrom
transforming-provider
Dec 19, 2025
Merged

Refactor MountedProvider into FastMCPProvider + TransformingProvider#2653
jlowin merged 2 commits intomainfrom
transforming-provider

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 19, 2025

Splits the monolithic MountedProvider into two focused, composable components:

  • FastMCPProvider: Wraps a FastMCP server, exposing its components through the Provider interface
  • TransformingProvider: Wraps any provider to apply namespace prefixes and tool renames

This separation enables cleaner composition and makes the transformation logic reusable across any provider type.

New API

Providers now support a fluent with_transforms() method:

from fastmcp.server.providers import FastMCPProvider

# Apply namespace to all components
provider = FastMCPProvider(server).with_namespace("api")
# "my_tool" → "api_my_tool"
# "resource://data" → "resource://api/data"

# Rename specific tools (bypasses namespace)
provider = FastMCPProvider(server).with_transforms(
    namespace="api",
    tool_renames={"verbose_tool_name": "short"}
)
# "verbose_tool_name" → "short"
# "other_tool" → "api_other_tool"

# Stacking composes transformations
provider = (
    FastMCPProvider(server)
    .with_namespace("inner")
    .with_namespace("outer")
)
# "tool" → "outer_inner_tool"

Breaking Changes

  • MountedProvider is removed (replaced by FastMCPProvider + TransformingProvider)
  • mount(prefix=...) is deprecated in favor of mount(namespace=...)

Split the monolithic MountedProvider into two focused components:
- FastMCPProvider: wraps a FastMCP server as a provider
- TransformingProvider: applies namespace/rename transformations to any provider

Add with_transforms() method to Provider base class for fluent API.
Rename mount() prefix parameter to namespace (deprecate prefix).
@marvin-context-protocol marvin-context-protocol Bot added breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. labels Dec 19, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 19, 2025

Walkthrough

This change removes the MountedProvider and replaces it with two new providers: FastMCPProvider (wraps a FastMCP server and exposes tools, resources, prompts, templates, tasks, and lifespan via the Provider interface) and TransformingProvider (wraps any Provider to apply namespace prefixes and explicit tool renames, including reverse lookups). Provider gains with_transforms() and with_namespace() helpers for composing transforms. Server.mount() is updated to accept a namespace parameter (keeps prefix for backward compatibility with deprecation warnings) and now constructs FastMCPProvider plus optional transforms. Package exports are updated to expose FastMCPProvider and TransformingProvider.

Possibly related PRs

  • PR 2635: Directly touches the provider/mounting implementation and likely conflicts with removal of MountedProvider and the new provider structure.
  • PR 2619: Changes mounting-time tool rename/override behavior and reverse-lookup logic used by TransformingProvider.
  • PR 2622: Alters the Provider abstraction and provider-based server integration that FastMCPProvider and TransformingProvider build on.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is well-structured with clear explanations of the refactoring, new API examples, and breaking changes. However, the contributor checklist items are unchecked and there's no reference to a related issue number, which are required by the template. Check all applicable checklist items (at minimum closing issue and self-review), add the issue number reference (#xxxx), and confirm testing and documentation updates were completed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main refactoring: splitting MountedProvider into FastMCPProvider and TransformingProvider, which matches the core changes throughout the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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 transforming-provider

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between afe24ad and e094ae3.

📒 Files selected for processing (1)
  • src/fastmcp/server/server.py (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/fastmcp/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/fastmcp/**/*.py: Write Python code with ≥3.10 type annotations required throughout
Never use bare except - be specific with exception types

Files:

  • src/fastmcp/server/server.py
🧬 Code graph analysis (1)
src/fastmcp/server/server.py (2)
src/fastmcp/server/providers/fastmcp_provider.py (1)
  • FastMCPProvider (28-223)
src/fastmcp/server/providers/base.py (2)
  • Provider (72-384)
  • with_transforms (92-141)
🪛 Ruff (0.14.8)
src/fastmcp/server/server.py

2700-2700: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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.10 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (4)
src/fastmcp/server/server.py (4)

852-852: LGTM! Comment updates align with the provider refactor.

The comments correctly reference FastMCPProvider instead of the removed MountedProvider, maintaining documentation accuracy.

Also applies to: 942-942, 982-982, 1022-1022, 1112-1112


646-685: Excellent documentation of the namespace-based mounting approach.

The docstring comprehensively explains how namespaces affect tools, resources, templates, and prompts with clear examples. The migration guidance from prefix to namespace is well-documented.


688-688: Well-structured provider composition using FastMCPProvider + transformations.

The implementation correctly:

  • Creates a FastMCPProvider to wrap the mounted server
  • Applies optional transformations via with_transforms() for namespace and tool renames
  • Composes providers cleanly without complex conditional logic

This aligns perfectly with the PR's goal of splitting monolithic MountedProvider into focused, composable components.

Also applies to: 717-723


2773-2779: Helper function correctly maintains backward compatibility.

The local add_resource_prefix helper properly transforms resource URIs for the deprecated import_server flow, using the existing URI_PATTERN regex to parse and reconstruct URIs with the prefix as a path segment.


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.

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

🧹 Nitpick comments (1)
src/fastmcp/server/server.py (1)

2774-2780: Consider reusing the existing compiled regex.

The add_resource_prefix function uses re.match(r"^([^:]+://)(.*?)$", uri) which duplicates the pattern already compiled at module level as URI_PATTERN (line 119). Consider reusing it:

🔎 Suggested optimization
         def add_resource_prefix(uri: str, prefix: str) -> str:
             """Add prefix to resource URI: protocol://path → protocol://prefix/path."""
-            match = re.match(r"^([^:]+://)(.*?)$", uri)
+            match = URI_PATTERN.match(uri)
             if match:
                 protocol, path = match.groups()
                 return f"{protocol}{prefix}/{path}"
             return uri
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caeaa86 and afe24ad.

⛔ Files ignored due to path filters (5)
  • tests/server/providers/__init__.py is excluded by none and included by none
  • tests/server/providers/test_fastmcp_provider.py is excluded by none and included by none
  • tests/server/providers/test_transforming_provider.py is excluded by none and included by none
  • tests/server/test_mount.py is excluded by none and included by none
  • tests/server/test_server.py is excluded by none and included by none
📒 Files selected for processing (7)
  • src/fastmcp/contrib/component_manager/component_service.py (7 hunks)
  • src/fastmcp/server/providers/__init__.py (1 hunks)
  • src/fastmcp/server/providers/base.py (1 hunks)
  • src/fastmcp/server/providers/fastmcp_provider.py (1 hunks)
  • src/fastmcp/server/providers/mounted.py (0 hunks)
  • src/fastmcp/server/providers/transforming.py (1 hunks)
  • src/fastmcp/server/server.py (8 hunks)
💤 Files with no reviewable changes (1)
  • src/fastmcp/server/providers/mounted.py
🧰 Additional context used
📓 Path-based instructions (2)
src/fastmcp/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/fastmcp/**/*.py: Write Python code with ≥3.10 type annotations required throughout
Never use bare except - be specific with exception types

Files:

  • src/fastmcp/server/providers/__init__.py
  • src/fastmcp/server/providers/base.py
  • src/fastmcp/server/providers/fastmcp_provider.py
  • src/fastmcp/contrib/component_manager/component_service.py
  • src/fastmcp/server/providers/transforming.py
  • src/fastmcp/server/server.py
src/fastmcp/**/__init__.py

📄 CodeRabbit inference engine (AGENTS.md)

Be intentional about re-exports; core types that define a module's purpose should be exported; specialized features can live in submodules; only re-export to fastmcp.* for fundamental types

Files:

  • src/fastmcp/server/providers/__init__.py
🧠 Learnings (1)
📚 Learning: 2025-12-17T03:06:14.522Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T03:06:14.522Z
Learning: Applies to src/fastmcp/**/__init__.py : Be intentional about re-exports; core types that define a module's purpose should be exported; specialized features can live in submodules; only re-export to fastmcp.* for fundamental types

Applied to files:

  • src/fastmcp/server/providers/__init__.py
🧬 Code graph analysis (2)
src/fastmcp/server/providers/base.py (1)
src/fastmcp/server/providers/transforming.py (1)
  • TransformingProvider (31-349)
src/fastmcp/contrib/component_manager/component_service.py (1)
src/fastmcp/server/providers/transforming.py (2)
  • _reverse_resource_uri (154-165)
  • _reverse_tool_name (108-119)
🪛 Ruff (0.14.8)
src/fastmcp/server/providers/transforming.py

86-89: Avoid specifying long messages outside the exception class

(TRY003)

src/fastmcp/server/server.py

2700-2700: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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.13 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on windows-latest
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (22)
src/fastmcp/server/providers/__init__.py (1)

28-36: LGTM!

The updated exports correctly reflect the new provider architecture. FastMCPProvider and TransformingProvider are the core types that define the module's purpose and are appropriately exported. Components and TaskComponents remain available in base.py for specialized use cases, which aligns with the coding guidelines about being intentional with re-exports.

src/fastmcp/contrib/component_manager/component_service.py (2)

18-50: LGTM - Clean recursive resolution logic.

The helper function correctly handles the provider chain by recursively unwrapping TransformingProvider layers until reaching a FastMCPProvider. The approach of using _reverse_tool_name for both tools and prompts is correct since they share the same namespace transformation pattern (namespace_name).


79-87: Clean provider iteration pattern.

The updated logic correctly iterates over providers and uses the helper to resolve the mounted server and unprefixed key. The delegation to ComponentService(server) ensures proper recursive handling for deeply nested mounts.

src/fastmcp/server/providers/base.py (1)

92-158: LGTM - Well-designed fluent API for transformations.

The with_transforms() and with_namespace() methods provide a clean, chainable interface for applying transformations. Key observations:

  1. Lazy import of TransformingProvider correctly avoids circular dependencies.
  2. Return type Provider enables method chaining (e.g., .with_namespace("a").with_namespace("b")).
  3. Comprehensive docstrings with practical examples clearly document the stacking behavior.
src/fastmcp/server/server.py (2)

2690-2700: Proper deprecation handling for prefix → namespace migration.

The logic correctly handles the three cases:

  1. prefix provided alone: warn and map to namespace
  2. namespace provided: use directly
  3. Both provided: raise ValueError

The error message is appropriately descriptive for debugging. The static analysis hint (TRY003) about long messages outside exception classes is a minor style preference and acceptable here given the clarity it provides.


2717-2723: Clean provider composition.

The provider creation correctly separates concerns:

  • FastMCPProvider wraps the server to expose components via the Provider interface
  • with_transforms() is applied only when namespace or tool_renames are provided

This enables mounting without transformation when neither namespace nor tool_names is specified.

src/fastmcp/server/providers/fastmcp_provider.py (4)

75-88: LGTM - Tool methods correctly delegate to middleware.

The implementation properly invokes the mounted server's middleware chain for tool operations. list_tools() and call_tool() use the middleware-wrapped methods, ensuring middleware is applied for execution.


103-130: Correct provider semantics for resource access.

The read_resource method correctly catches NotFoundError and returns None, adhering to the provider contract where None signals "not found" and allows the server to try other providers. The read_resource_template delegation to read_resource is appropriate since the server's middleware handles template resolution internally.


155-208: LGTM - Task discovery with correct manager access pattern.

The implementation correctly:

  1. Accesses managers directly (bypassing middleware) for registration efficiency
  2. Filters by task_config.mode != "forbidden" consistently with the server's Docket registration
  3. Recursively collects tasks from nested providers to support deeply mounted servers

214-223: Correct lifespan scoping.

The lifespan correctly invokes only the user-defined lifespan of the wrapped server, not the full _lifespan_manager(). This is the right approach because:

  1. The parent server's Docket handles all background task registration
  2. Starting a nested Docket/Worker would cause conflicts

The comment clearly documents this intentional design decision.

src/fastmcp/server/providers/transforming.py (12)

1-25: LGTM!

Imports are well-organized with appropriate use of TYPE_CHECKING to avoid circular imports for type-only references.


27-28: LGTM!

The URI pattern correctly captures the protocol prefix and path components for namespace insertion.


61-92: LGTM!

The initialization logic correctly validates that tool_renames has no duplicate target names (ensuring reversibility) and builds the reverse lookup map efficiently. The detailed error message identifying both conflicting source names is helpful for debugging.


98-119: LGTM!

The tool name transformation logic correctly prioritizes explicit renames over namespace prefixing, and the reverse lookup properly handles both cases.


125-138: LGTM!

Prompt name transformation is intentionally simpler than tools (namespace-only, no explicit renames), and the implementation is consistent with that design.


144-165: Consider: Asymmetric handling of URIs that don't match the pattern.

When a URI doesn't match protocol://path:

  • _transform_resource_uri returns it unchanged (line 152)
  • _reverse_resource_uri returns None (line 165)

This asymmetry could cause issues: a resource with a non-standard URI would be listed (unchanged) but then be unretrievable via get_resource/read_resource because the reverse lookup returns None.

If non-standard URIs are expected to be passed through, consider returning uri in the reverse case as well. If they should be rejected, consider raising or returning None in the transform case too.


171-196: LGTM!

Tool methods correctly apply transformations on listing and reverse them on lookup/call. Using model_copy ensures the original objects aren't mutated.


202-225: LGTM!

Resource methods follow the same transformation pattern as tools. The str(r.uri) conversion handles URI types correctly.


231-260: LGTM!

Resource template methods correctly transform uri_template fields. The pattern matches other resource handling.


266-291: LGTM!

Prompt methods follow the established transformation pattern consistently.


297-339: LGTM!

The get_tasks method correctly applies the same transformations as the individual list_* methods to all component types. The # type: ignore[arg-type] comments are reasonable given model_copy's return type limitations.


345-349: LGTM!

Lifecycle correctly delegates to the wrapped provider using async context manager composition.

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/server.py (1)

638-700: Clean deprecation path for prefix → namespace migration.

The deprecation handling is well-implemented with proper warnings and clear migration guidance. The logic correctly maps prefix to namespace when only prefix is provided, and prevents ambiguous usage when both are specified.

One minor static analysis concern at line 2700: Consider extracting the long error message to improve readability:

🔎 Optional refactor for error message
+NAMESPACE_PREFIX_CONFLICT_MSG = "Cannot specify both 'prefix' and 'namespace'"
+
 def mount(
     self,
     server: FastMCP[LifespanResultT],
     namespace: str | None = None,
     as_proxy: bool | None = None,
     tool_names: dict[str, str] | None = None,
     prefix: str | None = None,
 ) -> None:
     ...
     if namespace is None:
         namespace = prefix
     else:
-        raise ValueError("Cannot specify both 'prefix' and 'namespace'")
+        raise ValueError(NAMESPACE_PREFIX_CONFLICT_MSG)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between afe24ad and e094ae3.

📒 Files selected for processing (1)
  • src/fastmcp/server/server.py (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/fastmcp/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/fastmcp/**/*.py: Write Python code with ≥3.10 type annotations required throughout
Never use bare except - be specific with exception types

Files:

  • src/fastmcp/server/server.py
🧬 Code graph analysis (1)
src/fastmcp/server/server.py (2)
src/fastmcp/server/providers/fastmcp_provider.py (1)
  • FastMCPProvider (28-223)
src/fastmcp/server/providers/base.py (2)
  • Provider (72-384)
  • with_transforms (92-141)
🪛 Ruff (0.14.8)
src/fastmcp/server/server.py

2700-2700: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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.10 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (4)
src/fastmcp/server/server.py (4)

852-852: LGTM! Comment updates align with the provider refactor.

The comments correctly reference FastMCPProvider instead of the removed MountedProvider, maintaining documentation accuracy.

Also applies to: 942-942, 982-982, 1022-1022, 1112-1112


646-685: Excellent documentation of the namespace-based mounting approach.

The docstring comprehensively explains how namespaces affect tools, resources, templates, and prompts with clear examples. The migration guidance from prefix to namespace is well-documented.


688-688: Well-structured provider composition using FastMCPProvider + transformations.

The implementation correctly:

  • Creates a FastMCPProvider to wrap the mounted server
  • Applies optional transformations via with_transforms() for namespace and tool renames
  • Composes providers cleanly without complex conditional logic

This aligns perfectly with the PR's goal of splitting monolithic MountedProvider into focused, composable components.

Also applies to: 717-723


2773-2779: Helper function correctly maintains backward compatibility.

The local add_resource_prefix helper properly transforms resource URIs for the deprecated import_server flow, using the existing URI_PATTERN regex to parse and reconstruct URIs with the prefix as a path segment.

@jlowin jlowin merged commit 3a6b1ba into main Dec 19, 2025
11 of 12 checks passed
@jlowin jlowin deleted the transforming-provider branch December 19, 2025 16:43
@jlowin jlowin added provider Related to the FastMCP Provider class and removed breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. labels Dec 19, 2025
@jlowin jlowin added this to the 2.15 milestone Dec 19, 2025
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. provider Related to the FastMCP Provider class server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant