Consolidate execution method chains into single public API#2728
Conversation
Add call_tool(), read_resource(), render_prompt() methods with apply_middleware parameter, following the pattern from #2719.
|
Warning Rate limit exceeded@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 21 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 (3)
📒 Files selected for processing (2)
WalkthroughCentralizes tool invocation, resource reading, and prompt rendering behind three new FastMCP public methods: Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/server/server.py (1)
1552-1555: Minor inconsistency in error message formatting.The
call_tool()method raisesNotFoundError(f"Unknown tool: {name!r}")with!rformatting, but this handler re-raises withNotFoundError(f"Unknown tool: {key}")without it. Consider using consistent formatting:- except NotFoundError as e: - raise NotFoundError(f"Unknown tool: {key}") from e + except NotFoundError as e: + raise NotFoundError(f"Unknown tool: {key!r}") from eAlso applies to the
DisabledErrorhandling on line 1553.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/server/middleware/test_middleware.pyis excluded by none and included by none
📒 Files selected for processing (2)
src/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/server/server.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Use Python ≥ 3.10 with full type annotations
Never use bareexcept- be specific with exception types
Files:
src/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/server/server.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions
📚 Learning: 2025-11-26T21:51:44.174Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions
Applied to files:
src/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/server/server.py
📚 Learning: 2025-12-21T21:37:55.031Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/**/__init__.py : Core types that define a module's purpose should be exported (e.g., `Middleware` from `fastmcp.server.middleware`), while specialized features can live in submodules
Applied to files:
src/fastmcp/server/server.py
🧬 Code graph analysis (2)
src/fastmcp/server/providers/fastmcp_provider.py (3)
src/fastmcp/server/server.py (3)
call_tool(1145-1209)read_resource(1211-1305)render_prompt(1307-1370)src/fastmcp/resources/resource.py (1)
Resource(137-315)src/fastmcp/prompts/prompt.py (1)
Prompt(118-306)
src/fastmcp/server/server.py (9)
src/fastmcp/tools/tool.py (3)
ToolResult(78-124)Tool(127-352)to_mcp_result(111-124)src/fastmcp/server/context.py (4)
fastmcp(169-174)Context(115-1061)read_resource(308-318)get_prompt(294-306)src/fastmcp/server/providers/fastmcp_provider.py (6)
get_tool(432-435)get_resource(451-454)_read(143-149)_read(280-317)get_resource_template(471-477)get_prompt(492-495)src/fastmcp/server/providers/base.py (4)
get_tool(142-152)get_resource(161-171)get_resource_template(180-194)get_prompt(203-213)src/fastmcp/server/providers/transforming.py (4)
get_tool(177-185)get_resource(199-207)get_resource_template(223-235)get_prompt(249-257)src/fastmcp/server/providers/openapi/provider.py (3)
get_tool(355-357)get_resource(363-365)get_resource_template(371-376)src/fastmcp/server/middleware/tool_injection.py (2)
read_resource(98-103)get_prompt(64-72)src/fastmcp/resources/resource.py (4)
ResourceContent(39-134)_read(225-259)key(286-288)Resource(137-315)src/fastmcp/resources/template.py (4)
_read(171-203)_read(290-313)matches(154-156)key(251-253)
🪛 Ruff (0.14.10)
src/fastmcp/server/server.py
1206-1206: Avoid specifying long messages outside the exception class
(TRY003)
1207-1207: Avoid specifying long messages outside the exception class
(TRY003)
1209-1209: Avoid specifying long messages outside the exception class
(TRY003)
1267-1267: Consider moving this statement to an else block
(TRY300)
1274-1276: Avoid specifying long messages outside the exception class
(TRY003)
1277-1279: Avoid specifying long messages outside the exception class
(TRY003)
1291-1291: Consider moving this statement to an else block
(TRY300)
1298-1300: Avoid specifying long messages outside the exception class
(TRY003)
1301-1303: Avoid specifying long messages outside the exception class
(TRY003)
1305-1305: Avoid specifying long messages outside the exception class
(TRY003)
1365-1365: Avoid specifying long messages outside the exception class
(TRY003)
1366-1368: Avoid specifying long messages outside the exception class
(TRY003)
1370-1370: Avoid specifying long messages outside the exception class
(TRY003)
1553-1553: Avoid specifying long messages outside the exception class
(TRY003)
1555-1555: Avoid specifying long messages outside the exception class
(TRY003)
1599-1599: Avoid specifying long messages outside the exception class
(TRY003)
1601-1601: Avoid specifying long messages outside the exception class
(TRY003)
1643-1643: Avoid specifying long messages outside the exception class
(TRY003)
1645-1645: Avoid specifying long messages outside the exception class
(TRY003)
1660-1660: Prefer TypeError exception for invalid type
(TRY004)
1660-1660: Avoid specifying long messages outside the exception class
(TRY003)
1661-1661: Consider moving this statement to an else block
(TRY300)
1664-1664: Avoid specifying long messages outside the exception class
(TRY003)
1667-1667: Avoid specifying long messages outside the exception class
(TRY003)
1685-1685: Prefer TypeError exception for invalid type
(TRY004)
1685-1685: Avoid specifying long messages outside the exception class
(TRY003)
1689-1689: Avoid specifying long messages outside the exception class
(TRY003)
1692-1692: 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 (11)
src/fastmcp/server/providers/fastmcp_provider.py (4)
100-105: LGTM!The delegation to
call_tool()is correct and the return type matches the server method's signature.
151-163: LGTM!The delegation and result unwrapping are correct. The
result[0]access is safe becauseread_resource()guarantees a non-empty list when returninglist[ResourceContent].
209-220: LGTM!The delegation to
render_prompt()is correct with proper type alignment.
280-317: LGTM!The delegation with proper contextvar management and result unwrapping is correct. The
finallyblock ensures proper token reset.src/fastmcp/server/server.py (7)
1145-1209: LGTM!The new public API is well-structured with proper type annotations, comprehensive exception handling, and correct middleware application pattern.
1211-1305: LGTM!The two-pass approach (concrete resources first, then templates) is correct and consistent with existing lookup patterns. The defensive
template.matches(uri)check provides extra safety.
1307-1370: LGTM!The prompt rendering follows the established pattern with proper exception handling and type safety.
1579-1601: LGTM with same minor note.Contextvar handling and result conversion are correct. The same
!rformatting inconsistency exists here as in_call_tool_mcp(line 1599 vsread_resourceline 1305).
1626-1645: LGTM!The handler follows the established pattern correctly with proper contextvar management and exception handling.
1647-1667: LGTM!The defensive check for unexpected
CreateTaskResultwithRuntimeErroris appropriate - this represents a logic invariant violation ("should never happen without task metadata") rather than a type error. The static analysis suggestion forTypeErrorcan be safely ignored here.
1669-1692: LGTM!The implementation correctly follows the established pattern with proper result conversion and defensive error handling.
Also fix inspect.py to skip middleware during introspection.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/server/server.py (1)
1143-1207: Context is created unconditionally but only used whenrun_middleware=True.The
Contextis entered at line 1169 regardless of therun_middlewareflag, but it's only used within theif run_middleware:block (lines 1170-1187). Whenrun_middleware=False, the context is entered and exited without being used, adding unnecessary overhead.Consider restructuring to only create the context when needed:
🔎 Suggested refactor
async def call_tool( self, name: str, arguments: dict[str, Any] | None = None, *, run_middleware: bool = True, ) -> ToolResult | mcp.types.CreateTaskResult: ... - async with fastmcp.server.context.Context(fastmcp=self) as ctx: - if run_middleware: - mw_context = MiddlewareContext[CallToolRequestParams]( - message=mcp.types.CallToolRequestParams( - name=name, arguments=arguments or {} - ), - source="client", - type="request", - method="tools/call", - fastmcp_context=ctx, - ) - return await self._run_middleware( - context=mw_context, - call_next=lambda context: self.call_tool( - context.message.name, - context.message.arguments or {}, - run_middleware=False, - ), - ) - - # Core logic: find and execute tool + if run_middleware: + async with fastmcp.server.context.Context(fastmcp=self) as ctx: + mw_context = MiddlewareContext[CallToolRequestParams]( + message=mcp.types.CallToolRequestParams( + name=name, arguments=arguments or {} + ), + source="client", + type="request", + method="tools/call", + fastmcp_context=ctx, + ) + return await self._run_middleware( + context=mw_context, + call_next=lambda context: self.call_tool( + context.message.name, + context.message.arguments or {}, + run_middleware=False, + ), + ) + + # Core logic: find and execute tool
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
tests/server/middleware/test_middleware.pyis excluded by none and included by nonetests/server/test_tool_transformation.pyis excluded by none and included by none
📒 Files selected for processing (4)
src/fastmcp/server/low_level.pysrc/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/server/server.pysrc/fastmcp/utilities/inspect.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Use Python ≥ 3.10 with full type annotations
Never use bareexcept- be specific with exception types
Files:
src/fastmcp/server/low_level.pysrc/fastmcp/utilities/inspect.pysrc/fastmcp/server/server.pysrc/fastmcp/server/providers/fastmcp_provider.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: When modifying MCP functionality (Tools, Resources, Resource Templates, Prompts), changes typically need to be applied across all object types
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/**/__init__.py : Core types that define a module's purpose should be exported (e.g., `Middleware` from `fastmcp.server.middleware`), while specialized features can live in submodules
📚 Learning: 2025-12-21T21:37:55.031Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/**/__init__.py : Core types that define a module's purpose should be exported (e.g., `Middleware` from `fastmcp.server.middleware`), while specialized features can live in submodules
Applied to files:
src/fastmcp/server/low_level.pysrc/fastmcp/server/server.py
📚 Learning: 2025-12-21T21:37:55.031Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/__init__.py : All module exports should be intentional - only re-export to `fastmcp.*` for fundamental types like `FastMCP` and `Client`, prefer users importing from specific submodules for specialized features
Applied to files:
src/fastmcp/server/server.py
📚 Learning: 2025-11-26T21:51:44.174Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions
Applied to files:
src/fastmcp/server/providers/fastmcp_provider.py
🧬 Code graph analysis (4)
src/fastmcp/server/low_level.py (2)
src/fastmcp/server/context.py (1)
fastmcp(169-174)src/fastmcp/server/server.py (1)
_run_middleware(695-704)
src/fastmcp/utilities/inspect.py (1)
src/fastmcp/server/server.py (4)
get_tools(793-835)get_prompts(1040-1084)get_resources(890-937)get_resource_templates(962-1013)
src/fastmcp/server/server.py (4)
src/fastmcp/resources/resource.py (2)
Resource(137-315)key(286-288)src/fastmcp/prompts/prompt.py (3)
Prompt(118-306)PromptResult(73-115)to_mcp_prompt_result(109-115)src/fastmcp/server/middleware/middleware.py (1)
MiddlewareContext(47-63)src/fastmcp/server/providers/base.py (4)
get_tool(142-152)get_resource(161-171)get_resource_template(180-194)get_prompt(203-213)
src/fastmcp/server/providers/fastmcp_provider.py (3)
src/fastmcp/server/server.py (6)
read_resource(1209-1303)render_prompt(1305-1368)get_tools(793-835)get_resources(890-937)get_resource_templates(962-1013)get_prompts(1040-1084)src/fastmcp/resources/resource.py (1)
Resource(137-315)src/fastmcp/prompts/prompt.py (1)
Prompt(118-306)
🪛 Ruff (0.14.10)
src/fastmcp/server/server.py
815-815: Unused lambda argument: context
(ARG005)
912-912: Unused lambda argument: context
(ARG005)
986-986: Unused lambda argument: context
(ARG005)
1062-1062: Unused lambda argument: context
(ARG005)
1204-1204: Avoid specifying long messages outside the exception class
(TRY003)
1205-1205: Avoid specifying long messages outside the exception class
(TRY003)
1207-1207: Avoid specifying long messages outside the exception class
(TRY003)
1265-1265: Consider moving this statement to an else block
(TRY300)
1272-1274: Avoid specifying long messages outside the exception class
(TRY003)
1275-1277: Avoid specifying long messages outside the exception class
(TRY003)
1289-1289: Consider moving this statement to an else block
(TRY300)
1296-1298: Avoid specifying long messages outside the exception class
(TRY003)
1299-1301: Avoid specifying long messages outside the exception class
(TRY003)
1303-1303: Avoid specifying long messages outside the exception class
(TRY003)
1363-1363: Avoid specifying long messages outside the exception class
(TRY003)
1364-1366: Avoid specifying long messages outside the exception class
(TRY003)
1368-1368: Avoid specifying long messages outside the exception class
(TRY003)
1551-1551: Avoid specifying long messages outside the exception class
(TRY003)
1553-1553: Avoid specifying long messages outside the exception class
(TRY003)
1597-1597: Avoid specifying long messages outside the exception class
(TRY003)
1599-1599: Avoid specifying long messages outside the exception class
(TRY003)
1641-1641: Avoid specifying long messages outside the exception class
(TRY003)
1643-1643: Avoid specifying long messages outside the exception class
(TRY003)
1658-1658: Prefer TypeError exception for invalid type
(TRY004)
1658-1658: Avoid specifying long messages outside the exception class
(TRY003)
1659-1659: Consider moving this statement to an else block
(TRY300)
1662-1662: Avoid specifying long messages outside the exception class
(TRY003)
1665-1665: Avoid specifying long messages outside the exception class
(TRY003)
1683-1683: Prefer TypeError exception for invalid type
(TRY004)
1683-1683: Avoid specifying long messages outside the exception class
(TRY003)
1687-1687: Avoid specifying long messages outside the exception class
(TRY003)
1690-1690: 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 with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
🔇 Additional comments (16)
src/fastmcp/server/low_level.py (1)
109-111: LGTM!The method rename from
_apply_middlewareto_run_middlewareis consistent with the broader refactoring inserver.py. The call signature and behavior remain unchanged.src/fastmcp/server/server.py (9)
695-704: LGTM!The
_run_middlewaremethod correctly builds and executes the middleware chain. The reverse iteration ensures middleware runs in registration order (first registered = outermost wrapper).
793-817: LGTM!The
run_middlewareparameter provides a clean API for middleware-aware vs. direct access. The lambda ignoring thecontextparameter is intentional—the context is only needed for middleware processing, not the actual data fetch.
1209-1303: LGTM!The
read_resourcemethod correctly implements the two-pass lookup (concrete resources first, then templates) with proper error handling and masking. The same Context optimization noted forcall_toolapplies here as well.
1305-1368: LGTM!The
render_promptmethod follows the same consistent pattern ascall_toolandread_resource, with proper provider iteration and error handling.
1499-1553: LGTM!The
_call_tool_mcphandler correctly manages task metadata contextvars with proper token-based cleanup in the finally block. The exception handling appropriately standardizes error messages.
1555-1599: LGTM!The
_read_resource_handlercorrectly implements custom handling forCreateTaskResultsupport, with proper contextvar management and MCP result wrapping.
1601-1643: LGTM!The
_get_prompt_handlerfollows the same consistent pattern as_read_resource_handler, with proper contextvar management and MCP result handling.
1645-1690: LGTM!The helper methods correctly delegate to the public APIs and include appropriate safety checks for unexpected
CreateTaskResultreturns. TheRuntimeErroris appropriate for internal invariant violations.
1431-1497: LGTM!The MCP listing handlers correctly invoke
get_*methods withrun_middleware=True, ensuring middleware is applied for client-facing protocol handlers.src/fastmcp/server/providers/fastmcp_provider.py (5)
97-105: LGTM!The delegation to
call_tool()is correct and maintains the proper mapping via_original_name.
151-163: LGTM!The delegation correctly handles both
CreateTaskResultpass-through and normal results. Extractingresult[0]is appropriate sinceread_resource()returns a list.
209-220: LGTM!The delegation to
render_prompt()is straightforward and correct.
280-317: LGTM!The template's
_read()correctly manages the_docket_fn_keycontextvar and delegates toread_resource()with proper result unwrapping.
422-495: LGTM!The provider's list methods correctly invoke the wrapped server's
get_*methods withrun_middleware=True, ensuring middleware is applied for mounted server discovery.src/fastmcp/utilities/inspect.py (1)
109-113: LGTM!Bypassing middleware for inspection is the correct behavior. The comment clearly explains the rationale: auth and rate-limiting middleware should not affect introspection of available components.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/server/server.py (1)
1259-1265: Optional: Consider restructuring CreateTaskResult handling.The mime_type assignment at lines 1263-1265 could be moved into an else block after the CreateTaskResult check for clearer flow:
try: result = await resource._read() if isinstance(result, mcp.types.CreateTaskResult): return result - if result.mime_type is None: - result.mime_type = resource.mime_type - return [result] + else: + if result.mime_type is None: + result.mime_type = resource.mime_type + return [result]This makes it explicit that the mime_type logic only applies to non-task results.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/server.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Use Python ≥ 3.10 with full type annotations
Never use bareexcept- be specific with exception types
Files:
src/fastmcp/server/server.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: When modifying MCP functionality (Tools, Resources, Resource Templates, Prompts), changes typically need to be applied across all object types
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/**/__init__.py : Core types that define a module's purpose should be exported (e.g., `Middleware` from `fastmcp.server.middleware`), while specialized features can live in submodules
📚 Learning: 2025-12-21T21:37:55.031Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/**/__init__.py : Core types that define a module's purpose should be exported (e.g., `Middleware` from `fastmcp.server.middleware`), while specialized features can live in submodules
Applied to files:
src/fastmcp/server/server.py
📚 Learning: 2025-12-21T21:37:55.031Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T21:37:55.031Z
Learning: Applies to src/fastmcp/__init__.py : All module exports should be intentional - only re-export to `fastmcp.*` for fundamental types like `FastMCP` and `Client`, prefer users importing from specific submodules for specialized features
Applied to files:
src/fastmcp/server/server.py
🧬 Code graph analysis (1)
src/fastmcp/server/server.py (5)
src/fastmcp/tools/tool.py (4)
Tool(127-352)ToolResult(78-124)_run(268-291)to_mcp_result(111-124)src/fastmcp/prompts/prompt.py (4)
Prompt(118-306)PromptResult(73-115)_render(238-277)to_mcp_prompt_result(109-115)src/fastmcp/server/context.py (4)
fastmcp(169-174)Context(115-1061)read_resource(308-318)get_prompt(294-306)src/fastmcp/server/middleware/middleware.py (1)
MiddlewareContext(47-63)src/fastmcp/server/providers/base.py (4)
get_tool(142-152)get_resource(161-171)get_resource_template(180-194)get_prompt(203-213)
🪛 Ruff (0.14.10)
src/fastmcp/server/server.py
815-815: Unused lambda argument: context
(ARG005)
858-858: Avoid specifying long messages outside the exception class
(TRY003)
912-912: Unused lambda argument: context
(ARG005)
986-986: Unused lambda argument: context
(ARG005)
1062-1062: Unused lambda argument: context
(ARG005)
1204-1204: Avoid specifying long messages outside the exception class
(TRY003)
1205-1205: Avoid specifying long messages outside the exception class
(TRY003)
1207-1207: Avoid specifying long messages outside the exception class
(TRY003)
1265-1265: Consider moving this statement to an else block
(TRY300)
1272-1274: Avoid specifying long messages outside the exception class
(TRY003)
1275-1277: Avoid specifying long messages outside the exception class
(TRY003)
1289-1289: Consider moving this statement to an else block
(TRY300)
1296-1298: Avoid specifying long messages outside the exception class
(TRY003)
1299-1301: Avoid specifying long messages outside the exception class
(TRY003)
1303-1303: Avoid specifying long messages outside the exception class
(TRY003)
1363-1363: Avoid specifying long messages outside the exception class
(TRY003)
1364-1366: Avoid specifying long messages outside the exception class
(TRY003)
1368-1368: Avoid specifying long messages outside the exception class
(TRY003)
1551-1551: Avoid specifying long messages outside the exception class
(TRY003)
1553-1553: Avoid specifying long messages outside the exception class
(TRY003)
1597-1597: Avoid specifying long messages outside the exception class
(TRY003)
1599-1599: Avoid specifying long messages outside the exception class
(TRY003)
1641-1641: Avoid specifying long messages outside the exception class
(TRY003)
1643-1643: Avoid specifying long messages outside the exception class
(TRY003)
1658-1658: Prefer TypeError exception for invalid type
(TRY004)
1658-1658: Avoid specifying long messages outside the exception class
(TRY003)
1659-1659: Consider moving this statement to an else block
(TRY300)
1662-1662: Avoid specifying long messages outside the exception class
(TRY003)
1665-1665: Avoid specifying long messages outside the exception class
(TRY003)
1683-1683: Prefer TypeError exception for invalid type
(TRY004)
1683-1683: Avoid specifying long messages outside the exception class
(TRY003)
1687-1687: Avoid specifying long messages outside the exception class
(TRY003)
1690-1690: 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). (3)
- 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 (8)
src/fastmcp/server/server.py (8)
793-817: Lambda argument unused by design in middleware recursion.The
contextparameter in the lambda at line 815 is unused because the recursive call bypasses middleware. This matches the pattern in the new public APIs (call_tool, read_resource, render_prompt) where the lambda extracts arguments fromcontext.messagebefore recursing. However, for list operations there are no arguments to extract from the context, so the simpler pattern is appropriate.The static analysis warning (ARG005) can be safely ignored here.
1143-1207: LGTM: Well-structured public API for tool execution.The implementation correctly:
- Applies middleware by default while allowing bypass via
run_middleware=False- Extracts arguments from middleware context before recursive calls
- Handles task routing via
tool._run()- Provides comprehensive error handling with optional masking
The static analysis warnings about exception messages (TRY003) can be ignored—inline messages are appropriate for user-facing errors.
1305-1368: LGTM: Consistent implementation for prompt rendering.The render_prompt method follows the same well-structured pattern as call_tool, with proper middleware support, task routing, and error handling.
1439-1496: LGTM: MCP list handlers properly invoke middleware.The updates ensure middleware chains execute for all MCP list operations, maintaining consistency with the new public API pattern.
1526-1553: LGTM: Proper task metadata and contextvar management.The handler correctly:
- Extracts SEP-1686 task metadata from the SDK request context
- Sets contextvars for tool._run() to access
- Resets contextvars in a finally block for cleanup
- Converts DisabledError to NotFoundError to avoid leaking component presence
The static analysis warnings about exception messages can be ignored.
1577-1599: LGTM: Consistent task-aware resource handler.The implementation follows the same correct pattern as _call_tool_mcp with proper contextvar management and error handling.
1624-1643: LGTM: Consistent task-aware prompt handler.The implementation maintains the same correct pattern with proper contextvar lifecycle management and security-conscious error handling.
1656-1665: Defensive guards appropriate for internal consistency.The RuntimeError checks at lines 1658 and 1683 are correct defensive programming to catch unexpected CreateTaskResult returns when no task metadata is present. The static analysis suggestion (TRY004) to use TypeError is incorrect—RuntimeError is appropriate for internal invariant violations rather than type validation.
Test Failure AnalysisSummary: Two tests are failing because error messages now include quotes around tool names (e.g., Root Cause: PR #2728 changed error messages in
The failing tests use regex patterns that expect the old format:
Suggested Solution: Update the regex patterns in the two failing tests to match the new quoted format:
Detailed AnalysisFailed Test OutputRelated Changes in PRThe PR consolidated execution methods and changed error message formatting to use # Before
raise NotFoundError(f"Unknown tool: {name}")
# After
raise NotFoundError(f"Unknown tool: {name!r}")The Impact
Related Files
|
Following the pattern from #2719, this consolidates the execution method chains into single public methods with a
run_middlewareparameter.New Public API
Changes
Each method consolidates the previous 3-4 method chain (e.g.,
_call_tool_middleware→_call_tool→_execute_tool) into a single method using lambda recursion for middleware application.Also renames
apply_middleware→run_middlewareacross the codebase for clarity.Deleted private methods:
_call_tool_middleware,_call_tool,_execute_tool_read_resource_middleware,_read_resource,_execute_resource,_execute_template_get_prompt_content_middleware,_get_prompt,_execute_promptNot a breaking change since all removed methods were private (
_*).