@@ -124,7 +124,7 @@ async def send_progress_tool(progress: float, total: float, message: str) -> boo
124124 """Send a progress notification to the client."""
125125 # Get the progress token from the request metadata
126126 ctx = server .get_context ()
127- if ctx .request_context .meta and ctx .request_context .meta .progressToken :
127+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no branch
128128 await ctx .session .send_progress_notification (
129129 progress_token = ctx .request_context .meta .progressToken ,
130130 progress = progress ,
@@ -137,7 +137,7 @@ async def message_handler(
137137 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
138138 ) -> None :
139139 """Handle exceptions from the session."""
140- if isinstance (message , Exception ):
140+ if isinstance (message , Exception ): # pragma: no cover
141141 raise message
142142
143143 async with create_session (
@@ -179,7 +179,7 @@ async def message_handler(
179179 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
180180 ) -> None :
181181 """Handle exceptions from the session."""
182- if isinstance (message , Exception ):
182+ if isinstance (message , Exception ): # pragma: no cover
183183 raise message
184184
185185 async with create_session (
@@ -216,7 +216,7 @@ async def message_handler(
216216 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
217217 ) -> None :
218218 """Handle exceptions from the session."""
219- if isinstance (message , Exception ):
219+ if isinstance (message , Exception ): # pragma: no cover
220220 raise message
221221
222222 async with create_session (
@@ -249,7 +249,7 @@ async def message_handler(
249249 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
250250 ) -> None :
251251 """Handle exceptions from the session."""
252- if isinstance (message , Exception ):
252+ if isinstance (message , Exception ): # pragma: no cover
253253 raise message
254254
255255 async with create_session (
@@ -282,7 +282,7 @@ async def message_handler(
282282 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
283283 ) -> None :
284284 """Handle exceptions from the session."""
285- if isinstance (message , Exception ):
285+ if isinstance (message , Exception ): # pragma: no cover
286286 raise message
287287
288288 async with create_session (
@@ -374,7 +374,7 @@ async def test_notification_callback_parametrized(
374374 async def send_progress_tool (progress : float , total : float , message : str ) -> bool :
375375 """Send a progress notification to the client."""
376376 ctx = server .get_context ()
377- if ctx .request_context .meta and ctx .request_context .meta .progressToken :
377+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no branch
378378 await ctx .session .send_progress_notification (
379379 progress_token = ctx .request_context .meta .progressToken ,
380380 progress = progress ,
@@ -411,7 +411,7 @@ async def message_handler(
411411 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
412412 ) -> None :
413413 """Handle exceptions from the session."""
414- if isinstance (message , Exception ):
414+ if isinstance (message , Exception ): # pragma: no cover
415415 raise message
416416
417417 # Create session with the appropriate callback
@@ -444,7 +444,7 @@ async def test_all_default_callbacks_with_notifications() -> None:
444444 async def send_progress_tool (progress : float , total : float ) -> bool :
445445 """Send a progress notification."""
446446 ctx = server .get_context ()
447- if ctx .request_context .meta and ctx .request_context .meta .progressToken :
447+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no branch
448448 await ctx .session .send_progress_notification (
449449 progress_token = ctx .request_context .meta .progressToken ,
450450 progress = progress ,
@@ -506,3 +506,97 @@ async def send_prompt_list_changed_tool() -> bool:
506506 # Test prompt list changed with default callback
507507 result5 = await client_session .call_tool ("send_prompt_list_changed" , {})
508508 assert result5 .isError is False
509+
510+
511+ @pytest .mark .anyio
512+ async def test_progress_tool_without_progress_token () -> None :
513+ """Test progress tool when no progress token is provided in metadata."""
514+ from mcp .server .fastmcp import FastMCP
515+
516+ progress_collector = ProgressNotificationCollector ()
517+ server = FastMCP ("test" )
518+
519+ @server .tool ("send_progress" )
520+ async def send_progress_tool (progress : float , total : float , message : str ) -> bool :
521+ """Send a progress notification to the client."""
522+ ctx = server .get_context ()
523+ # This branch: when meta is None or no progressToken
524+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no cover
525+ await ctx .session .send_progress_notification (
526+ progress_token = ctx .request_context .meta .progressToken ,
527+ progress = progress ,
528+ total = total ,
529+ message = message ,
530+ )
531+ return True
532+
533+ async with create_session (server ._mcp_server , progress_notification_callback = progress_collector ) as client_session :
534+ # Call without meta - takes the False branch
535+ result = await client_session .call_tool (
536+ "send_progress" ,
537+ {"progress" : 50.0 , "total" : 100.0 , "message" : "test" },
538+ )
539+ assert result .isError is False
540+ # No notification should be sent when no progress token is provided
541+ assert len (progress_collector .notifications ) == 0
542+
543+
544+ @pytest .mark .anyio
545+ async def test_parametrized_progress_tool_without_progress_token () -> None :
546+ """Test parametrized progress tool when no progress token is provided."""
547+ from mcp .server .fastmcp import FastMCP
548+
549+ progress_collector = ProgressNotificationCollector ()
550+ server = FastMCP ("test" )
551+
552+ @server .tool ("send_progress" )
553+ async def send_progress_tool (progress : float , total : float , message : str ) -> bool :
554+ """Send a progress notification to the client."""
555+ ctx = server .get_context ()
556+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no cover
557+ await ctx .session .send_progress_notification (
558+ progress_token = ctx .request_context .meta .progressToken ,
559+ progress = progress ,
560+ total = total ,
561+ message = message ,
562+ )
563+ return True
564+
565+ async with create_session (server ._mcp_server , progress_notification_callback = progress_collector ) as client_session :
566+ # Call with empty meta dict - takes the False branch
567+ result = await client_session .call_tool (
568+ "send_progress" ,
569+ {"progress" : 75.0 , "total" : 100.0 , "message" : "Almost done" },
570+ meta = {},
571+ )
572+ assert result .isError is False
573+ # No notification should be sent when meta exists but has no progressToken
574+ assert len (progress_collector .notifications ) == 0
575+
576+
577+ @pytest .mark .anyio
578+ async def test_default_callback_progress_tool_without_progress_token () -> None :
579+ """Test that default progress callback handles missing progress token correctly."""
580+ from mcp .server .fastmcp import FastMCP
581+
582+ server = FastMCP ("test-server" )
583+
584+ @server .tool ("send_progress" )
585+ async def send_progress_tool (progress : float , total : float ) -> bool :
586+ """Send a progress notification."""
587+ ctx = server .get_context ()
588+ if ctx .request_context .meta and ctx .request_context .meta .progressToken : # pragma: no cover
589+ await ctx .session .send_progress_notification (
590+ progress_token = ctx .request_context .meta .progressToken ,
591+ progress = progress ,
592+ total = total ,
593+ )
594+ return True
595+
596+ async with create_session (server ._mcp_server ) as client_session :
597+ # Call without meta - the False branch is taken in the tool
598+ result = await client_session .call_tool (
599+ "send_progress" ,
600+ {"progress" : 50.0 , "total" : 100.0 },
601+ )
602+ assert result .isError is False
0 commit comments