@@ -42,7 +42,7 @@ Let's build your first MCP server in Python! We'll create a weather server that
4242
4343 <Step title = " Install additional dependencies" >
4444 ``` bash
45- uv add requests python-dotenv
45+ uv add httpx python-dotenv
4646 ```
4747 </Step >
4848
@@ -69,7 +69,7 @@ Let's build your first MCP server in Python! We'll create a weather server that
6969 from functools import lru_cache
7070 from typing import Any
7171
72- import requests
72+ import httpx
7373 import asyncio
7474 from dotenv import load_dotenv
7575 from mcp.server import Server
@@ -108,20 +108,20 @@ Let's build your first MCP server in Python! We'll create a weather server that
108108 Add this functionality:
109109
110110 ``` python
111- # Create reusable session
112- http = requests.Session()
113- http.params = {
111+ # Create reusable params
112+ http_params = {
114113 " appid" : API_KEY ,
115114 " units" : " metric"
116115 }
117116
118117 async def fetch_weather (city : str ) -> dict[str , Any]:
119- response = http.get(
120- f " { API_BASE_URL } /weather " ,
121- params = {" q" : city}
122- )
123- response.raise_for_status()
124- data = response.json()
118+ async with httpx.AsyncClient() as client:
119+ response = await client.get(
120+ f " { API_BASE_URL } /weather " ,
121+ params = {" q" : city, ** http_params}
122+ )
123+ response.raise_for_status()
124+ data = response.json()
125125
126126 return {
127127 " temperature" : data[" main" ][" temp" ],
@@ -167,7 +167,7 @@ Let's build your first MCP server in Python! We'll create a weather server that
167167 try :
168168 weather_data = await fetch_weather(city)
169169 return json.dumps(weather_data, indent = 2 )
170- except requests.RequestException as e:
170+ except httpx.HTTPError as e:
171171 raise RuntimeError (f " Weather API error: { str (e)} " )
172172
173173 ```
@@ -220,15 +220,17 @@ Let's build your first MCP server in Python! We'll create a weather server that
220220 days = min (int (arguments.get(" days" , 3 )), 5 )
221221
222222 try :
223- response = http.get(
224- f " { API_BASE_URL } / { FORECAST_ENDPOINT } " ,
225- params = {
226- " q" : city,
227- " cnt" : days * 8 # API returns 3-hour intervals
228- }
229- )
230- response.raise_for_status()
231- data = response.json()
223+ async with httpx.AsyncClient() as client:
224+ response = await client.get(
225+ f " { API_BASE_URL } / { FORECAST_ENDPOINT } " ,
226+ params = {
227+ " q" : city,
228+ " cnt" : days * 8 , # API returns 3-hour intervals
229+ ** http_params,
230+ }
231+ )
232+ response.raise_for_status()
233+ data = response.json()
232234
233235 forecasts = []
234236 for i in range (0 , len (data[" list" ]), 8 ):
@@ -245,7 +247,7 @@ Let's build your first MCP server in Python! We'll create a weather server that
245247 text = json.dumps(forecasts, indent = 2 )
246248 )
247249 ]
248- except requests.RequestException as e:
250+ except requests.HTTPError as e:
249251 logger.error(f " Weather API error: { str (e)} " )
250252 raise RuntimeError (f " Weather API error: { str (e)} " )
251253 ```
@@ -443,9 +445,10 @@ Let's build your first MCP server in Python! We'll create a weather server that
443445 <Card title = " Error Handling" icon = " shield" >
444446 ``` python
445447 try :
446- response = self .http.get(... )
447- response.raise_for_status()
448- except requests.RequestException as e:
448+ async with httpx.AsyncClient() as client:
449+ response = await client.get(... , params = {... , ** http_params})
450+ response.raise_for_status()
451+ except requests.HTTPError as e:
449452 raise McpError(
450453 ErrorCode.INTERNAL_ERROR ,
451454 f " API error: { str (e)} "
@@ -565,12 +568,13 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
565568 last_cache_time is None or
566569 now - last_cache_time > cache_timeout):
567570
568- response = http.get(
569- f " { API_BASE_URL } / { CURRENT_WEATHER_ENDPOINT } " ,
570- params = {" q" : city}
571- )
572- response.raise_for_status()
573- data = response.json()
571+ async with httpx.AsyncClient() as client:
572+ response = await client.get(
573+ f " { API_BASE_URL } / { CURRENT_WEATHER_ENDPOINT } " ,
574+ params = {" q" : city, ** http_params}
575+ )
576+ response.raise_for_status()
577+ data = response.json()
574578
575579 cached_weather = {
576580 " temperature" : data[" main" ][" temp" ],
@@ -674,6 +678,10 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
674678 DEFAULT_CITY
675679 )
676680
681+ @pytest.fixture
682+ def anyio_backend ():
683+ return " asyncio"
684+
677685 @pytest.fixture
678686 def mock_weather_response ():
679687 return {
@@ -706,7 +714,7 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
706714 ]
707715 }
708716
709- @pytest.mark.asyncio
717+ @pytest.mark.anyio
710718 async def test_fetch_weather (mock_weather_response ):
711719 with patch(' requests.Session.get' ) as mock_get:
712720 mock_get.return_value.json.return_value = mock_weather_response
@@ -720,7 +728,7 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
720728 assert weather[" wind_speed" ] == 3.6
721729 assert " timestamp" in weather
722730
723- @pytest.mark.asyncio
731+ @pytest.mark.anyio
724732 async def test_read_resource ():
725733 with patch(' weather_service.server.fetch_weather' ) as mock_fetch:
726734 mock_fetch.return_value = {
@@ -736,12 +744,26 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
736744 assert " temperature" in result
737745 assert " clear sky" in result
738746
739- @pytest.mark.asyncio
747+ @pytest.mark.anyio
740748 async def test_call_tool (mock_forecast_response ):
741- with patch(' weather_service.server.http.get' ) as mock_get:
742- mock_get.return_value.json.return_value = mock_forecast_response
743- mock_get.return_value.raise_for_status = Mock()
749+ class Response ():
750+ def raise_for_status (self ):
751+ pass
752+
753+ def json (self ):
754+ return nock_forecast_response
755+
756+ class AsyncClient ():
757+ def __aenter__ (self ):
758+ return self
759+
760+ async def __aexit__ (self , * exc_info ):
761+ pass
762+
763+ async def get (self , * args , ** kwargs ):
764+ return Response()
744765
766+ with patch(' httpx.AsyncClient' , new = AsyncClient) as mock_client:
745767 result = await call_tool(" get_forecast" , {" city" : " London" , " days" : 2 })
746768
747769 assert len (result) == 1
@@ -751,14 +773,14 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
751773 assert forecast_data[0 ][" temperature" ] == 18.5
752774 assert forecast_data[0 ][" conditions" ] == " sunny"
753775
754- @pytest.mark.asyncio
776+ @pytest.mark.anyio
755777 async def test_list_resources ():
756778 resources = await list_resources()
757779 assert len (resources) == 1
758780 assert resources[0 ].name == f " Current weather in { DEFAULT_CITY } "
759781 assert resources[0 ].mimeType == " application/json"
760782
761- @pytest.mark.asyncio
783+ @pytest.mark.anyio
762784 async def test_list_tools ():
763785 tools = await list_tools()
764786 assert len (tools) == 1
@@ -768,7 +790,7 @@ uvicorn.run(app, host="0.0.0.0", port=8000)
768790 </Step >
769791 <Step title = " Run tests" >
770792 ``` bash
771- uv add --dev pytest pytest-asyncio
793+ uv add --dev pytest
772794 uv run pytest
773795 ```
774796 </Step >
0 commit comments