Skip to content

Add Anthropic sampling handler and move handlers out of experimental#2584

Closed
jlowin wants to merge 10 commits intomainfrom
anthropic-sampling-handler
Closed

Add Anthropic sampling handler and move handlers out of experimental#2584
jlowin wants to merge 10 commits intomainfrom
anthropic-sampling-handler

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 10, 2025

Depends on: #2551

Adds an Anthropic sampling handler alongside the existing OpenAI handler, and moves both out of experimental into fastmcp.server.sampling.

from fastmcp import Client
from fastmcp.server.sampling.anthropic import AnthropicSamplingHandler

handler = AnthropicSamplingHandler(default_model="claude-sonnet-4-5-20250929")

async with Client("server.py", sampling_handler=handler) as client:
    result = await client.call_tool("summarize", {"text": "..."})

Both handlers support tool use during sampling and work as either client handlers or server fallback handlers.

Also consolidates sampling examples into examples/sampling/ with rich formatted output showing the MCP flow (client → server → sampling → LLM).

jlowin added 10 commits December 4, 2025 10:52
- 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
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.
- Add AnthropicSamplingHandler in server/sampling/anthropic.py
- Consolidate all sampling examples into examples/sampling/ with rich output
- Examples: text.py, structured_output.py, tool_use.py, server_fallback.py
- Add anthropic optional dependency
- Move from fastmcp.experimental.sampling.handlers.openai to fastmcp.server.sampling.openai
- Update docs and tests to use new import path
- Remove experimental handlers directory
- Add Pre-built Handlers section to clients/sampling.mdx with Anthropic and OpenAI
- Simplify servers/sampling.mdx to reference client docs for handler details
- Add examples for Azure OpenAI and local model providers
@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. client Related to the FastMCP client SDK or client-side functionality. server Related to FastMCP server implementation or server-side functionality. labels Dec 10, 2025
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: Tests are failing with TypeError: ClientSession.__init__() got an unexpected keyword argument 'sampling_capabilities' because FastMCP's SessionKwargs TypedDict includes a parameter that doesn't exist in the MCP SDK's ClientSession.

Root Cause: In src/fastmcp/client/transports.py:68, the PR adds sampling_capabilities to the SessionKwargs TypedDict:

class SessionKwargs(TypedDict, total=False):
    read_timeout_seconds: datetime.timedelta | None
    sampling_callback: SamplingFnT | None
    sampling_capabilities: mcp.types.SamplingCapability | None  # ❌ This doesn't exist!
    list_roots_callback: ListRootsFnT | None
    logging_callback: LoggingFnT | None
    # ...

However, ClientSession.__init__ (from mcp 1.23.3) does not accept sampling_capabilities as a parameter. Looking at the MCP SDK source:

# mcp/client/session.py:112-125
def __init__(
    self,
    read_stream: MemoryObjectReceiveStream[SessionMessage | Exception],
    write_stream: MemoryObjectSendStream[SessionMessage],
    read_timeout_seconds: timedelta | None = None,
    sampling_callback: SamplingFnT | None = None,
    elicitation_callback: ElicitationFnT | None = None,
    list_roots_callback: ListRootsFnT | None = None,
    logging_callback: LoggingFnT | None = None,
    message_handler: MessageHandlerFnT | None = None,
    client_info: types.Implementation | None = None,
    *,
    experimental_task_handlers: ExperimentalTaskHandlers | None = None,
) -> None:

The sampling_capabilities are determined internally by ClientSession based on whether a sampling_callback is provided (see line 147: sampling = types.SamplingCapability() if self._sampling_callback is not _default_sampling_callback else None).

Suggested Solution: Remove sampling_capabilities from the SessionKwargs TypedDict in src/fastmcp/client/transports.py:

class SessionKwargs(TypedDict, total=False):
    read_timeout_seconds: datetime.timedelta | None
    sampling_callback: SamplingFnT | None
-   sampling_capabilities: mcp.types.SamplingCapability | None
    list_roots_callback: ListRootsFnT | None
    logging_callback: LoggingFnT | None
    elicitation_callback: ElicitationFnT | None
    message_handler: MessageHandlerFnT | None
    client_info: mcp.types.Implementation | None

This will allow the ClientSession to automatically determine sampling capabilities based on the provided callback, as designed by the MCP SDK.

Failing Tests

The following tests are failing with this error:

  • test_proxy_with_stateful_proxy_without_value_error
  • test_mount_proxy
  • test_mount_proxy_with_dynamic_server
  • test_mount_stateful_proxy_with_prefix
  • test_raises_error_when_connecting_to_nonexistent_proxy_server
  • test_connect_to_mcp_config_with_one_server
  • test_connect_to_mcp_config_with_multiple_servers
  • test_connect_to_mcp_config_with_prefixes_only_selects_one_server_tools

All with the same error message.

Error Example
TypeError: ClientSession.__init__() got an unexpected keyword argument 'sampling_capabilities'
  File "src/fastmcp/client/transports.py", line 222, in connect_session
    async with ClientSession(
        read_stream, write_stream, **session_kwargs
    ) as session:

Workflow Run: #20083028791

Base automatically changed from sampling-tools-sep-1577 to main December 14, 2025 18:51
@jlowin jlowin closed this Dec 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. client Related to the FastMCP client SDK or client-side functionality. enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant