Skip to content

Raise on unhandled content types in sampling handler dispatch chains#3857

Merged
jlowin merged 2 commits intomainfrom
exhaustiveness-checking
Apr 12, 2026
Merged

Raise on unhandled content types in sampling handler dispatch chains#3857
jlowin merged 2 commits intomainfrom
exhaustiveness-checking

Conversation

@strawgate
Copy link
Copy Markdown
Collaborator

@strawgate strawgate commented Apr 11, 2026

The Anthropic and OpenAI sampling handlers have isinstance chains that dispatch on MCP content types (TextContent, ImageContent, AudioContent, ToolUseContent, ToolResultContent) but silently drop unhandled variants like EmbeddedResource and ResourceLink.

This adds explicit else: raise ValueError to the two list-content dispatch loops that were missing catch-all branches, matching the behavior of:

  • The Gemini handler (already raises on all unhandled types)
  • The single-content dispatch paths in both Anthropic and OpenAI (already raise)

Why raise instead of warn-and-skip: These handlers convert MCP messages to vendor API formats for LLM calls. A partial conversion produces a plausible-but-wrong response — the LLM confidently answers based on incomplete input, and the user has no reason to doubt it. A clear error is less surprising than a subtly wrong answer.

# Before: EmbeddedResource silently dropped
for item in content:
    if isinstance(item, ToolUseContent): ...
    elif isinstance(item, TextContent): ...
    elif isinstance(item, ImageContent): ...
    elif isinstance(item, AudioContent): ...
    elif isinstance(item, ToolResultContent): ...
    # EmbeddedResource falls through silently

# After: explicit guard
    else:
        raise ValueError(
            f"Unsupported content type for Anthropic: {type(item).__name__}"
        )

🤖 Generated with Claude Code

@strawgate strawgate added the bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. label Apr 11, 2026
@marvin-context-protocol marvin-context-protocol Bot added the client Related to the FastMCP client SDK or client-side functionality. label Apr 11, 2026
@strawgate strawgate force-pushed the exhaustiveness-checking branch from 90a2235 to f156cc5 Compare April 11, 2026 23:41
@strawgate strawgate changed the title Add exhaustiveness guards to sampling content type dispatchers Log warning for unhandled content types in sampling handlers Apr 11, 2026
The Anthropic and OpenAI sampling handlers have isinstance chains that
dispatch on MCP content types but silently drop unhandled variants like
EmbeddedResource and ResourceLink. This adds explicit else-raise guards
to match the Gemini handler's behavior and the single-content dispatch
paths that already raise.

Raising is the right choice over warn-and-skip: a partial conversion
produces a plausible-but-wrong LLM response (the model confidently
answers based on incomplete input), which is worse than a clear error
that tells the user exactly what isn't supported.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@strawgate strawgate force-pushed the exhaustiveness-checking branch from f156cc5 to 9d6b045 Compare April 12, 2026 00:01
@strawgate strawgate changed the title Log warning for unhandled content types in sampling handlers Raise on unhandled content types in sampling handler dispatch chains Apr 12, 2026
Tests the new ValueError raises for unsupported content types
(e.g. EmbeddedResource) in the Anthropic and OpenAI message
conversion loops. Uses model_construct to bypass Pydantic's
union validation since the raise is a defensive guard for
future SDK content types.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link
Copy Markdown
Member

@jlowin jlowin left a comment

Choose a reason for hiding this comment

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

Exhaustiveness is the right posture for message conversion — silent-drop was the bug. Matches the Gemini handler and single-content paths.

@jlowin jlowin merged commit d6b55c0 into main Apr 12, 2026
8 checks passed
@jlowin jlowin deleted the exhaustiveness-checking branch April 12, 2026 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. client Related to the FastMCP client SDK or client-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants