-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add TaskConfig for SEP-1686 execution modes #2570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Expose the full MCP task execution modes (forbidden/optional/required) via TaskConfig instead of just boolean task=True/False.
WalkthroughThis PR introduces TaskConfig, a new configuration structure for managing background task execution across the FastMCP framework (implementing SEP-1686). The change replaces boolean Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
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 (3)
src/fastmcp/server/tasks/config.py (1)
78-83: Consider handlingclassmethodin unwrap logic.The unwrap logic handles
staticmethodbut notclassmethod. If someone decorates a classmethod with task execution, theiscoroutinefunctioncheck may fail.# Unwrap callable classes and staticmethods fn_to_check = fn if not inspect.isroutine(fn) and callable(fn): fn_to_check = fn.__call__ if isinstance(fn_to_check, staticmethod): fn_to_check = fn_to_check.__func__ + if isinstance(fn_to_check, classmethod): + fn_to_check = fn_to_check.__func__docs/servers/tasks.mdx (1)
90-93: Consider renaming example function for clarity.The function
sync_onlyis declared asasync def, which may confuse readers. Consider renaming tono_backgroundorimmediate_onlyto better convey that it executes without background task support rather than implying it's a synchronous function.# No task support (default when task=False or omitted) @mcp.tool(task=TaskConfig(mode="forbidden")) -async def sync_only() -> str: +async def no_background() -> str: return "Never runs as background task"src/fastmcp/server/server.py (1)
596-622: Mode enforcement logic is correct but duplicated.The mode enforcement pattern is correctly implemented:
mode="required"+ no task metadata → errormode="forbidden"+ task metadata → error- Task metadata present + mode allows → route to background handler
However, this same pattern is repeated in three handlers (resources, prompts, tools). Consider extracting to a helper function for maintainability.
Example helper extraction:
def _check_task_mode( mode: str, task_meta: Any, component_type: str, name: str, ) -> None: """Validate task mode constraints, raising McpError on violation.""" if mode == "required" and not task_meta: raise McpError( ErrorData( code=METHOD_NOT_FOUND, message=f"{component_type} '{name}' requires task-augmented execution", ) ) if mode == "forbidden" and task_meta: raise McpError( ErrorData( code=METHOD_NOT_FOUND, message=f"{component_type} '{name}' does not support task-augmented execution", ) )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (9)
tests/client/tasks/test_task_result_caching.pyis excluded by none and included by nonetests/server/tasks/test_server_tasks_parameter.pyis excluded by none and included by nonetests/server/tasks/test_sync_function_task_disabled.pyis excluded by none and included by nonetests/server/tasks/test_task_config_modes.pyis excluded by none and included by nonetests/server/tasks/test_task_prompts.pyis excluded by none and included by nonetests/server/tasks/test_task_resources.pyis excluded by none and included by nonetests/server/tasks/test_task_tools.pyis excluded by none and included by nonetests/server/test_tool_annotations.pyis excluded by none and included by nonetests/tools/test_tool.pyis excluded by none and included by none
📒 Files selected for processing (10)
docs/servers/tasks.mdx(2 hunks)src/fastmcp/__init__.py(2 hunks)src/fastmcp/prompts/prompt.py(6 hunks)src/fastmcp/resources/resource.py(5 hunks)src/fastmcp/resources/template.py(7 hunks)src/fastmcp/server/server.py(16 hunks)src/fastmcp/server/tasks/__init__.py(2 hunks)src/fastmcp/server/tasks/config.py(1 hunks)src/fastmcp/server/tasks/converters.py(1 hunks)src/fastmcp/tools/tool.py(7 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
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/tasks.mdx
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bareexcept- be specific with exception types
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code organization and style
Files:
src/fastmcp/server/tasks/config.pysrc/fastmcp/server/tasks/__init__.pysrc/fastmcp/server/tasks/converters.pysrc/fastmcp/__init__.pysrc/fastmcp/resources/resource.pysrc/fastmcp/tools/tool.pysrc/fastmcp/server/server.pysrc/fastmcp/resources/template.pysrc/fastmcp/prompts/prompt.py
🧠 Learnings (1)
📚 Learning: 2025-12-04T00:17:41.238Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T00:17:41.238Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients for testing using in-memory transport; only use HTTP transport when explicitly testing network features
Applied to files:
src/fastmcp/__init__.py
🧬 Code graph analysis (7)
src/fastmcp/server/tasks/__init__.py (1)
src/fastmcp/server/tasks/config.py (1)
TaskConfig(19-89)
src/fastmcp/server/tasks/converters.py (1)
src/fastmcp/tools/tool.py (2)
ToolResult(74-120)_convert_to_content(611-637)
src/fastmcp/__init__.py (2)
src/fastmcp/server/context.py (1)
fastmcp(152-157)src/fastmcp/server/tasks/config.py (1)
TaskConfig(19-89)
src/fastmcp/resources/resource.py (2)
src/fastmcp/server/tasks/config.py (3)
TaskConfig(19-89)from_bool(51-60)validate_function(62-89)src/fastmcp/utilities/types.py (1)
get_fn_name(34-35)
src/fastmcp/tools/tool.py (1)
src/fastmcp/server/tasks/config.py (3)
TaskConfig(19-89)from_bool(51-60)validate_function(62-89)
src/fastmcp/resources/template.py (1)
src/fastmcp/server/tasks/config.py (3)
TaskConfig(19-89)from_bool(51-60)validate_function(62-89)
src/fastmcp/prompts/prompt.py (1)
src/fastmcp/server/tasks/config.py (3)
TaskConfig(19-89)from_bool(51-60)validate_function(62-89)
🪛 Ruff (0.14.7)
src/fastmcp/server/tasks/config.py
86-89: Avoid specifying long messages outside the exception class
(TRY003)
src/fastmcp/tools/tool.py
333-333: 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 windows-latest
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (19)
src/fastmcp/server/tasks/converters.py (1)
36-38: LGTM!The in-function import with the documented rationale is an appropriate solution for breaking the circular import chain. The comment clearly explains the dependency cycle.
src/fastmcp/server/tasks/config.py (1)
18-48: LGTM!The
TaskConfigdataclass is well-designed with clear mode semantics and comprehensive docstring. The three execution modes align with SEP-1686 specification.src/fastmcp/__init__.py (1)
17-17: LGTM!
TaskConfigis appropriately added to the public API, following the existing import/export pattern forFastMCPandContext.Also applies to: 35-35
src/fastmcp/server/tasks/__init__.py (1)
6-6: LGTM!Both
TaskConfigandTaskModeare correctly re-exported, enabling the import path shown in the docstring examples.Also applies to: 30-31
docs/servers/tasks.mdx (1)
64-98: LGTM!The Execution Modes section is well-structured with a clear behavior matrix table and practical code examples. The boolean shortcut mappings are documented accurately.
src/fastmcp/prompts/prompt.py (2)
203-210: LGTM!The normalization logic correctly converts the
taskparameter toTaskConfigand validates the function. The default mode of"forbidden"aligns with the PR's backward-compatible design.
162-165: LGTM!Using
default_factorycorrectly prevents shared state betweenFunctionPromptinstances.src/fastmcp/tools/tool.py (3)
273-276: LGTM on TaskConfig field definition.The
task_configfield is correctly defined with adefault_factoryfor theTaskConfig(mode="forbidden")default, and the type annotation withAnnotatedandFieldfollows existing patterns in the codebase.
293-296: LGTM on MCP tool conversion logic.The
to_mcp_toolmethod correctly checks bothtask_config.mode != "forbidden"and"execution" not in overridesbefore settingToolExecution(taskSupport=...). This ensures explicit overrides take precedence and forbidden mode doesn't advertise task support.
335-342: LGTM on TaskConfig normalization and validation.The normalization logic correctly handles all three input cases (
None,bool,TaskConfig) and validates the function compatibility afterward. The flow ensurestask_configis always a properly validatedTaskConfiginstance before construction.src/fastmcp/resources/template.py (3)
235-238: LGTM on TaskConfig field in FunctionResourceTemplate.The field definition is consistent with
FunctionTool.task_config- same type annotation, default factory, and description pattern.
375-382: Good: Validation happens before function unwrapping.The
task_config.validate_function(fn, func_name)call correctly validates the original function at line 382 before the function unwrapping logic at lines 384-389. This ensures async validation checks the actual function, not the unwrapped__call__method.
254-257: Verify TaskConfig propagation to created Resource.When
create_resourcecallsResource.from_function, it passestask=self.task_config. This correctly propagates the template's task configuration to the dynamically created resource.src/fastmcp/resources/resource.py (2)
174-177: LGTM on TaskConfig field in FunctionResource.Consistent with the pattern established in
FunctionToolandFunctionResourceTemplate.
199-208: LGTM on normalization and validation logic.The logic correctly:
- Derives
func_nameearly (line 199) for use in validation error messages- Normalizes
tasktotask_config(lines 201-207)- Validates before wrapping (line 208 before line 211)
src/fastmcp/server/server.py (4)
408-436: LGTM on Docket registration filtering.The registration now correctly filters components by
task_config.mode != "forbidden"instead of a boolean flag. This ensures only components that support task execution are registered with Docket.
1826-1829: Type annotation forsupports_taskis correct.The variable
supports_task: bool | TaskConfigcorrectly reflects that the value can be either a boolean (fromself._support_tasks_by_default) or aTaskConfig(from thetaskparameter). This is then passed toTool.from_functionwhich handles both types via normalization.
1377-1405: LGTM on tool call handler mode enforcement.The mode enforcement logic for tools follows the same correct pattern as resources and prompts. The handler appropriately checks mode constraints before routing to either background task execution or synchronous execution.
682-713: LGTM on prompt handler mode enforcement.The mode enforcement for prompts is consistent with the resource and tool handlers. The result is correctly wrapped in
mcp.types.ServerResultbefore returning.
chrisguidry
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it!
|
Cool |
|
Bravo |
Exposes the full MCP task execution modes via
TaskConfiginstead of justtask=True/False(bool still works though!)The primary user-facing impact of this change is the ability to create MCP components (e.g. tools) that require background task execution as opposed to whatever the client wants.
Boolean shortcuts still work:
task=True→mode="optional",task=False→mode="forbidden".Key changes:
TaskConfigdataclass insrc/fastmcp/server/tasks/config.pytask_config: TaskConfigfield (decorators still usetask=for ergonomics)-32601forrequiredwithout task orforbiddenwith taskTaskConfig.validate_function()centralizes the async function checkInternally, task details are stored on these new
TaskConfigobjects, anticipating both future MCP-task-specific config as well as the ability to pass Docket-specific config without accessing function dependencies.cc @chrisguidry