Conversation
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]
WalkthroughAdds AnthropicSamplingHandler at src/fastmcp/client/sampling/handlers/anthropic.py. The handler converts MCP SamplingMessage sequences into Anthropic MessageParam blocks, selects a model from provided preferences or a default, translates tools and tool_choice values to Anthropic equivalents, invokes the AsyncAnthropic client, and maps Anthropic responses back to MCP CreateMessageResult or CreateMessageResultWithTools. Includes helpers for iterating model preferences, converting messages/tools/tool choices, and validating content and stop reasons. Possibly related PRs
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.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
pyproject.tomlis excluded by none and included by nonetests/client/sampling/handlers/test_anthropic_handler.pyis excluded by none and included by noneuv.lockis excluded by!**/*.lockand included by none
📒 Files selected for processing (1)
src/fastmcp/client/sampling/handlers/anthropic.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Write Python code with Python ≥3.10 and include full type annotations
Use specific exception types in error handling - never use bareexcept
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter
Files:
src/fastmcp/client/sampling/handlers/anthropic.py
🪛 Ruff (0.14.8)
src/fastmcp/client/sampling/handlers/anthropic.py
39-42: Avoid specifying long messages outside the exception class
(TRY003)
75-75: Unused method argument: context
(ARG002)
251-251: Avoid specifying long messages outside the exception class
(TRY003)
260-260: Avoid specifying long messages outside the exception class
(TRY003)
271-271: Avoid specifying long messages outside the exception class
(TRY003)
323-323: Avoid specifying long messages outside the exception class
(TRY003)
359-359: 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). (4)
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- 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 (8)
src/fastmcp/client/sampling/handlers/anthropic.py (8)
21-44: Clean import handling with helpful error message.The try/except pattern with an actionable error message guiding users to install the optional dependency is a good practice. The Ruff TRY003 hints about long exception messages can be safely ignored here as the message provides valuable installation guidance.
47-69: Well-documented class with clear initialization.The docstring provides a practical usage example, and the initialization logic with optional client injection is appropriate for testing and flexibility.
111-132: Clean iterator implementation for model preferences.The method correctly handles all the documented preference formats (None, str, list, ModelPreferences object) with appropriate type narrowing.
134-253: Comprehensive message conversion handling.The method thoroughly handles all MCP content types (TextContent, ToolUseContent, ToolResultContent) including nested list content. The
# type: ignore[arg-type]on line 190 is acceptable given TypedDict constraints.
255-271: Straightforward text response conversion.The method correctly extracts text from the first content block for simple (non-tool) responses. The distinction between
CreateMessageResultandCreateMessageResultWithToolsreturn types is handled appropriately.
273-281: Simple and effective model selection.The preference iteration with
claudeprefix matching and fallback to the default model is a reasonable approach for an Anthropic-specific handler.
283-300: Proper tool schema conversion.The conversion correctly ensures
"type": "object"is present in the input schema (a JSON Schema requirement) and handles missing descriptions gracefully.
317-366: Solid response conversion with appropriate stop reason mapping.The stop reason translations and content block conversions are handled correctly. The defensive check on line 346 (
block.input if isinstance(block.input, dict) else {}) is reasonable given Anthropic's API contract, though the input should always be a dict per the SDK types.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
src/fastmcp/client/sampling/handlers/anthropic.py (3)
21-105: Don’t call the sync Anthropic client fromasync __call__(event-loop blocking).
This handler should useAsyncAnthropicandawaitthe request.try: - from anthropic import Anthropic, NotGiven + from anthropic import AsyncAnthropic, NotGiven from anthropic._types import NOT_GIVEN @@ class AnthropicSamplingHandler: @@ def __init__( - self, default_model: ModelParam, client: Anthropic | None = None + self, default_model: ModelParam, client: AsyncAnthropic | None = None ) -> None: - self.client: Anthropic = client or Anthropic() + self.client: AsyncAnthropic = client or AsyncAnthropic() self.default_model: ModelParam = default_model @@ - response = self.client.messages.create( + response = await self.client.messages.create( model=model, messages=anthropic_messages,Anthropic Python SDK: what is the correct async client class (AsyncAnthropic?) and is client.messages.create awaitable?
95-104: Preserve valid falsy values (temperature=0,stopSequences=[]) instead of treating them as unset.
Usingor NOT_GIVENchanges semantics.response = await self.client.messages.create( model=model, messages=anthropic_messages, - system=params.systemPrompt or NOT_GIVEN, - temperature=params.temperature or NOT_GIVEN, + system=params.systemPrompt if params.systemPrompt is not None else NOT_GIVEN, + temperature=params.temperature if params.temperature is not None else NOT_GIVEN, max_tokens=params.maxTokens, - stop_sequences=params.stopSequences or NOT_GIVEN, + stop_sequences=params.stopSequences if params.stopSequences is not None else NOT_GIVEN, tools=anthropic_tools, tool_choice=anthropic_tool_choice, )
83-109:tool_choice="none"must not silently behave like"auto"(can trigger unwanted tool calls).
ReturnNonefrom the converter and omittools/tool_choicewhen the caller disables tools.- anthropic_tools: list[ToolParam] | NotGiven = NOT_GIVEN + anthropic_tools: list[ToolParam] | NotGiven = NOT_GIVEN if params.tools: anthropic_tools = self._convert_tools_to_anthropic(params.tools) # Convert tool_choice to Anthropic format - anthropic_tool_choice: ToolChoiceParam | NotGiven = NOT_GIVEN + anthropic_tool_choice: ToolChoiceParam | NotGiven = NOT_GIVEN if params.toolChoice: - anthropic_tool_choice = self._convert_tool_choice_to_anthropic( - params.toolChoice - ) + converted = self._convert_tool_choice_to_anthropic(params.toolChoice) + if converted is None: + anthropic_tools = NOT_GIVEN + anthropic_tool_choice = NOT_GIVEN + else: + anthropic_tool_choice = converted @@ def _convert_tool_choice_to_anthropic( tool_choice: ToolChoice, - ) -> ToolChoiceParam: + ) -> ToolChoiceParam | None: @@ elif tool_choice.mode == "none": - # Anthropic doesn't have a "none" option, use auto - return ToolChoiceAutoParam(type="auto") + return NoneAlso applies to: 302-316
🧹 Nitpick comments (1)
src/fastmcp/client/sampling/handlers/anthropic.py (1)
71-76: Silencecontextunused lint (if required by the handler interface).
Otherwise Ruff will keep flagging this.async def __call__( self, messages: list[SamplingMessage], params: SamplingParams, - context: Any, + context: Any, # noqa: ARG002 (required by handler protocol) ) -> CreateMessageResult | CreateMessageResultWithTools:
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/clients/sampling.mdx(3 hunks)docs/servers/sampling.mdx(1 hunks)src/fastmcp/client/sampling/handlers/anthropic.py(1 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/clients/sampling.mdxdocs/servers/sampling.mdx
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Write Python code with Python ≥3.10 and include full type annotations
Use specific exception types in error handling - never use bareexcept
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter
Files:
src/fastmcp/client/sampling/handlers/anthropic.py
🪛 Ruff (0.14.8)
src/fastmcp/client/sampling/handlers/anthropic.py
39-42: Avoid specifying long messages outside the exception class
(TRY003)
75-75: Unused method argument: context
(ARG002)
251-251: Avoid specifying long messages outside the exception class
(TRY003)
260-260: Avoid specifying long messages outside the exception class
(TRY003)
271-271: Avoid specifying long messages outside the exception class
(TRY003)
323-323: Avoid specifying long messages outside the exception class
(TRY003)
359-359: 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). (4)
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests with lowest-direct dependencies
| ### Anthropic Handler | ||
|
|
||
| The Anthropic handler uses Claude models via the Anthropic API: | ||
|
|
||
| ```python | ||
| from fastmcp import Client | ||
| from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler | ||
|
|
||
| client = Client( | ||
| "my_mcp_server.py", | ||
| sampling_handler=AnthropicSamplingHandler(default_model="claude-sonnet-4-5"), | ||
| ) | ||
| ``` | ||
|
|
||
| You can pass a custom client for advanced configuration: | ||
|
|
||
| ```python | ||
| from anthropic import Anthropic | ||
|
|
||
| client = Client( | ||
| "my_mcp_server.py", | ||
| sampling_handler=AnthropicSamplingHandler( | ||
| default_model="claude-sonnet-4-5", | ||
| client=Anthropic(api_key="your-key"), | ||
| ), | ||
| ) | ||
| ``` | ||
|
|
||
| <Note> | ||
| Install the Anthropic handler with `pip install fastmcp[anthropic]`. | ||
| </Note> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find AnthropicSamplingHandler implementation
find . -type f -name "*.py" | xargs grep -l "AnthropicSamplingHandler" | head -5Repository: jlowin/fastmcp
Length of output: 109
🏁 Script executed:
# Check the AnthropicSamplingHandler definition
find . -path "*/client/sampling*" -name "*.py" -type fRepository: jlowin/fastmcp
Length of output: 251
🏁 Script executed:
# Search for handler implementations in the codebase
fd "anthropic" --type f -e py | grep -i handlerRepository: jlowin/fastmcp
Length of output: 107
🌐 Web query:
Anthropic Python SDK best practices for API key handling in documentation examples and AsyncAnthropic vs Anthropic client usage
💡 Result:
Summary and recommendations
- API-key handling in documentation examples — best practices
- Never hard-code keys in examples; read from ANTHROPIC_API_KEY (env var) instead. [1][2]
- Show use of dotenv/secrets only in local/dev examples and remind readers to add .env to .gitignore. [1][2]
- Recommend cloud/CI secret managers (KMS, Vault, GitHub Actions secrets) for deployments and CI; avoid pasting keys into third-party tools unless encrypted. [1]
- Encourage key rotation, per-environment keys (dev/test/prod), monitoring/logging, and least-privilege use. [1]
- Avoid printing keys or committing them to repos; demonstrate safe checks (e.g., a quick auth-test call rather than echoing the key). [1][3]
Minimal example to show in docs (safe pattern):
- Python (sync): import os; client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) # recommend omitting api_key since SDK reads env by default. [2]
- Note: for Claude Code, advise keeping ANTHROPIC_API_KEY unset if using a subscription to avoid unexpected charges. [4]
- AsyncAnthropic vs Anthropic — when & how to use
- Anthropic: synchronous client for simple scripts or blocking workflows. Use client.messages.create(...) directly. [2]
- AsyncAnthropic: async client for concurrent/high-throughput use—use await for calls and prefer async context managers when available; enables asyncio.gather and streaming patterns. Example: async_client = AsyncAnthropic(...); await async_client.messages.create(...). [2][3]
- Both accept an api_key parameter but default to reading ANTHROPIC_API_KEY from the environment; prefer env-based usage in examples. [2][3]
- Practical rule: use Anthropic in quick CLI scripts or examples; show AsyncAnthropic in docs for concurrency and production async apps. Also show explicit client.close() or async context usage to avoid resource leaks in examples. [2][3]
Sources
[1] Anthropic — API Key Best Practices (support article). (support.anthropic.com)
[2] anthropics/anthropic-sdk-python README (usage + AsyncAnthropic examples). (github.com)
[3] Anthropic docs — Getting started / Authentication. (docs.anthropic.com)
[4] Anthropic — Managing API Key Environment Variables in Claude Code. (support.anthropic.com)
Citations:
- 1: https://support.anthropic.com/en/articles/9767949-api-key-best-practices-keeping-your-keys-safe-and-secure?utm_source=openai
- 2: https://github.com/anthropics/anthropic-sdk-python?utm_source=openai
- 3: https://docs.anthropic.com/en/api/getting-started?utm_source=openai
- 4: https://support.anthropic.com/en/articles/12304248-managing-api-key-environment-variables-in-claude-code?utm_source=openai
🏁 Script executed:
cat -n src/fastmcp/client/sampling/handlers/anthropic.pyRepository: jlowin/fastmcp
Length of output: 15874
🏁 Script executed:
# Check for async patterns in the file
grep -n "async\|await\|AsyncAnthropic" src/fastmcp/client/sampling/handlers/anthropic.pyRepository: jlowin/fastmcp
Length of output: 84
🏁 Script executed:
# Look at the imports and class structure
head -50 src/fastmcp/client/sampling/handlers/anthropic.pyRepository: jlowin/fastmcp
Length of output: 1444
Use environment variables for API keys; avoid hard-coded placeholders in docs.
The example Anthropic(api_key="your-key") encourages committing secrets to code. Instead, use Anthropic() (relies on ANTHROPIC_API_KEY env var by default) or explicitly pass os.environ["ANTHROPIC_API_KEY"]. The handler accepts only the synchronous Anthropic client, not AsyncAnthropic.
Suggested edit:
-from anthropic import Anthropic
+import os
+from anthropic import Anthropic
client = Client(
"my_mcp_server.py",
sampling_handler=AnthropicSamplingHandler(
default_model="claude-sonnet-4-5",
- client=Anthropic(api_key="your-key"),
+ client=Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]),
),
)Or simpler, omit api_key entirely since the SDK reads ANTHROPIC_API_KEY by default.
🤖 Prompt for AI Agents
In docs/clients/sampling.mdx around lines 218 to 248, the example shows a
hard-coded API key (Anthropic(api_key="your-key")) which encourages committing
secrets and is inaccurate about client types; update the examples to use the
default environment-driven constructor (Anthropic()) or pass
os.environ["ANTHROPIC_API_KEY"] so keys come from ANTHROPIC_API_KEY, and note
that the sampling handler requires the synchronous Anthropic client (not
AsyncAnthropic); remove the literal "your-key" placeholder and adjust the text
to recommend installing via pip and relying on env vars for credentials.
| FastMCP provides built-in handlers for [OpenAI and Anthropic APIs](/clients/sampling#built-in-handlers). These handlers support the full sampling API including tools, automatically converting your Python functions to each provider's format. | ||
|
|
||
| <Note> | ||
| The OpenAI handler requires the `openai` package. Install it with: | ||
| ```bash | ||
| pip install fastmcp[openai] | ||
| # or | ||
| pip install openai | ||
| ``` | ||
| You'll also need to set the `OPENAI_API_KEY` environment variable or pass it directly to the client. | ||
| Install handlers with `pip install fastmcp[openai]` or `pip install fastmcp[anthropic]`. | ||
| </Note> | ||
|
|
||
| ```python | ||
| import os | ||
| from openai import OpenAI | ||
| from fastmcp import FastMCP | ||
| from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler | ||
|
|
||
| server = FastMCP( | ||
| name="My Server", | ||
| sampling_handler=OpenAISamplingHandler( | ||
| default_model="gpt-4o-mini", | ||
| client=OpenAI(api_key=os.getenv("OPENAI_API_KEY")), | ||
| ), | ||
| sampling_handler=OpenAISamplingHandler(default_model="gpt-4o-mini"), | ||
| sampling_handler_behavior="fallback", | ||
| ) | ||
| ``` | ||
|
|
||
| Or with Anthropic: | ||
|
|
||
| ```python | ||
| from fastmcp import FastMCP | ||
| from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler | ||
|
|
||
| server = FastMCP( | ||
| name="My Server", | ||
| sampling_handler=AnthropicSamplingHandler(default_model="claude-sonnet-4-5"), | ||
| sampling_handler_behavior="fallback", | ||
| ) | ||
| ``` |
There was a problem hiding this comment.
Add the missing prerequisite for Anthropic: how you provide credentials.
The examples are copy/pasteable, but you should tell the reader to set ANTHROPIC_API_KEY (or show it in code) so the snippet runs.
Suggested doc tweak (minimal):
- Add a sentence (or a
<Note>) near the install note saying you setOPENAI_API_KEY/ANTHROPIC_API_KEYin the environment before running the server.
🤖 Prompt for AI Agents
In docs/servers/sampling.mdx around lines 446 to 474, the Anthropic and OpenAI
example snippets don't mention how to supply credentials; add a short Note after
the install line stating that users must set OPENAI_API_KEY or ANTHROPIC_API_KEY
in their environment (or show how to provide equivalent credentials) before
running the server so the handler can authenticate; keep the wording minimal and
mirror the existing Note style.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/fastmcp/client/sampling/handlers/anthropic.py (1)
173-195: Tool result error flag correctly propagated.Line 193 correctly passes the
isErrorflag to Anthropic'sToolResultBlockParam. The conditional handles bothNoneandFalseby converting them toFalse, which is appropriate for Anthropic's boolean field.Optional: The expression could be slightly simplified for clarity:
- is_error=item.isError if item.isError else False, + is_error=bool(item.isError),Both forms are correct; the suggestion is purely stylistic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/client/sampling/handlers/test_anthropic_handler.pyis excluded by none and included by none
📒 Files selected for processing (2)
docs/clients/sampling.mdx(3 hunks)src/fastmcp/client/sampling/handlers/anthropic.py(1 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/clients/sampling.mdx
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Write Python code with Python ≥3.10 and include full type annotations
Use specific exception types in error handling - never use bareexcept
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter
Files:
src/fastmcp/client/sampling/handlers/anthropic.py
🧬 Code graph analysis (1)
src/fastmcp/client/sampling/handlers/anthropic.py (3)
src/fastmcp/tools/tool.py (1)
Tool(123-272)src/fastmcp/prompts/prompt.py (1)
Message(30-38)src/fastmcp/server/sampling/run.py (1)
text(72-82)
🪛 Ruff (0.14.8)
src/fastmcp/client/sampling/handlers/anthropic.py
39-42: Avoid specifying long messages outside the exception class
(TRY003)
75-75: Unused method argument: context
(ARG002)
263-263: Avoid specifying long messages outside the exception class
(TRY003)
272-272: Avoid specifying long messages outside the exception class
(TRY003)
285-287: Avoid specifying long messages outside the exception class
(TRY003)
336-336: Avoid specifying long messages outside the exception class
(TRY003)
344-344: Avoid specifying long messages outside the exception class
(TRY003)
380-380: 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). (4)
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (7)
docs/clients/sampling.mdx (2)
218-250: LGTM! API key handling follows best practices.The Anthropic handler documentation correctly uses
AsyncAnthropic()without hardcoded credentials, relying on theANTHROPIC_API_KEYenvironment variable. The inline comment on line 243 helpfully explains this behavior. This addresses the previous security concern.
252-254: Clear explanation of tool execution architecture.The Tool Execution section accurately describes the client/server division of responsibilities: the client forwards tools to the LLM and returns tool-use requests, while the server handles actual tool execution and follow-up sampling rounds.
src/fastmcp/client/sampling/handlers/anthropic.py (5)
21-42: Excellent error handling for optional dependency.The import error handling provides clear, actionable guidance on installing the required package. The detailed error message is helpful for users and should not be shortened despite the static analysis hint.
71-119: Correct async implementation with proper falsy-value handling.The main handler logic correctly:
- Uses
AsyncAnthropicand awaits the API call- Explicitly checks
is not Nonefor parameters (lines 103, 106, 110) to preserve falsy values liketemperature=0- Handles
tool_choice="none"by omitting tools entirely (lines 89-97)Note: The
contextparameter (line 75) is unused but required by the handler interface signature.
267-287: Correct handling of multiple text blocks.The method now joins all
TextBlockcontent (lines 275-277) instead of taking only the first block, preventing data loss. The error message on line 286 helpfully includes block types for debugging.
318-336: Proper handling of unsupported tool_choice modes.The method correctly returns
Nonefor"none"mode (lines 331-334), signaling the caller to omit tools entirely, which addresses the past review concern about semantic mismatch. Line 336 appropriately raisesValueErrorfor truly unsupported modes with a descriptive message.
338-387: Comprehensive response conversion with proper validation.The method correctly:
- Maps all Anthropic stop reasons to MCP equivalents (lines 347-357)
- Handles both
TextBlockandToolUseBlockcontent (lines 362-376)- Validates that tool input is a dict (line 367)
- Ensures non-empty content (lines 379-380)
Adds a sampling handler for the Anthropic API, mirroring the existing
OpenAISamplingHandler. This enables FastMCP servers to use Claude models for sampling requests when clients don't provide their own sampling capability.Install with
pip install fastmcp[anthropic].Includes full tool calling support, matching the OpenAI handler's capabilities from #2551.