mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-10 07:39:10 +02:00
163 lines
5.4 KiB
Python
163 lines
5.4 KiB
Python
import json
|
|
from typing import cast
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from mcp.types import TextContent
|
|
from pydantic_core import to_json
|
|
|
|
from fastmcp import Client, Context, FastMCP
|
|
from fastmcp.client.sampling import RequestContext, SamplingMessage, SamplingParams
|
|
from fastmcp.utilities.types import Image
|
|
|
|
|
|
@pytest.fixture
|
|
def fastmcp_server():
|
|
mcp = FastMCP()
|
|
|
|
@mcp.tool
|
|
async def simple_sample(message: str, context: Context) -> str:
|
|
result = await context.sample("Hello, world!")
|
|
return result.text # type: ignore[attr-defined]
|
|
|
|
@mcp.tool
|
|
async def sample_with_system_prompt(message: str, context: Context) -> str:
|
|
result = await context.sample("Hello, world!", system_prompt="You love FastMCP")
|
|
return result.text # type: ignore[attr-defined]
|
|
|
|
@mcp.tool
|
|
async def sample_with_messages(message: str, context: Context) -> str:
|
|
result = await context.sample(
|
|
[
|
|
"Hello!",
|
|
SamplingMessage(
|
|
content=TextContent(
|
|
type="text", text="How can I assist you today?"
|
|
),
|
|
role="assistant",
|
|
),
|
|
]
|
|
)
|
|
return result.text # type: ignore[attr-defined]
|
|
|
|
@mcp.tool
|
|
async def sample_with_image(image_bytes: bytes, context: Context) -> str:
|
|
image = Image(data=image_bytes)
|
|
|
|
result = await context.sample(
|
|
[
|
|
SamplingMessage(
|
|
content=TextContent(type="text", text="What's in this image?"),
|
|
role="user",
|
|
),
|
|
SamplingMessage(
|
|
content=image.to_image_content(),
|
|
role="user",
|
|
),
|
|
]
|
|
)
|
|
return result.text # type: ignore[attr-defined]
|
|
|
|
return mcp
|
|
|
|
|
|
async def test_simple_sampling(fastmcp_server: FastMCP):
|
|
def sampling_handler(
|
|
messages: list[SamplingMessage], params: SamplingParams, ctx: RequestContext
|
|
) -> str:
|
|
return "This is the sample message!"
|
|
|
|
async with Client(fastmcp_server, sampling_handler=sampling_handler) as client:
|
|
result = await client.call_tool("simple_sample", {"message": "Hello, world!"})
|
|
assert result.data == "This is the sample message!"
|
|
|
|
|
|
async def test_sampling_with_system_prompt(fastmcp_server: FastMCP):
|
|
def sampling_handler(
|
|
messages: list[SamplingMessage], params: SamplingParams, ctx: RequestContext
|
|
) -> str:
|
|
assert params.systemPrompt is not None
|
|
return params.systemPrompt
|
|
|
|
async with Client(fastmcp_server, sampling_handler=sampling_handler) as client:
|
|
result = await client.call_tool(
|
|
"sample_with_system_prompt", {"message": "Hello, world!"}
|
|
)
|
|
assert result.data == "You love FastMCP"
|
|
|
|
|
|
async def test_sampling_with_messages(fastmcp_server: FastMCP):
|
|
def sampling_handler(
|
|
messages: list[SamplingMessage], params: SamplingParams, ctx: RequestContext
|
|
) -> str:
|
|
assert len(messages) == 2
|
|
|
|
assert isinstance(messages[0].content, TextContent)
|
|
assert messages[0].content.type == "text"
|
|
assert messages[0].content.text == "Hello!"
|
|
|
|
assert isinstance(messages[1].content, TextContent)
|
|
assert messages[1].content.type == "text"
|
|
assert messages[1].content.text == "How can I assist you today?"
|
|
return "I need to think."
|
|
|
|
async with Client(fastmcp_server, sampling_handler=sampling_handler) as client:
|
|
result = await client.call_tool(
|
|
"sample_with_messages", {"message": "Hello, world!"}
|
|
)
|
|
assert result.data == "I need to think."
|
|
|
|
|
|
async def test_sampling_with_fallback(fastmcp_server: FastMCP):
|
|
openai_sampling_handler = AsyncMock(return_value="But I need to think")
|
|
|
|
fastmcp_server = FastMCP(
|
|
sampling_handler=openai_sampling_handler,
|
|
)
|
|
|
|
@fastmcp_server.tool
|
|
async def sample_with_fallback(context: Context) -> str:
|
|
sampling_result = await context.sample("Do not think.")
|
|
return cast(TextContent, sampling_result).text
|
|
|
|
client = Client(fastmcp_server)
|
|
|
|
async with client:
|
|
call_tool_result = await client.call_tool("sample_with_fallback")
|
|
|
|
assert call_tool_result.data == "But I need to think"
|
|
|
|
|
|
async def test_sampling_with_image(fastmcp_server: FastMCP):
|
|
def sampling_handler(
|
|
messages: list[SamplingMessage], params: SamplingParams, ctx: RequestContext
|
|
) -> str:
|
|
assert len(messages) == 2
|
|
return to_json(messages).decode()
|
|
|
|
async with Client(fastmcp_server, sampling_handler=sampling_handler) as client:
|
|
image_bytes = b"abc123"
|
|
result = await client.call_tool(
|
|
"sample_with_image", {"image_bytes": image_bytes}
|
|
)
|
|
assert json.loads(result.data) == [
|
|
{
|
|
"role": "user",
|
|
"content": {
|
|
"type": "text",
|
|
"text": "What's in this image?",
|
|
"annotations": None,
|
|
"_meta": None,
|
|
},
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": {
|
|
"type": "image",
|
|
"data": "YWJjMTIz",
|
|
"mimeType": "image/png",
|
|
"annotations": None,
|
|
"_meta": None,
|
|
},
|
|
},
|
|
]
|