22import json
33import sys
44from collections .abc import AsyncGenerator
5+ from unittest .mock import AsyncMock
56
67import pytest
78import uvicorn
89from mcp import McpError
10+ from mcp .types import TextContent
911from starlette .applications import Starlette
1012from starlette .routing import Mount
1113
14+ from fastmcp import Context
1215from fastmcp .client import Client
1316from fastmcp .client .transports import StreamableHttpTransport
1417from fastmcp .server .dependencies import get_http_request
@@ -38,6 +41,12 @@ async def sleep(seconds: float) -> str:
3841 await asyncio .sleep (seconds )
3942 return f"Slept for { seconds } seconds"
4043
44+ @server .tool
45+ async def greet_with_progress (name : str , ctx : Context ) -> str :
46+ """Report progress for a greeting."""
47+ await ctx .report_progress (0.5 , 1.0 , "Greeting in progress" )
48+ return f"Hello, { name } !"
49+
4150 # Add a resource
4251 @server .resource (uri = "data://users" )
4352 async def get_users ():
@@ -63,8 +72,10 @@ def welcome(name: str) -> str:
6372 return server
6473
6574
66- def run_server (host : str , port : int , ** kwargs ) -> None :
67- fastmcp_server ().run (host = host , port = port , ** kwargs )
75+ def run_server (host : str , port : int , stateless_http : bool = False , ** kwargs ) -> None :
76+ server = fastmcp_server ()
77+ server .settings .stateless_http = stateless_http
78+ server .run (host = host , port = port , ** kwargs )
6879
6980
7081def run_nested_server (host : str , port : int ) -> None :
@@ -88,8 +99,12 @@ def run_nested_server(host: str, port: int) -> None:
8899
89100
90101@pytest .fixture ()
91- async def streamable_http_server () -> AsyncGenerator [str , None ]:
92- with run_server_in_process (run_server , transport = "streamable-http" ) as url :
102+ async def streamable_http_server (
103+ stateless_http : bool = False ,
104+ ) -> AsyncGenerator [str , None ]:
105+ with run_server_in_process (
106+ run_server , stateless_http = stateless_http , transport = "streamable-http"
107+ ) as url :
93108 async with Client (transport = StreamableHttpTransport (f"{ url } /mcp" )) as client :
94109 assert await client .ping ()
95110 yield f"{ url } /mcp"
@@ -117,6 +132,24 @@ async def test_http_headers(streamable_http_server: str):
117132 assert json_result ["x-demo-header" ] == "ABC"
118133
119134
135+ @pytest .mark .parametrize ("streamable_http_server" , [True , False ], indirect = True )
136+ async def test_greet_with_progress_tool (streamable_http_server : str ):
137+ """Test calling the greet tool."""
138+ progress_handler = AsyncMock (return_value = None )
139+
140+ async with Client (
141+ transport = StreamableHttpTransport (streamable_http_server ),
142+ progress_handler = progress_handler ,
143+ ) as client :
144+ result = await client .call_tool ("greet_with_progress" , {"name" : "Alice" })
145+
146+ assert isinstance (result , list )
147+ assert isinstance (result [0 ], TextContent )
148+ assert result [0 ].text == "Hello, Alice!"
149+
150+ progress_handler .assert_called_once_with (0.5 , 1.0 , "Greeting in progress" )
151+
152+
120153async def test_nested_streamable_http_server_resolves_correctly ():
121154 # tests patch for
122155 # https://github.com/modelcontextprotocol/python-sdk/pull/659
0 commit comments