Skip to content

Add AnthropicSamplingHandler#2617

Merged
jlowin merged 5 commits intomainfrom
anthropic-sampling-handler-pr2
Dec 14, 2025
Merged

Add AnthropicSamplingHandler#2617
jlowin merged 5 commits intomainfrom
anthropic-sampling-handler-pr2

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 14, 2025

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.

from anthropic import Anthropic
from fastmcp import FastMCP
from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler

server = FastMCP(
    sampling_handler=AnthropicSamplingHandler(
        default_model="claude-haiku-4-5-20250514",
        client=Anthropic(),
    )
)

Install with pip install fastmcp[anthropic].

Includes full tool calling support, matching the OpenAI handler's capabilities from #2551.

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]
@marvin-context-protocol marvin-context-protocol Bot added the enhancement Improvement to existing functionality. For issues and smaller PR improvements. label Dec 14, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 14, 2025

Walkthrough

Adds 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)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The description includes a clear explanation of the change, usage example, installation instructions, and reference to related work, but lacks the required Contributors and Review Checklists from the template. Complete the Contributors Checklist (issue reference, workflow compliance, testing, documentation) and Review Checklist (self-review confirmation) sections to meet template requirements.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add AnthropicSamplingHandler' clearly and concisely summarizes the main change: adding a new sampling handler class for Anthropic API support.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch anthropic-sampling-handler-pr2

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da77cfa and 626faa4.

⛔ Files ignored due to path filters (3)
  • pyproject.toml is excluded by none and included by none
  • tests/client/sampling/handlers/test_anthropic_handler.py is excluded by none and included by none
  • uv.lock is excluded by !**/*.lock and 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 bare except
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 CreateMessageResult and CreateMessageResultWithTools return types is handled appropriately.


273-281: Simple and effective model selection.

The preference iteration with claude prefix 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.

Comment thread src/fastmcp/client/sampling/handlers/anthropic.py Outdated
Comment thread src/fastmcp/client/sampling/handlers/anthropic.py Outdated
Comment thread src/fastmcp/client/sampling/handlers/anthropic.py Outdated
@jlowin jlowin added feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues. and removed enhancement Improvement to existing functionality. For issues and smaller PR improvements. labels Dec 14, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
src/fastmcp/client/sampling/handlers/anthropic.py (3)

21-105: Don’t call the sync Anthropic client from async __call__ (event-loop blocking).
This handler should use AsyncAnthropic and await the 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.
Using or NOT_GIVEN changes 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).
Return None from the converter and omit tools/tool_choice when 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 None

Also applies to: 302-316

🧹 Nitpick comments (1)
src/fastmcp/client/sampling/handlers/anthropic.py (1)

71-76: Silence context unused 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

📥 Commits

Reviewing files that changed from the base of the PR and between 626faa4 and a3e839e.

📒 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.mdx
  • docs/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 bare except
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

Comment thread docs/clients/sampling.mdx
Comment on lines +218 to 248
### 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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find AnthropicSamplingHandler implementation
find . -type f -name "*.py" | xargs grep -l "AnthropicSamplingHandler" | head -5

Repository: jlowin/fastmcp

Length of output: 109


🏁 Script executed:

# Check the AnthropicSamplingHandler definition
find . -path "*/client/sampling*" -name "*.py" -type f

Repository: jlowin/fastmcp

Length of output: 251


🏁 Script executed:

# Search for handler implementations in the codebase
fd "anthropic" --type f -e py | grep -i handler

Repository: 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

  1. 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]
  1. 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:


🏁 Script executed:

cat -n src/fastmcp/client/sampling/handlers/anthropic.py

Repository: 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.py

Repository: jlowin/fastmcp

Length of output: 84


🏁 Script executed:

# Look at the imports and class structure
head -50 src/fastmcp/client/sampling/handlers/anthropic.py

Repository: 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.

Comment thread docs/servers/sampling.mdx
Comment on lines +446 to 474
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",
)
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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 set OPENAI_API_KEY / ANTHROPIC_API_KEY in 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.

Comment thread src/fastmcp/client/sampling/handlers/anthropic.py
Comment thread src/fastmcp/client/sampling/handlers/anthropic.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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 (1)
src/fastmcp/client/sampling/handlers/anthropic.py (1)

173-195: Tool result error flag correctly propagated.

Line 193 correctly passes the isError flag to Anthropic's ToolResultBlockParam. The conditional handles both None and False by converting them to False, 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

📥 Commits

Reviewing files that changed from the base of the PR and between a3e839e and a272306.

⛔ Files ignored due to path filters (1)
  • tests/client/sampling/handlers/test_anthropic_handler.py is 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 bare except
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 the ANTHROPIC_API_KEY environment 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 AsyncAnthropic and awaits the API call
  • Explicitly checks is not None for parameters (lines 103, 106, 110) to preserve falsy values like temperature=0
  • Handles tool_choice="none" by omitting tools entirely (lines 89-97)

Note: The context parameter (line 75) is unused but required by the handler interface signature.


267-287: Correct handling of multiple text blocks.

The method now joins all TextBlock content (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 None for "none" mode (lines 331-334), signaling the caller to omit tools entirely, which addresses the past review concern about semantic mismatch. Line 336 appropriately raises ValueError for 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 TextBlock and ToolUseBlock content (lines 362-376)
  • Validates that tool input is a dict (line 367)
  • Ensures non-empty content (lines 379-380)

@jlowin jlowin merged commit c91c43e into main Dec 14, 2025
14 checks passed
@jlowin jlowin deleted the anthropic-sampling-handler-pr2 branch December 14, 2025 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant