pydocket 0.16.3 fixes a race condition in `_worker_loop` where cancellation
arriving between `_worker_done.clear()` and the try block would cause
`_worker_done.set()` to never run, blocking `Worker.__aexit__` forever.
Also fixes:
- Simplified `_docket_lifespan` cleanup (timeout wrapper no longer needed)
- Fixed `nested_server` test fixture to use graceful uvicorn shutdown
- Fixed uv transport tests to use local fastmcp in dev mode
Closes#2679🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a test that verifies task cancellation actually interrupts running
coroutines (they receive CancelledError) rather than just marking the task
as cancelled in Redis while the coroutine continues to completion.
This requires pydocket >= 0.16.2 which added best-effort cancellation via
Redis pub/sub signaling to workers.
Closes#2679🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move OpenAPI to providers/openapi/ submodule
Migrates the OpenAPI implementation to the provider pattern:
- Creates src/fastmcp/server/providers/openapi/ submodule with provider.py,
components.py, and routing.py
- Converts server/openapi/ to deprecated re-export stubs
- FastMCPOpenAPI remains as deprecated wrapper that uses OpenAPIProvider
- Updates experimental/server/openapi to import from new canonical location
* Update tests and FastMCP methods to use OpenAPIProvider
- Update FastMCP.from_openapi() and from_fastapi() to use OpenAPIProvider
directly, returning FastMCP instead of deprecated FastMCPOpenAPI
- Move tests from tests/server/openapi/ to tests/server/providers/openapi/
- Update all test imports to use new canonical location
- Add tests/deprecated/openapi/ for backward compatibility testing
- Update tests/client/test_openapi.py to use new imports
* Fix deprecation warning tests and address CodeRabbit feedback
- Use importlib.reload() to ensure deprecation warnings fire in tests
- Change logger.error to logger.exception for better stack traces
* Refactor FastMCPProxy into ProxyProvider
Move proxy functionality from custom manager classes to the Provider pattern:
- Create ProxyProvider that implements the Provider interface
- Move all proxy code to src/fastmcp/server/providers/proxy.py
- Keep FastMCPProxy as a convenience wrapper using ProxyProvider
- Add deprecation warning when importing from old location
- Convert handler classmethods to module-level functions
- Remove redundant get_* methods (base class defaults work)
* Remove unused Components class, simplify TaskComponents()
Renamed protocol.py to requests.py for clarity - it handles MCP task
request endpoints (tasks/get, tasks/result, tasks/cancel, tasks/list).
Consolidated constants to config.py as single source of truth:
- DEFAULT_POLL_INTERVAL_MS now derived from DEFAULT_POLL_INTERVAL
- TaskConfig uses constant instead of hardcoded timedelta(seconds=5)
Extracted _lookup_task_execution() helper to eliminate ~50 lines of
duplicated Redis lookup code. Uses redis.mget() for single round-trip
instead of 3 separate calls (performance improvement).
* Add poll_interval to TaskConfig
Allow users to configure polling interval per component via
TaskConfig(poll_interval=timedelta(...)). Default is 5 seconds.
* Update snapshots for poll_interval field
* Add version badge to poll_interval docs
* Add defensive handling for Redis data and align default poll intervals
* Replace type: ignore[attr-defined] with isinstance assertions in tests
* Fix isinstance assertions in failing tests
- Fix enum test to check for ResponseEnum instead of str
- Fix binary resource test to check for BlobResourceContents instead of TextResourceContents
- Fix Root type tests to check attributes directly instead of isinstance checks
* Fix type errors without using type: ignore
- Remove execution methods from TransformingProvider (only handles transformations)
- Add execution methods to base Provider class with default implementations
- Fix type narrowing in tests using cast() instead of type: ignore
- Fix PromptResult type handling in prompt render tests
- Fix type narrowing in middleware test for arguments and structured_content
* Add supports_tasks() method to replace string mode checks
Consolidates task config mode checks into a readable method on TaskConfig.
Instead of `task_config.mode == "forbidden"` or `task_config.mode != "forbidden"`,
code now uses `task_config.supports_tasks()` for clearer intent.
Updated 20 instances across the codebase and added type assertions in tests
to resolve type checker warnings.
* Update test to match new error message
* Add test_custom_subclass_tasks.py
* Refactor provider execution: delegate to middleware via wrapper components
- Remove execution methods (call_tool, read_resource, etc.) from Provider base
- Add FastMCPProvider* wrapper classes that delegate to child server middleware
- Move task routing to Tool._run() using contextvars (_task_metadata, _tool_call_key)
- Add convert_to_tool_result(result, output_schema) utility for Docket results
- Add convert_to_prompt_result() utility for prompt task results
- Pass namespaced key via add_to_docket(name=) for mounted tool lookup
* Standardize add_to_docket() with fn_key/task_key parameters
All components now use explicit fn_key (function lookup) and task_key
(result storage) parameters instead of relying on implicit key handling.
This fixes mounted component task execution where the MCP-visible key
differs from the Docket-registered function name.
* Add middleware chain tests for three-level mount hierarchy
Tests verify middleware runs at parent, child, and grandchild levels
for tools, resources, prompts, and resource templates.
* WIP: Provider refactor - unified submit_to_docket, template _read() in progress
Work in progress on refactoring execution to use component _read()/_run()/_render() methods.
Template background tasks not yet working - needs fix for Docket key lookup.
* Fix conversion functions to take full component for attribute access
Pass Tool/Prompt/Resource/Template to conversion functions instead of
individual attributes, ensuring access to serializer, output_schema,
mime_type, etc. Also fixes mixed-content output schema validation.
* Refactor: unified convert_result() methods and check_background_task helper
- Add convert_result() instance methods to all component types (Tool, Prompt, Resource, ResourceTemplate)
- Extract duplicated task routing logic into check_background_task() helper
- Fix type annotations on FastMCPProviderResource.read() and FastMCPProviderPrompt.render()
- Update protocol.py to use component.convert_result() uniformly
* Update tests to use namespace= instead of deprecated prefix= parameter
* Use CreateTaskResult for background task creation
Move result conversion logic to components (convert_result methods) and
return proper CreateTaskResult SDK type from task handlers. Consolidates
MCP protocol handler overrides into server.py with documentation.
* Address CodeRabbit nitpicks
- Add type annotation for resource parameter in handle_resource_as_task
- Move RootModel import to module level in client.py
* Document intentionally unused task_meta parameters
Prefix with underscore to suppress lint warnings. Client TTL will be
configurable via TaskConfig in the future; keeping parameter for API stability.
Refactors docket/background task support to be encapsulated within each
component rather than requiring external coordination:
- Move task_config to FastMCPComponent base class (default: forbidden)
- Add register_with_docket(docket) method that components use to register
themselves, checking task_config internally
- Add add_to_docket() method that handles component-specific calling
conventions (splatted kwargs vs positional dict)
- Simplify server registration to just call component.register_with_docket()
- Update task handlers to use component.add_to_docket()
This enables custom Tool/Resource/Prompt subclasses to support background
tasks by setting task_config and optionally overriding the docket methods.
Minor documentation fix addressing missing MCPError raise event. Given there are no documentation guidelines I proceeded to modify docstrings of methods that both
- MAY raise `McpError`
- have a docstring that contains a `Raises` section
* Refactor MountedProvider into FastMCPProvider + TransformingProvider
Split the monolithic MountedProvider into two focused components:
- FastMCPProvider: wraps a FastMCP server as a provider
- TransformingProvider: applies namespace/rename transformations to any provider
Add with_transforms() method to Provider base class for fluent API.
Rename mount() prefix parameter to namespace (deprecate prefix).
* Reuse compiled URI_PATTERN in deprecated import_server
* Simplify .key as computed property
Keep .key as the standard lookup interface for all components but
implement it as a computed property instead of a stored field.
- Remove _key private attribute and custom model_copy(key=...)
- .key returns .name for tools/prompts, str(.uri) for resources,
.uri_template for templates
- Use .key universally for component lookups in managers
- MountedProvider: prefix URIs only for resources/templates, not names
- Docket registration: tools/prompts use .key, resources use .name
(matches fn.__name__ for function lookup)
* Simplify .key as computed property
Keep .key as the standard lookup interface for all components but
implement it as a computed property instead of a stored field.
- Remove _key private attribute and custom model_copy(key=...)
- .key returns .name for tools/prompts, str(.uri) for resources,
.uri_template for templates
- Use .key universally for component lookups and Docket registration
- MountedProvider: prefix URIs only for resources/templates, not names
- Add _backend_* fields to proxy classes to preserve original identifiers
for backend calls when prefixed via import_server
* Standardize .key as computed property
- .key is now a read-only computed property:
- Tools/Prompts: returns .name
- Resources: returns str(.uri)
- Templates: returns .uri_template
- Prefixing uses model_copy(update={...}) to change underlying field
- Resource/template names are NOT prefixed, only URIs
- Move import_server tests to tests/deprecated/
When a tool was registered with a custom name different from its function
name, task execution would fail because Docket registered the function by
its `__name__` but the handler looked it up by the tool's configured name.
This switches to using Docket's new `names=` parameter (pydocket 0.16.0)
to register functions with their proper lookup keys, and removes the
`_create_named_fn_wrapper` hack that was used for mounted servers.
Closes#2642🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a dependency raises ToolError or other FastMCPError subclasses, they
were getting wrapped in RuntimeError with a generic "Failed to resolve
dependency" message. This made it hard to use ToolError for validation
in dependencies.
Now FastMCPError subclasses propagate unchanged, matching the pattern
used elsewhere in the codebase.
Closes#2633🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Simplify Provider interface and consolidate docket registration
- Remove get_http_routes from Provider (unused)
- Remove ProviderLifespanConfig, _base_lifespan, _register_tasks
- Remove supports_tasks flag from Provider.__init__
- Consolidate all docket registration in server._docket_lifespan()
- Simplify lifespan() to take no parameters
- Move MountedProvider to separate module
* Fix control flow in ComponentService resource methods
* Move providers to server/providers
* Ensure MountedProvider get_* methods go through middleware
* Fix get_resource to only return concrete resources
Reverts template-checking in get_resource that broke task execution.
Tasks need access to the original template, not instantiated resources.
* Move prefix utilities into mounted.py, deprecate import_server
- Add resource prefix functions (add/remove/has_resource_prefix) to mounted.py
- Deprecate import_server with warning to use mount() instead
- Add tool_names uniqueness validation in MountedProvider
* Fix provider iteration order and remove dead _is_mounted flag
- Remove unused _is_mounted flag (MountedProvider.lifespan() calls _lifespan
not _lifespan_manager, so the flag was never checked)
- Fix provider iteration: change reversed() to forward order in execution
methods (_call_tool, _read_resource_middleware, _get_prompt_content_middleware)
to match documented "first non-None wins" semantics
- Fix ComponentService to handle prefix-less mounted servers using
_strip_tool_prefix()/_strip_resource_prefix() methods
- Update conflict resolution tests to expect first-registered provider wins
- Add regression tests for Docket behavior and prefix-less ComponentService
* Add TaskComponents type and exception handling for provider task registration
- Create TaskComponents dataclass with FunctionTool/FunctionResource/etc. types
for proper typing of get_tasks() return value
- Add try/except wrapper around provider.get_tasks() in _docket_lifespan for
consistent error handling (warn + continue or raise based on settings)
- Remove type: ignore comments from server.py task registration loop
* Adopt streamable_http_client API from MCP SDK
- Update import to use new streamable_http_client function
- Convert httpx_client_factory to httpx.AsyncClient before passing to new API
- Maintain backward compatibility by continuing to accept factories
- Add deprecation warning for sse_read_timeout parameter
The new API accepts httpx.AsyncClient directly instead of factories.
We continue accepting factories for OAuth compatibility, converting
them to clients at the boundary with the MCP SDK.
* Fix timeout type conversion for streamable_http_client
Convert read_timeout_seconds from timedelta to float before passing
to httpx, matching the pattern used in the SSE transport.
* Enable redirect following in httpx client
* Fix httpx client resource leak
* Fix tool_choice to always require tools when result_type is set
* Consolidate sampling examples with rich output
* Replace eval() with explicit add/multiply tools
* Add AnthropicSamplingHandler
Adds a sampling handler for the Anthropic API at
fastmcp.client.sampling.handlers.anthropic, alongside the existing
OpenAI handler. Includes full support for tool calling.
Install with: pip install fastmcp[anthropic]
* Update default model
* Update sampling docs to cover both OpenAI and Anthropic handlers
* Use AsyncAnthropic, fix falsy value handling, handle tool_choice none
* Propagate isError to Anthropic, join multiple text blocks, fix docs
* Unify SamplingHandler and promote OpenAI handler
Consolidates ServerSamplingHandler and ClientSamplingHandler into a single
SamplingHandler type alias. Moves OpenAISamplingHandler from experimental
to fastmcp.client.sampling.handlers.openai as the canonical location.
Backwards compatibility maintained for imports from experimental.
* Remove unreachable code paths in OpenAI handler
* Fix docstring and use elif for mutually exclusive branches
* MCP → SDK (vocab change only)
* WIP: Sampling API with SamplingResult[T] and result_type
* SEP-1577: Sampling with tools
- Add tools and result_type parameters to ctx.sample()
- Update OpenAI handler for tool content types
- Client advertises sampling.tools capability by default
- Collect tool results into single message with list content
* Fix tool result content handling in OpenAI handler
* Remove @sampling_tool decorator - pass functions directly to sample()
Functions passed to ctx.sample(tools=[...]) are now auto-converted
via SamplingTool.from_function(). Users can still use that method
directly for custom name/description overrides.
* Remove auto-conversion of MCP tools to sampling tools
Users want MCP tools passed to ctx.sample() to go through the full MCP
machinery (middleware, native responses) rather than being auto-converted
to direct function calls. Now only SamplingTool and plain callables are
accepted - passing a FastMCP Tool raises a clear TypeError.
Also bumps mcp dependency to >=1.24.0 for required sampling features.
* Refactor sampling API: replace sample_iter() with sample_step()
Replace the mutable SampleRun/sample_iter() pattern with a simpler stateless
sample_step() function. sample_step() makes a single LLM call and returns a
SampleStep with the response and history. sample() now loops sample_step()
internally.
Key changes:
- Add sample_step() for fine-grained control over the sampling loop
- Remove SampleRun class and sample_iter() method
- Structured output uses tool description only (no prompt modification)
- execute_tools parameter controls automatic vs manual tool execution
* Address CodeRabbit nitpicks
* Address CodeRabbit review feedback for sampling tools
- Fix temperature=0.0 being dropped due to falsy evaluation
- Add ToolChoice.name support for forcing specific tools
- Replace assert statements with explicit RuntimeError checks
- Add mask_error_details parameter to sample()/sample_step() with ToolError escape hatch
- Fix hasattr patterns with proper isinstance checks
- Document mask_error_details and add OpenAI prerequisites to docs
* Address additional CodeRabbit review feedback
- Catch ValidationError specifically instead of bare Exception
- Update result_type docs to mention dataclasses and basic types
- Raise ValueError for unknown tool_choice modes
- Validate sampling_handler_behavior to catch typos
- Remove ToolChoice.name handling (not part of MCP spec)
- Validate tool_choice string in sample_step()
* Review fixes for sampling tools PR
- Remove internal functions from sampling __init__.py exports
- Remove fragile is_text property, use not is_tool_use instead
- Inline call_client into context.py, remove from run.py
- Fix SamplingMessage docs to use TextContent
- Handle result.text being None in doc examples
- Simplify client sampling docs to recommend OpenAISamplingHandler
- Add sampling_capabilities override documentation
- Raise iteration limit from 50 to 100
- Remove _parse_model_preferences duplication
- Use AsyncOpenAI in OpenAISamplingHandler
- Fix tool_choice docstring
* Fix OpenAI handler tests to use AsyncOpenAI
* Address remaining CodeRabbit review comments
- Fix message ordering in OpenAI handler: tool results now correctly
follow assistant message with tool_calls
- sample_step() now always includes assistant message in history
- Raise ValueError on JSON parse errors instead of silent {}
- Add has_sampling capability check when behavior is None
- Raise RuntimeError when structured output receives text response
- Wrap primitive result_type schemas in object wrapper
- Fix docs example using invalid SamplingMessage construction
- Add comprehensive client_sampling_test.py example
* Add return type annotation to OpenAISamplingHandler.__init__
* Use explicit 'is not None' check for sampling_capabilities defaulting
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Bill Easton <strawgate@users.noreply.github.com>
* fix: skip TextIO log file test on Windows
Avoids PytestUnraisableExceptionWarning caused by ProactorEventLoop
cleanup timing issues with subprocess pipe transports.
* Use WindowsSelectorEventLoopPolicy to fix Windows test warnings
Re-add the SelectorEventLoop fix from bcd2e594 that was inadvertently
removed in cf101c2a. This fixes ProactorEventLoop cleanup warnings
on Windows CI without needing to skip individual tests.
The function set experimental={"tasks": {}} but per the MCP spec:
1. Tasks belong in capabilities.tasks, not capabilities.experimental
2. Clients only need to declare task capabilities if receiving task-augmented
requests from the server (bi-directional support)
For client→server task requests (tools/call with task=True), only the server
needs to declare task capabilities. The SDK's native session.initialize()
handles this correctly.
* feat: add PromptResult as canonical internal type for prompts
Applies the same pattern as ResourceContent to prompts. PromptResult
wraps messages with description and meta. Public render() can return
either list[PromptMessage] or PromptResult (backwards compatible),
while private _render() always returns PromptResult.
* docs: fix incorrect PromptResult return type in example
* feat: add PromptResult canonical type with meta support
* fix: address PR #2600 review comments
Fixes test failures and code quality issues identified in PR review:
- Update 3 tests in test_server_interactions.py to access PromptResult.messages[0] instead of indexing directly
- Fix ProxyPromptManager to preserve meta field when converting GetPromptResult to PromptResult
- Fix ProxyPrompt.render() to return PromptResult instead of deprecated list[PromptMessage], preventing fastmcp tags from leaking into runtime meta
- Fix mask_error_details initialization to respect explicit False values
- Fix exception re-raising to preserve tracebacks (use bare raise instead of raise e)
- Update testing documentation to use pytest -n auto for parallel execution
* feat: make ResourceContent the canonical internal type for resources
Add Resource._read() private method that always returns ResourceContent,
maintaining backwards compatibility for custom resources returning str/bytes
from read(). Includes deprecation warning when str/bytes is returned.
* fix: address review feedback for ResourceContent
- Remove ResourceContent from root exports (import from fastmcp.resources)
- Fix FunctionResource.read() return type to str | bytes | ResourceContent
- Decode base64 blobs in proxy when receiving from remote servers
- Preserve meta in ProxyResource cached content
* fix: add empty result guards in proxy resource reads