Add timeout parameter for tool foreground execution#2872
Conversation
Tools can now specify a timeout to limit execution time:
@mcp.tool(timeout=30.0)
async def fetch_data(url: str) -> dict:
...
When exceeded, returns MCP error -32000. Applies to foreground
execution only; background tasks should use Docket's Timeout
dependency for task-level timeouts.
Test Failure AnalysisSummary: Static analysis failed due to a linting error (B904) and type check warnings in the new timeout test file. Root Cause:
Suggested Solution: Fix 1: Exception Chaining (src/fastmcp/tools/function_tool.py:271) Change line 271 from: raise McpError(
ErrorData(
code=-32000,
message=f"Tool '{self.name}' execution timed out after {self.timeout}s",
)
)To: raise McpError(
ErrorData(
code=-32000,
message=f"Tool '{self.name}' execution timed out after {self.timeout}s",
)
) from NoneThe Fix 2: Type Check Warnings (tests/tools/test_tool_timeout.py) For each assertion like
Since this is test code and we know the content will be text, the cleanest approach is to add proper assertions. Detailed AnalysisRuff Error: Type Check Warnings (7 occurrences): Related Files
|
There was a problem hiding this comment.
💡 Codex Review
https://github.com/jlowin/fastmcp/blob/ab50336e807b44b44a7959722f4639ad94640959/src/fastmcp/tools/function_tool.py#L406-L410
Propagate timeout in object decorator mode
When fastmcp.settings.decorator_mode == "object", @tool(timeout=...) is handled via create_tool(), but the ToolMeta constructed there omits timeout, so the resulting FunctionTool gets timeout=None and timeouts are silently ignored. This only affects the deprecated object decorator mode, but existing users on that path will see the new timeout option not enforced despite being set.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Warning Rate limit exceeded
⌛ 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 (1)
📒 Files selected for processing (1)
WalkthroughAdds a per-tool execution timeout feature: a new Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 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 |
There was a problem hiding this comment.
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/tools/function_tool.py (1)
396-413: Propagatetimeoutindecorator_mode='object'path (currently dropped).
attach_metadata()includestimeout=timeout, butcreate_tool()(used whenfastmcp.settings.decorator_mode == "object") buildsToolMetawithout thetimeout, so timeouts won’t apply in that mode.Proposed fix
tool_meta = ToolMeta( name=tool_name, title=title, description=description, icons=icons, tags=tags, output_schema=output_schema, annotations=annotations, meta=meta, task=resolve_task_config(task), exclude_args=exclude_args, serializer=serializer, + timeout=timeout, auth=auth, )Also applies to: 414-433
🧹 Nitpick comments (4)
src/fastmcp/tools/tool.py (1)
152-157: Add basic validation fortimeout(reject <= 0) to fail fast.Right now
timeout=0/ negative values are accepted and will produce unclear runtime behavior depending on anyio. Consider a Pydantic constraint (e.g.,gt=0) or a validator that raisesValueError.docs/servers/tools.mdx (2)
118-120: Timeout parameter docs are clear; consider adding constraints (must be > 0).Right now docs don’t say what happens for
timeout=0/ negative values—worth clarifying once code validates it.
774-823: Align Timeouts section with doc style + avoid overstating cancellation behavior.
- Prefer second-person voice (“You can set
timeout…”, “If you exceed it, you get…”).- “the tool stops processing” may be inaccurate for sync tools if the worker thread continues after cancellation—suggest wording like “FastMCP stops waiting and returns an error” unless you can guarantee hard cancellation.
src/fastmcp/server/server.py (1)
1981-2106: Timeout forwarding throughFastMCP.tool()looks correct.Minor: consider adding
timeoutto the method docstring “Args:” section for completeness.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/tools/test_tool_timeout.pyis excluded by none and included by none
📒 Files selected for processing (5)
docs/servers/tools.mdxsrc/fastmcp/server/providers/local_provider.pysrc/fastmcp/server/server.pysrc/fastmcp/tools/function_tool.pysrc/fastmcp/tools/tool.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python ≥3.10 with full type annotations required for all code
Never use bareexcept- be specific with exception types in Python code
Files:
src/fastmcp/server/server.pysrc/fastmcp/tools/tool.pysrc/fastmcp/tools/function_tool.pysrc/fastmcp/server/providers/local_provider.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/servers/tools.mdx
🧬 Code graph analysis (1)
src/fastmcp/tools/function_tool.py (3)
src/fastmcp/utilities/logging.py (1)
get_logger(14-26)src/fastmcp/utilities/types.py (1)
get_cached_typeadapter(45-117)src/fastmcp/utilities/async_utils.py (1)
call_sync_fn_in_threadpool(13-21)
🪛 GitHub Actions: Run static analysis
src/fastmcp/tools/function_tool.py
[error] 271-271: ruff-check: B904 Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling.
🪛 Ruff (0.14.11)
src/fastmcp/tools/function_tool.py
271-276: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
⏰ 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 with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (5)
src/fastmcp/tools/tool.py (1)
193-230: Timeout plumbing inTool.from_function()looks correct.Forwarding
timeout=timeoutintoFunctionTool.from_function()is the right place to keep the API consistent.src/fastmcp/tools/function_tool.py (2)
61-79: ToolMeta / from_function timeout wiring looks consistent.
timeoutis treated as an “individual param” correctly and is stored inToolMeta, then persisted onto the resultingFunctionTool.Also applies to: 106-180, 228-243
245-287: Fix timeout exception handling (ruff B904) and add explicit exception chaining.The code has a ruff B904 violation because it raises an exception without explicit exception chaining. Add
from Noneto theraise McpError()statement to fix this.However, the exception type should remain
TimeoutError, notanyio.TimeoutError. According to anyio's documentation,anyio.fail_after()raises the built-inTimeoutErrorexception, so catchinganyio.TimeoutError(which does not exist) would fail.The concern about
except TimeoutErrorpotentially catching user-raisedTimeoutErroris valid, but a solution would need different approaches (e.g., catching at a more specific scope or using a different timeout mechanism).For the sync threadpool tool, verify that
call_sync_fn_in_threadpool()is configured to abandon work on cancellation; otherwise,fail_after()may not enforce the deadline.except TimeoutError: logger.warning( f"Tool '{self.name}' timed out after {self.timeout}s. " f"Consider using task=True for long-running operations. " f"See https://gofastmcp.com/servers/tasks" ) - raise McpError( + raise McpError( ErrorData( code=-32000, message=f"Tool '{self.name}' execution timed out after {self.timeout}s", ) - ) + ) from NoneLikely an incorrect or invalid review comment.
src/fastmcp/server/providers/local_provider.py (2)
182-213: Good:add_tool()correctly carriesmeta.timeoutintoTool.from_function().This ensures standalone
@tool(timeout=...)metadata isn’t lost when later registered viaadd_tool.
421-654: Good: timeout is preserved across decorator call patterns (includingpartial(...)).The
timeoutthreading into both Tool construction paths and the returnedpartial()avoids surprising drops.
Tools can now limit execution time with a
timeoutparameter:When exceeded, clients receive MCP error code
-32000. Both sync and async tools are supported—sync functions run in thread pools so the timeout applies regardless of execution model.Important: This timeout applies to foreground execution only. Background tasks (
task=True) execute in Docket workers where this timeout isn't enforced. For task timeouts, use Docket'sTimeoutdependency directly in the function signature.