Skip to content

Commit 3bdb693

Browse files
committed
add test for http context extraction, and fix input validation tests
1 parent 5f08290 commit 3bdb693

File tree

2 files changed

+76
-6
lines changed

2 files changed

+76
-6
lines changed

tests/test_mcp_complex_app.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,5 @@ async def test_error_handling_missing_parameter(lowlevel_server_complex_app: Ser
214214
assert len(response.content) > 0
215215

216216
text_content = next(c for c in response.content if isinstance(c, types.TextContent))
217-
assert (
218-
"422" in text_content.text
219-
or "parameter" in text_content.text.lower()
220-
or "field" in text_content.text.lower()
221-
)
217+
assert "input validation error" in text_content.text.lower(), "Expected an input validation error"
218+
assert "required" in text_content.text.lower(), "Expected a missing required parameter error"

tests/test_mcp_simple_app.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)