FastMCP is a Python library for building MCP servers that expose tools through a standardized client/server protocol. FastMCP 2 improves deployment, server composition, security, client connectivity, testing, and integration capabilities over FastMCP 1. section 13.2.2 section 13.5
FastMCP fits into an agent integration workflow by providing:
- Decorator-based tool definitions with schemas generated from Python type hints. section 13.5
- A client for connecting to, authenticating with, and consuming tools from MCP servers. section 13.2.3
- Compatibility with OpenAI tool calling and LangChain or LangGraph integrations. section 13.2.3 section 13.5
An AccuWeather MCP server
A FastMCP 2 server can expose an asynchronous get_weather_conditions tool. The tool resolves a location through AccuWeather’s locations endpoint, requests current conditions, and runs as a separate server process. Its configuration reads an ACCUWEATHER_API_KEY from a .env file. section 13.3.1
Listing 13.1 AccuWeather MCP server
import os
import json
from typing import Dict
from fastmcp import FastMCP
from dotenv import load_dotenv
from aiohttp import ClientSession
load_dotenv() #1
mcp = FastMCP("mcp-accuweather") #2
@mcp.tool(description="""Get weather conditions
for a location.""") #3
async def get_weather_conditions(location: str) -> Dict:
"""Get weather conditions for a location."""
api_key = os.getenv("ACCUWEATHER_API_KEY") #4
base_url = "http://dataservice.accuweather.com"
async with ClientSession() as session:
location_search_url = f"{base_url}/locations/v1/cities/search"
params = { #5
"apikey": api_key,
"q": location,
}
async with session.get(location_search_url,
params=params) as response:
locations = await response.json() #6
if response.status != 200:
raise Exception(f"""Error fetching location
data: {response.status}, {locations}""")
if not locations or len(locations) == 0:
raise Exception("Location not found")
location_key = locations[0]["Key"] #7
current_conditions_url =
f"{base_url}/currentconditions/v1/{location_key}"
params = { #8
"apikey": api_key,
"details": "true"
}
async with session.get(current_conditions_url,
params=params) as response:
current_conditions = \
await response.json() #9
if current_conditions and len(current_conditions) > 0:
current = current_conditions[0] #10
current_data = {
"temperature": {
"value": current["Temperature"]["Metric"]["Value"],
"unit": current["Temperature"]["Metric"]["Unit"]
},
"weather_text": current["WeatherText"],
"relative_humidity": current.get("RelativeHumidity"),
"precipitation": current.get("HasPrecipitation", False),
"observation_time": current["LocalObservationDateTime"]
}
else:
current_data = "No current conditions available"
return { #11
"location": locations[0]["LocalizedName"],
"location_key": location_key,
"country": locations[0]["Country"]["LocalizedName"],
"current_conditions": current_data,
}
if __name__ == "__main__":
mcp.run(transport="streamable-http", #12
host="127.0.0.1",
port=8020, path="/accu-mcp-server")
Inspect the complete FastMCP 2 weather-server listing, including imports, server construction, the tool decorator, and asynchronous API calls. section 13.3.1
