Add standalone decorators and eliminate fastmcp.fs module#2832
Conversation
- Add standalone @tool, @resource, @prompt decorators that create objects without registering to a server - Move FileSystemProvider to fastmcp.server.providers - Refactor LocalProvider.resource() and prompt() to delegate to standalone decorators - Update all imports to use canonical paths - Add comprehensive tests for enabled=False behavior
|
Warning Rate limit exceeded@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 4 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (6)
📒 Files selected for processing (5)
WalkthroughThe pull request removes the public Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/patterns/tool-transformation.mdx (1)
31-67: Update to second-person voice + make the example runnable (definedatabase).The new standalone-decorator flow is clear, but the copy uses “we” (Line 31) and the code sample isn’t runnable because
databaseis undefined (Line 42). As per coding guidelines fordocs/**/*.mdx, prefer 2nd person and fully runnable snippets. Based on coding guidelines, …Proposed doc patch
-In the following example, we take a generic `search` tool and adjust its name and description to help an LLM client better understand its purpose. +In the following example, you take a generic `search` tool and adjust its name and description to help an LLM client understand its purpose. ```python {1, 6, 11-19, 22} from fastmcp.tools import tool, Tool from fastmcp import FastMCP + +class _Database: + def search(self, query: str, category: str) -> list[dict]: + return [{"query": query, "category": category, "id": "item_123"}] + +database = _Database() # Create a tool without registering it using the standalone @tool decorator # This creates a Tool object that can be transformed before registration @tool def search(query: str, category: str = "all") -> list[dict]: """Searches for items in the database.""" return database.search(query, category)</details> </blockquote></details> <details> <summary>src/fastmcp/server/providers/filesystem_discovery.py (1)</summary><blockquote> `11-18`: **Blocker: `importlib` is used but never imported.** `import_module_from_file()` calls `importlib.reload` / `importlib.import_module` (Line 143-144), but the module only imports `importlib.util` (Line 11). This will raise `NameError: name 'importlib' is not defined`. <details> <summary>Proposed fix</summary> ```diff -import importlib.util +import importlib +import importlib.utilAlso applies to: 140-148
🧹 Nitpick comments (4)
src/fastmcp/tools/tool.py (1)
881-891: PreferTypeErrorfor invalid type argument.When rejecting
classmethodas input,TypeErroris more semantically appropriate thanValueErrorsince the issue is with the type of the argument, not its value. This aligns with the static analysis hint (Ruff TRY004).♻️ Suggested fix
if isinstance(name_or_fn, classmethod): - raise ValueError( + raise TypeError( inspect.cleandoc( """ To decorate a classmethod, first define the method and then call tool() directly on the method instead of using it as a decorator. See https://gofastmcp.com/patterns/decorating-methods for examples and more information. """ ) )src/fastmcp/resources/resource.py (1)
590-624: Remove unreachable else branch.The else branch at lines 620-624 is logically unreachable. The two conditions are exhaustive:
- If
has_uri_params or has_func_paramsis True → first branch- If
has_uri_params or has_func_paramsis False → equivalent tonot has_uri_params and not has_func_params→ second branchThe else case can never execute because every possible combination of boolean values is covered by the first two branches.
♻️ Simplify by removing unreachable code
if has_uri_params or has_func_params: from fastmcp.resources.template import ResourceTemplate return ResourceTemplate.from_function( fn=fn, uri_template=uri, name=name, title=title, description=description, icons=icons, mime_type=mime_type, tags=tags, annotations=annotations, meta=meta, task=supports_task, ) - elif not has_uri_params and not has_func_params: + else: return Resource.from_function( fn=fn, uri=uri, name=name, title=title, description=description, icons=icons, mime_type=mime_type, tags=tags, annotations=annotations, meta=meta, task=supports_task, ) - else: - raise ValueError( - "Invalid resource or template definition due to a " - "mismatch between URI parameters and function parameters." - )src/fastmcp/prompts/prompt.py (1)
614-773: Standalonepromptdecorator: clarify callable support + consider TypeError for invalid decorator target.
- If
@promptis intended to support only real functions, consider tightening wording (AnyFunction) / docs accordingly; otherwise,inspect.isroutine(...)(Line 727) blocks callable instances even thoughPrompt.from_functionlater handles callable classes.- For
classmethodbeing an invalid decorator target,TypeErroris a better fit thanValueError(Line 714-725) (and matches Ruff TRY004).src/fastmcp/server/providers/filesystem_discovery.py (1)
175-210: Avoid registering re-exported/imported components during extraction.
extract_components()currently registers anyTool/Resource/ResourceTemplate/Promptinstance reachable as a module attribute (Line 196-209). That will also include components imported from other modules (e.g., convenience re-exports), which is likely surprising for filesystem discovery.Consider filtering to objects “owned” by the module, e.g.
getattr(obj, "__module__", None) == module.__name__, and/or iteratingmodule.__dict__.items()instead ofdir(...).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
tests/fs/test_decorators.pyis excluded by none and included by nonetests/fs/test_discovery.pyis excluded by none and included by nonetests/fs/test_provider.pyis excluded by none and included by nonetests/prompts/test_standalone_decorator.pyis excluded by none and included by nonetests/resources/test_standalone_decorator.pyis excluded by none and included by nonetests/server/providers/test_local_provider.pyis excluded by none and included by nonetests/tools/test_standalone_decorator.pyis excluded by none and included by none
📒 Files selected for processing (20)
docs/docs.jsondocs/patterns/tool-transformation.mdxdocs/servers/providers/filesystem.mdxexamples/filesystem-provider/mcp/prompts/assistant.pyexamples/filesystem-provider/mcp/resources/config.pyexamples/filesystem-provider/mcp/tools/calculator.pyexamples/filesystem-provider/mcp/tools/greeting.pyexamples/filesystem-provider/server.pysrc/fastmcp/fs/__init__.pysrc/fastmcp/fs/decorators.pysrc/fastmcp/prompts/__init__.pysrc/fastmcp/prompts/prompt.pysrc/fastmcp/resources/__init__.pysrc/fastmcp/resources/resource.pysrc/fastmcp/server/providers/__init__.pysrc/fastmcp/server/providers/filesystem.pysrc/fastmcp/server/providers/filesystem_discovery.pysrc/fastmcp/server/providers/local_provider.pysrc/fastmcp/tools/__init__.pysrc/fastmcp/tools/tool.py
💤 Files with no reviewable changes (3)
- docs/docs.json
- src/fastmcp/fs/decorators.py
- src/fastmcp/fs/init.py
🧰 Additional context used
📓 Path-based instructions (4)
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/providers/__init__.pysrc/fastmcp/resources/resource.pysrc/fastmcp/prompts/__init__.pysrc/fastmcp/server/providers/filesystem.pysrc/fastmcp/tools/__init__.pysrc/fastmcp/prompts/prompt.pysrc/fastmcp/tools/tool.pysrc/fastmcp/server/providers/filesystem_discovery.pysrc/fastmcp/server/providers/local_provider.pysrc/fastmcp/resources/__init__.py
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...
Files:
docs/patterns/tool-transformation.mdxdocs/servers/providers/filesystem.mdx
docs/**/*.{md,mdx,json}
📄 CodeRabbit inference engine (AGENTS.md)
Documentation uses Mintlify framework. Files must be in docs.json to be included. Never modify docs/python-sdk/** (auto-generated)
Files:
docs/patterns/tool-transformation.mdxdocs/servers/providers/filesystem.mdx
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
docs/**/*.{md,mdx}: Code examples in documentation must explain before showing code and make blocks fully runnable (include imports)
Documentation structure: Headers form navigation guide with logical H2/H3 hierarchy. Content should be user-focused with sections motivating features (why) before mechanics (how). Use prose over code comments for important information
Never use 'This isn't...' or 'not just...' constructions in writing - state what something IS directly. Avoid defensive writing patterns
Files:
docs/patterns/tool-transformation.mdxdocs/servers/providers/filesystem.mdx
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.656Z
Learning: Applies to src/fastmcp/**/*.py : 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
📚 Learning: 2025-12-25T15:53:07.656Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.656Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required
Applied to files:
examples/filesystem-provider/server.pysrc/fastmcp/resources/resource.pysrc/fastmcp/server/providers/filesystem.pysrc/fastmcp/tools/tool.pysrc/fastmcp/server/providers/filesystem_discovery.pysrc/fastmcp/server/providers/local_provider.pydocs/servers/providers/filesystem.mdx
📚 Learning: 2025-12-25T15:53:07.656Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.656Z
Learning: Applies to src/fastmcp/**/*.py : 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
Applied to files:
src/fastmcp/server/providers/__init__.pyexamples/filesystem-provider/mcp/tools/greeting.pysrc/fastmcp/prompts/__init__.pyexamples/filesystem-provider/mcp/resources/config.pysrc/fastmcp/tools/__init__.pysrc/fastmcp/server/providers/filesystem_discovery.pysrc/fastmcp/resources/__init__.py
📚 Learning: 2025-12-25T15:53:07.656Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.656Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients for testing without network complexity; only use HTTP transport when explicitly testing network features
Applied to files:
docs/servers/providers/filesystem.mdx
🧬 Code graph analysis (10)
examples/filesystem-provider/server.py (1)
src/fastmcp/server/providers/filesystem.py (1)
FileSystemProvider(46-215)
src/fastmcp/server/providers/__init__.py (1)
src/fastmcp/server/providers/filesystem.py (1)
FileSystemProvider(46-215)
examples/filesystem-provider/mcp/tools/greeting.py (1)
src/fastmcp/tools/tool.py (4)
tool(775-775)tool(779-790)tool(794-806)tool(809-944)
examples/filesystem-provider/mcp/prompts/assistant.py (2)
src/fastmcp/prompts/prompt.py (4)
prompt(619-619)prompt(623-632)prompt(636-646)prompt(649-773)src/fastmcp/server/providers/local_provider.py (3)
prompt(621-633)prompt(636-648)prompt(650-738)
examples/filesystem-provider/mcp/resources/config.py (2)
src/fastmcp/resources/resource.py (1)
resource(495-626)src/fastmcp/server/providers/local_provider.py (1)
resource(539-618)
src/fastmcp/server/providers/filesystem.py (5)
src/fastmcp/resources/resource.py (2)
resource(495-626)Resource(210-399)src/fastmcp/resources/template.py (1)
ResourceTemplate(97-305)src/fastmcp/server/providers/filesystem_discovery.py (1)
discover_and_import(213-245)src/fastmcp/tools/tool.py (5)
tool(775-775)tool(779-790)tool(794-806)tool(809-944)Tool(141-395)src/fastmcp/utilities/components.py (1)
FastMCPComponent(34-184)
src/fastmcp/tools/__init__.py (1)
src/fastmcp/tools/tool.py (6)
tool(775-775)tool(779-790)tool(794-806)tool(809-944)FunctionTool(398-545)Tool(141-395)
src/fastmcp/prompts/prompt.py (2)
src/fastmcp/server/providers/local_provider.py (3)
prompt(621-633)prompt(636-648)prompt(650-738)src/fastmcp/server/tasks/config.py (1)
TaskConfig(41-120)
src/fastmcp/server/providers/filesystem_discovery.py (1)
src/fastmcp/utilities/components.py (1)
FastMCPComponent(34-184)
src/fastmcp/resources/__init__.py (1)
src/fastmcp/resources/resource.py (2)
resource(495-626)Resource(210-399)
🪛 Ruff (0.14.10)
src/fastmcp/resources/resource.py
561-564: Avoid specifying long messages outside the exception class
(TRY003)
568-577: Prefer TypeError exception for invalid type
(TRY004)
621-624: Avoid specifying long messages outside the exception class
(TRY003)
src/fastmcp/server/providers/filesystem.py
132-132: Do not catch blind exception: Exception
(BLE001)
src/fastmcp/prompts/prompt.py
715-724: Prefer TypeError exception for invalid type
(TRY004)
750-753: Avoid specifying long messages outside the exception class
(TRY003)
759-761: Avoid specifying long messages outside the exception class
(TRY003)
src/fastmcp/tools/tool.py
882-891: Prefer TypeError exception for invalid type
(TRY004)
919-922: Avoid specifying long messages outside the exception class
(TRY003)
928-930: 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 ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (20)
src/fastmcp/tools/__init__.py (1)
1-4: LGTM!The export of
toolalongsideFunctionToolandToolis appropriate. These are core types defining the module's purpose, following the principle of being intentional about re-exports. Based on learnings, this aligns with the guideline to export core types that define a module's purpose.examples/filesystem-provider/mcp/tools/greeting.py (1)
3-3: LGTM!The import path update from
fastmcp.fstofastmcp.toolsaligns with the PR's objective to eliminate thefastmcp.fsmodule and use canonical import paths.src/fastmcp/tools/tool.py (1)
809-944: Well-structured standalone decorator implementation.The
tool()function correctly handles all calling patterns (@tool,@tool(),@tool("name"),@tool(name="x")) with proper type overloads and comprehensive documentation. The use ofpartialfor deferred decoration is appropriate.examples/filesystem-provider/mcp/tools/calculator.py (1)
3-3: LGTM!Import path correctly updated to
fastmcp.tools. The example demonstrates proper usage of the standalone@tooldecorator with various parameters.src/fastmcp/server/providers/__init__.py (1)
32-48: LGTM!The direct import of
FileSystemProvider(rather than lazy-loading) is appropriate since it's a core provider type that will be commonly used. The__all__list is correctly updated. Based on learnings, this aligns with exporting core types that define the module's purpose.examples/filesystem-provider/mcp/resources/config.py (1)
5-5: LGTM!Import path correctly updated to
fastmcp.resources. The examples effectively demonstrate static resources, templated resources with URI parameters, and resources with custom metadata.examples/filesystem-provider/mcp/prompts/assistant.py (1)
3-3: LGTM!Import path correctly updated to
fastmcp.prompts. The examples demonstrate both the bare@promptdecorator and@prompt(...)with custom parameters.examples/filesystem-provider/server.py (1)
19-19: LGTM! Import path correctly updated.The import path change from
fastmcp.fstofastmcp.server.providersaligns with the PR's module reorganization and follows the more specific namespace structure.src/fastmcp/prompts/__init__.py (1)
1-10: LGTM! Appropriate public API expansion.The addition of
FunctionPromptandpromptto the module's public API is intentional and aligns with the standalone decorator pattern introduced in this PR. These are core types for the prompts module's purpose.Based on learnings, the re-exports are appropriately scoped to fundamental types.
src/fastmcp/resources/__init__.py (1)
1-29: LGTM! Consistent with standalone decorator pattern.The addition of the
resourcedecorator to the public API is consistent with the parallel changes inprompts/__init__.pyand appropriately exposes a core type for this module.Based on learnings, the re-export follows the guideline of exposing core types that define the module's purpose.
docs/servers/providers/filesystem.mdx (4)
18-18: LGTM! Clear explanation of standalone decorators.The updated description accurately explains the decoupling achieved by standalone decorators and aligns with the PR's objectives.
28-37: LGTM! Example demonstrates best practices.The quick start example correctly:
- Uses
Path(__file__).parentfor relative paths (better than hardcoded strings)- Shows the updated import path
- Provides clear, runnable code
43-133: LGTM! Decorator examples are comprehensive and accurate.All decorator examples:
- Use the correct new import paths
- Include complete, runnable code
- Show both basic and advanced usage patterns
- Follow MDX documentation guidelines with proper code blocks
195-200: LGTM! Reload mode example is clear.The example correctly demonstrates the reload mode configuration with the updated import path and Path-based root specification.
src/fastmcp/resources/resource.py (3)
15-16: LGTM! Appropriate use of TYPE_CHECKING.The conditional import of
ResourceTemplateunderTYPE_CHECKINGavoids circular import issues while maintaining type hints. The runtime import at line 591 ensures the class is available when needed.
491-492: LGTM! Clear type alias.The
AnyFunctionalias provides clear documentation for the decorator's input type.
495-565: LGTM! Well-designed decorator with proper safeguards.The decorator signature, docstring, and guard clauses are well-implemented:
- Comprehensive parameter documentation
- Clear examples showing both Resource and ResourceTemplate usage
- Proper normalization of annotations dict
- Guard against incorrect usage (
@resourcewithout calling it)- Guard against classmethod decoration with helpful error message and documentation link
src/fastmcp/prompts/prompt.py (1)
588-612:FunctionPrompt.add_to_docketargument passing fix looks correct.Using
docket.add(...)(**(arguments or {}))(Line 611) matches the expectation thatFunctionPrompt.fnis invoked with keyword args.src/fastmcp/server/providers/local_provider.py (2)
704-738:prompt()delegation and registration wrapper are solid.Handling both “direct FunctionPrompt return” vs “decorator/partial” (Line 728-738) is the right pattern to preserve all call styles while centralizing object creation.
588-618: Delegation looks good; no behavioral regression.The flow is clean and consistent. Server decorators always pass an explicit
taskparameter (either user-provided orself._support_tasks_by_default), so theFalsedefault at line 589 only applies to standalone decorator usage, which is correct. No behavior change for server-attached resources/prompts.
| # Clear warnings for files that now import successfully | ||
| successful_files = {fp for fp, _, _ in result.components} | ||
| successful_files = {fp for fp, _ in result.components} | ||
| for fp in successful_files: | ||
| self._warned_files.pop(fp, None) | ||
|
|
There was a problem hiding this comment.
successful_files should reflect “import succeeded”, not “components found”.
Right now you only clear _warned_files for files that produced at least one component (Line 125). If a file previously failed to import, then later imports successfully but defines no components, the stale _warned_files entry remains.
Consider tracking successful imports in DiscoveryResult (e.g., imported_files: set[Path]) or returning it from discover_and_import, then clearing _warned_files for (all_discovered_files - failed_files).
- Use TypeError instead of ValueError for classmethod rejection - Remove unreachable else branch in resource decorator - Use logger.exception for registration failures - Add defensive else branch in _register_component - Remove incorrect subclass comment
Update LocalProvider.tool() and all related tests to use TypeError instead of ValueError when rejecting classmethod-decorated functions. This is consistent with the standalone decorators.
Standalone decorators (
@tool,@resource,@prompt) now create MCP objects without registering them to a server. This enables workflows where you create an object first, then register it—useful for tool transformations and FileSystemProvider discovery.Changes:
fastmcp.tools,fastmcp.resources,fastmcp.promptsFileSystemProvidermoved tofastmcp.server.providersLocalProvider.resource()andprompt()delegate to standalone decoratorsenabled=Falsetests for all component typesfastmcp.fsmodule entirelyNote: this is also a significant refactor of the new FileSystemProvider (#2823) which had its own metadata-based decorators and was housed in the wrong submodule