@@ -161,6 +161,72 @@ async def list_tools():
161161 assert mock_call_tool .call_count == 1
162162
163163
164+ @pytest .mark .asyncio
165+ @patch ("mcp.client.stdio.stdio_client" , return_value = DummyStreamsContextManager ())
166+ @patch ("mcp.client.session.ClientSession.initialize" , new_callable = AsyncMock , return_value = None )
167+ @patch ("mcp.client.session.ClientSession.list_tools" )
168+ async def test_older_concurrent_refresh_does_not_overwrite_newer_cache (
169+ mock_list_tools : AsyncMock ,
170+ mock_initialize : AsyncMock ,
171+ mock_stdio_client ,
172+ ):
173+ first_refresh_started = asyncio .Event ()
174+ release_first_refresh = asyncio .Event ()
175+ request_count = 0
176+
177+ async def list_tools ():
178+ nonlocal request_count
179+ request_count += 1
180+ if request_count == 1 :
181+ first_refresh_started .set ()
182+ await release_first_refresh .wait ()
183+ return ListToolsResult (
184+ tools = [
185+ MCPTool (
186+ name = "tool1" ,
187+ description = "first-started" ,
188+ inputSchema = {"required" : ["old" ]},
189+ ),
190+ ],
191+ )
192+ return ListToolsResult (
193+ tools = [
194+ MCPTool (
195+ name = "tool1" ,
196+ description = "second-started" ,
197+ inputSchema = {"required" : ["latest" ]},
198+ ),
199+ ],
200+ )
201+
202+ mock_list_tools .side_effect = list_tools
203+ server = MCPServerStdio (
204+ params = {"command" : tee },
205+ cache_tools_list = True ,
206+ )
207+
208+ async with server :
209+ first_refresh = asyncio .create_task (server .list_tools ())
210+ try :
211+ await asyncio .wait_for (first_refresh_started .wait (), timeout = 1 )
212+ second_result = await asyncio .wait_for (server .list_tools (), timeout = 1 )
213+ assert second_result [0 ].description == "second-started"
214+ assert (server .cached_tools or [])[0 ].description == "second-started"
215+
216+ release_first_refresh .set ()
217+ first_result = await asyncio .wait_for (first_refresh , timeout = 1 )
218+ finally :
219+ release_first_refresh .set ()
220+ if not first_refresh .done ():
221+ first_refresh .cancel ()
222+ await asyncio .gather (first_refresh , return_exceptions = True )
223+
224+ assert first_result [0 ].description == "first-started"
225+ assert (server .cached_tools or [])[0 ].description == "second-started"
226+ assert (server .cached_tools or [])[0 ].input_schema == {"required" : ["latest" ]}
227+ assert request_count == 2
228+
229+
164230@pytest .mark .asyncio
165231@patch ("mcp.client.stdio.stdio_client" , return_value = DummyStreamsContextManager ())
166232@patch ("mcp.client.session.ClientSession.initialize" , new_callable = AsyncMock , return_value = None )
0 commit comments