Fix task capabilities location (issue #2870)#2875
Conversation
Tasks belong in capabilities.tasks (first-class field) per SEP-1686, not capabilities.experimental.tasks. This fixes VS Code Copilot 1.107+ integration which checks capabilities.tasks?.requests?.tools?.call. Changes: - Update get_task_capabilities() to return ServerTasksCapability types - Override get_capabilities() in LowLevelServer to set tasks field - Remove experimental_capabilities parameter usage - Update test to verify correct location Fixes #2870
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c3a62ee7d
ℹ️ 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".
| await self._mcp_server.run( | ||
| read_stream, | ||
| write_stream, | ||
| self._mcp_server.create_initialization_options( | ||
| notification_options=NotificationOptions( |
There was a problem hiding this comment.
Keep stdio run inside stream context
In run_stdio_async, the await self._mcp_server.run(...) call is now indented outside the async with stdio_server() block (see the async with stdio_server() a few lines above). Exiting that context closes read_stream/write_stream, so the stdio transport will try to run after its streams are already closed, leading to immediate I/O errors or a dead server. This affects all stdio transport usage; the run call needs to remain inside the stdio_server context.
Useful? React with 👍 / 👎.
Test Failure AnalysisSummary: Type checker () fails because Root Cause: In requests=ServerTasksRequestsCapability(
tools=TasksToolsCapability(call=TasksCallCapability()),
prompts={"get": {}}, # ❌ Not in type signature
resources={"read": {}}, # ❌ Not in type signature
),The MCP SDK's Suggested Solution: Pass the extra fields using requests=ServerTasksRequestsCapability(
tools=TasksToolsCapability(call=TasksCallCapability()),
**{"prompts": {"get": {}}, "resources": {"read": {}}}, # ✅ Extra fields via **kwargs
),This tells the type checker "we know these aren't in the signature, but the model accepts them" while maintaining the same runtime behavior. Detailed AnalysisError log excerpt: Runtime verification: >>> from mcp.types import ServerTasksRequestsCapability
>>> ServerTasksRequestsCapability.model_fields.keys()
dict_keys(['tools']) # Only 'tools' is defined
>>> ServerTasksRequestsCapability.model_config
{'extra': 'allow'} # But extra fields are allowed!
>>> cap = ServerTasksRequestsCapability(
... tools=TasksToolsCapability(call=TasksCallCapability()),
... prompts={'get': {}},
... resources={'read': {}}
... )
>>> cap.model_dump()
{'tools': {'call': {}}, 'prompts': {'get': {}}, 'resources': {'read': {}}}
# ✅ Works at runtimeRelated Files
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR moves task capability handling out of experimental capability plumbing and into the server capability model. It adds a Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
⏰ 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)
✏️ Tip: You can disable this entire section by setting 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/server/server.py (1)
2379-2394: Critical:run()call is outside thestdio_server()context manager.The indentation shows that
await self._mcp_server.run()(lines 2385-2394) is executed after theasync with stdio_server()context exits. This meansread_streamandwrite_streamwill be closed before they're used, causing the server to fail.The
run()call must be indented to be inside theasync with stdio_server()block.🐛 Proposed fix
async with self._lifespan_manager(): async with stdio_server() as (read_stream, write_stream): mode = " (stateless)" if stateless else "" logger.info( f"Starting MCP server {self.name!r} with transport 'stdio'{mode}" ) - await self._mcp_server.run( - read_stream, - write_stream, - self._mcp_server.create_initialization_options( - notification_options=NotificationOptions( - tools_changed=True + await self._mcp_server.run( + read_stream, + write_stream, + self._mcp_server.create_initialization_options( + notification_options=NotificationOptions( + tools_changed=True + ), ), - ), - stateless=stateless, - ) + stateless=stateless, + )
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/server/tasks/test_task_capabilities.pyis excluded by none and included by none
📒 Files selected for processing (5)
src/fastmcp/client/transports.pysrc/fastmcp/server/http.pysrc/fastmcp/server/low_level.pysrc/fastmcp/server/server.pysrc/fastmcp/server/tasks/capabilities.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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/tasks/capabilities.pysrc/fastmcp/server/server.pysrc/fastmcp/client/transports.pysrc/fastmcp/server/http.pysrc/fastmcp/server/low_level.py
🧠 Learnings (1)
📚 Learning: 2026-01-13T03:11:40.917Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T03:11:40.917Z
Learning: Applies to **/__init__.py : Be intentional about module re-exports - only re-export fundamental types to fastmcp.*; prefer users importing from specific submodules
Applied to files:
src/fastmcp/client/transports.py
🧬 Code graph analysis (4)
src/fastmcp/server/server.py (1)
src/fastmcp/server/low_level.py (2)
run(193-227)create_initialization_options(153-166)
src/fastmcp/client/transports.py (1)
src/fastmcp/server/low_level.py (1)
create_initialization_options(153-166)
src/fastmcp/server/http.py (1)
src/fastmcp/server/low_level.py (1)
create_initialization_options(153-166)
src/fastmcp/server/low_level.py (2)
src/fastmcp/server/context.py (1)
fastmcp(186-191)src/fastmcp/server/tasks/capabilities.py (1)
get_task_capabilities(20-42)
🪛 GitHub Actions: Run static analysis
src/fastmcp/server/tasks/capabilities.py
[error] 39-39: ty check: Argument prompts does not match any known parameter.
[error] 40-40: ty check: Argument resources does not match any known parameter.
⏰ 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 with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
🔇 Additional comments (4)
src/fastmcp/client/transports.py (1)
884-891: LGTM! Initialization options simplified correctly.The removal of
experimental_capabilitiesfromcreate_initialization_options()aligns with the PR objective. Task capabilities are now exposed as a first-class field via theget_capabilities()override inLowLevelServer, so they no longer need to be passed through the experimental path.src/fastmcp/server/http.py (1)
169-176: LGTM! SSE handler initialization simplified.The SSE transport path now correctly relies on the
get_capabilities()override inLowLevelServerto expose task capabilities as a first-class field, rather than passing them throughexperimental_capabilities.src/fastmcp/server/low_level.py (1)
168-191: LGTM! Core fix for SEP-1686 task capabilities location.This override correctly places task capabilities in
capabilities.tasksas a first-class field instead of routing throughexperimental_capabilities. The implementation:
- Delegates to the parent for base capability construction
- Sets
capabilities.tasksdirectly with the typedServerTasksCapability(orNoneif docket is unavailable)This enables proper task detection by clients like VS Code Copilot 1.107+ that check
capabilities.tasks?.requests?.tools?.call.src/fastmcp/server/tasks/capabilities.py (1)
34-42: No action needed. The code at lines 39-40 already has# type: ignore[call-arg]comments in place, which properly suppresses the type checker for these parameters. The docstring (lines 28-29) explicitly documents thatpromptsandresourcesare intentionally passed via extra data for forward compatibility since the SDK types don't yet include them. Tests confirm this code works correctly.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
The run() call was outside the async with stdio_server() block, meaning the streams would be closed before being used. 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Task capabilities were being set in
capabilities.experimental.tasksinstead ofcapabilities.tasks, which broke VS Code Copilot 1.107+ integration. Copilot checkscapabilities.tasks?.requests?.tools?.callto detect task support—when it doesn't find it, it falls back to synchronous calls with a 5-minute timeout.The fix overrides
get_capabilities()inLowLevelServerto setcapabilities.tasksas a first-class field per SEP-1686, rather than passing it through theexperimental_capabilitiesparameter. FastMCP's existing Docket-based task infrastructure remains unchanged.FastMCP continues to support prompts and resources for task execution (ahead of the spec) via the SDK's
**extra_datamechanism.Closes #2870