Skip to content

SamplingMessage.content should accept a list #1282

Description

@thomasahle

Description

(I originally posted this with fastmcp, but the mentioned it's really about the mcp library.)

I'm trying to use ctx.sample to convert an image into markdown.
But it seems difficult to send multiple messages with ctx.sample. I get a random McpError() which I assume is because the Anthropic-style API usually doesn't allow to consecutive user messages.
Normally with anthropic I would send two contents in a single message, but this is not allowed by the Pydantic type SamplingMessage.

See code examples below

v0 and v1 fail with McpError() (no description) mcp/shared/session.py:286 in send_request.
v2 and v3 fails because SamplingMessage doesn't allow lists on the content field.

Example Code

async def page_to_markdown_v0(
    ctx: Context, raw_text: str, page_num: int, image: MCPImage
) -> mcp.types.TextContent:
    return await ctx.sample(  # type: ignore[call-arg]
        system_prompt=textwrap.dedent(f"""
            Convert the content of this PDF page (page {page_num}) into clean, well-structured Markdown format.

            Here is the raw text extracted from the page:
            -----------
            {raw_text}
            -----------

            Guidelines:
            - Use the raw text as reference but also analyze the image for better formatting
            - Preserve the document structure (headings, lists, tables, etc.)
            - Use appropriate Markdown syntax for formatting
            - Maintain the logical flow and hierarchy of the content
            - Include any important visual elements as descriptions
            - For tables, use Markdown table syntax
            - For mathematical expressions, use LaTeX syntax if needed

            Return ONLY the Markdown content, no explanations or meta-text.
        """),
        messages=[
            SamplingMessage(
                role="user",
                content=image.to_image_content(),
            ),
        ],
        max_tokens=8192,
    )


async def page_to_markdown_v1(
    ctx, raw_text: str, page_num: int, image: MCPImage
) -> mcp.types.TextContent:
    return await ctx.sample(  # type: ignore[call-arg]
        messages=[
            SamplingMessage(
                role="user",
                content=mcp.types.TextContent(
                    type="text",
                    text=textwrap.dedent(f"""
                    Convert the content of this PDF page (page {page_num}) into clean, well-structured Markdown format.

                    Here is the raw text extracted from the page:
                    -----------
                    {raw_text}
                    -----------

                    Guidelines:
                    - Use the raw text as reference but also analyze the image for better formatting
                    - Preserve the document structure (headings, lists, tables, etc.)
                    - Use appropriate Markdown syntax for formatting
                    - Maintain the logical flow and hierarchy of the content
                    - Include any important visual elements as descriptions
                    - For tables, use Markdown table syntax
                    - For mathematical expressions, use LaTeX syntax if needed

                    Return ONLY the Markdown content, no explanations or meta-text.
                """),
                ),
            ),
            SamplingMessage(
                role="user",
                content=image.to_image_content(),
            ),
        ],
        max_tokens=8192,
    )


async def page_to_markdown_v2(
    ctx, raw_text: str, page_num: int, image: MCPImage
) -> mcp.types.TextContent:
    return await ctx.sample(  # type: ignore[call-arg]
        messages=[
            SamplingMessage(
                role="user",
                content=[
                    mcp.types.TextContent(
                        type="text",
                        text=textwrap.dedent(f"""
                    Convert the content of this PDF page (page {page_num}) into clean, well-structured Markdown format.

                    Here is the raw text extracted from the page:
                    -----------
                    {raw_text}
                    -----------

                    Guidelines:
                    - Use the raw text as reference but also analyze the image for better formatting
                    - Preserve the document structure (headings, lists, tables, etc.)
                    - Use appropriate Markdown syntax for formatting
                    - Maintain the logical flow and hierarchy of the content
                    - Include any important visual elements as descriptions
                    - For tables, use Markdown table syntax
                    - For mathematical expressions, use LaTeX syntax if needed

                    Return ONLY the Markdown content, no explanations or meta-text.
                """),
                    ),
                    image.to_image_content(),
                ],
            ),
        ],
        max_tokens=8192,
    )


async def page_to_markdown_v3(
    ctx, raw_text: str, page_num: int, image: MCPImage
) -> mcp.types.TextContent:
    return await ctx.sample(  # type: ignore[call-arg]
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": textwrap.dedent(f"""
                    Convert the content of this PDF page (page {page_num}) into clean, well-structured Markdown format.

                    Here is the raw text extracted from the page:
                    -----------
                    {raw_text}
                    -----------

                    Guidelines:
                    - Use the raw text as reference but also analyze the image for better formatting
                    - Preserve the document structure (headings, lists, tables, etc.)
                    - Use appropriate Markdown syntax for formatting
                    - Maintain the logical flow and hierarchy of the content
                    - Include any important visual elements as descriptions
                    - For tables, use Markdown table syntax
                    - For mathematical expressions, use LaTeX syntax if needed

                    Return ONLY the Markdown content, no explanations or meta-text.
                """),
                    },
                    image.to_image_content().model_dump(),
                ],
            }
        ],
        max_tokens=8192,
    )

Version Information

FastMCP version:                                                                      2.10.6
MCP version:                                                                          1.12.2
Python version:                                                                       3.12.3
Platform:                                                       macOS-15.3.1-arm64-arm-64bit
FastMCP root path: /Users/ahle/repos/verification/nectar2/.venv/lib/python3.12/site-packages

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions