@@ -112,7 +112,7 @@ async def test_error_handling(lowlevel_server_simple_app: Server):
112112
113113 text_content = next (c for c in response .content if isinstance (c , types .TextContent ))
114114 assert "item_id" in text_content .text .lower () or "missing" in text_content .text .lower ()
115- assert "422 " in text_content .text , "Expected a 422 status to appear in the response text "
115+ assert "input validation error " in text_content .text . lower () , "Expected an input validation error "
116116
117117
118118@pytest .mark .asyncio
@@ -368,3 +368,76 @@ async def test_custom_header_passthrough_to_tool_handler(fastapi_mcp_with_custom
368368 headers_arg = mock_request .call_args [0 ][4 ] # headers are the 5th argument
369369 assert "X-Custom-Header" in headers_arg
370370 assert headers_arg ["X-Custom-Header" ] == "MyValue123"
371+
372+
373+ @pytest .mark .asyncio
374+ async def test_context_extraction_in_tool_handler (fastapi_mcp : FastApiMCP ):
375+ """Test that handle_call_tool extracts HTTP request info from MCP context."""
376+ from unittest .mock import patch , MagicMock
377+ import mcp .types as types
378+ from mcp .server .lowlevel .server import request_ctx
379+
380+ # Create a fake HTTP request object with headers
381+ fake_http_request = MagicMock ()
382+ fake_http_request .method = "POST"
383+ fake_http_request .url .path = "/test"
384+ fake_http_request .headers = {"Authorization" : "Bearer token-123" , "X-Custom" : "custom-value-123" }
385+ fake_http_request .cookies = {}
386+ fake_http_request .query_params = {}
387+
388+ # Create a fake request context containing the HTTP request
389+ fake_request_context = MagicMock ()
390+ fake_request_context .request = fake_http_request
391+
392+ # Test with authorization header extraction from context
393+ token = request_ctx .set (fake_request_context )
394+ try :
395+ with patch .object (fastapi_mcp , "_execute_api_tool" ) as mock_execute :
396+ mock_execute .return_value = [types .TextContent (type = "text" , text = "success" )]
397+
398+ # Create a CallToolRequest like the MCP protocol would
399+ call_request = types .CallToolRequest (
400+ method = "tools/call" , params = types .CallToolRequestParams (name = "get_item" , arguments = {"item_id" : 1 })
401+ )
402+
403+ try :
404+ # Call the tool handler directly like the MCP server would
405+ await fastapi_mcp .server .request_handlers [types .CallToolRequest ](call_request )
406+ except Exception :
407+ pass
408+
409+ assert mock_execute .called , "The _execute_api_tool method was not called"
410+
411+ if mock_execute .called :
412+ # Verify that HTTPRequestInfo was extracted from context and passed to _execute_api_tool
413+ http_request_info = mock_execute .call_args .kwargs ["http_request_info" ]
414+ assert http_request_info is not None , "HTTPRequestInfo should be extracted from context"
415+ assert http_request_info .method == "POST"
416+ assert http_request_info .path == "/test"
417+ assert "Authorization" in http_request_info .headers
418+ assert http_request_info .headers ["Authorization" ] == "Bearer token-123"
419+ assert "X-Custom" in http_request_info .headers
420+ assert http_request_info .headers ["X-Custom" ] == "custom-value-123"
421+ finally :
422+ # Clean up the context variable
423+ request_ctx .reset (token )
424+
425+ # Test with missing request context (should still work but with None)
426+ with patch .object (fastapi_mcp , "_execute_api_tool" ) as mock_execute :
427+ mock_execute .return_value = [types .TextContent (type = "text" , text = "success" )]
428+
429+ call_request = types .CallToolRequest (
430+ method = "tools/call" , params = types .CallToolRequestParams (name = "get_item" , arguments = {"item_id" : 1 })
431+ )
432+
433+ try :
434+ await fastapi_mcp .server .request_handlers [types .CallToolRequest ](call_request )
435+ except Exception :
436+ pass
437+
438+ assert mock_execute .called , "The _execute_api_tool method was not called"
439+
440+ if mock_execute .called :
441+ # Verify that HTTPRequestInfo is None when context is not available
442+ http_request_info = mock_execute .call_args .kwargs ["http_request_info" ]
443+ assert http_request_info is None , "HTTPRequestInfo should be None when context is not available"
0 commit comments