Crea una tool que consulta una API externa real: aprende a hacer solicitudes HTTP dentro de un MCP Server.
Para nuestro server del clima, vamos a usar la wttr.in — una API meteorológica gratuita, sin necesidad de registrarse ni de una clave de autenticación. Devuelve datos meteorológicos en texto simple o JSON, es muy fácil de usar y perfecta para aprender. Solo tienes que hacer una solicitud GET y los datos llegan listos.
A wttr.in se accede mediante una URL simple. Para obtener datos en JSON, basta con agregar el parámetro ?format=j1:
# Ejemplos de URL:
https://wttr.in/São Paulo?format=j1
https://wttr.in/Tokyo?format=j1
https://wttr.in/London?format=j1
El JSON devuelto contiene la temperatura, las condiciones climáticas, la humedad, la sensación térmica, la velocidad del viento y mucho más. Vamos a extraer los datos más relevantes para crear una respuesta útil.
wttr.in es un proyecto open source alojado en GitHub. Admite más de 50 idiomas, acepta nombres de ciudades en cualquier idioma y también funciona con coordenadas geográficas. La usan millones de desarrolladores y no requiere autenticación — ideal para crear prototipos rápidamente y para proyectos de aprendizaje como el nuestro.
Antes de implementar la tool, prueba la API directamente en el navegador o en la terminal. Accede a https://wttr.in/São Paulo?format=j1 y revisa la estructura del JSON. Esto ayuda a entender qué campos están disponibles y cómo extraer los datos que queremos. También puedes probarlo en la terminal con curl "wttr.in/São Paulo?format=j1".
Para hacer solicitudes HTTP dentro de nuestro server MCP, necesitamos una biblioteca HTTP. En Python, las dos opciones principales son requests (síncrona) y httpx (con soporte async). Para los servers MCP, el httpx es la opción preferida porque admite llamadas asíncronas de forma nativa, lo que encaja mejor con la arquitectura de MCP.
Instala httpx en tu proyecto usando el gestor de paquetes uv:
# Agregar httpx como dependencia
uv add httpx
Con el httpx, podemos usar async with httpx.AsyncClient() para hacer solicitudes HTTP asíncronas dentro de tools MCP. La función de la tool se declarará como async def, lo que permite que el server siga respondiendo mientras espera la respuesta de la API externa.
Ahora implementaremos la tool para consultar el clima. Recibirá el nombre de una ciudad como string, hará una solicitud HTTP a wttr.in, analizará el JSON de respuesta y extraerá la información más relevante: temperatura, condición climática, humedad y sensación térmica.
from mcp.server.fastmcp import FastMCP
import httpx
mcp = FastMCP("Clima")
@mcp.tool()
async def consultar_clima(ciudad: str) -> str:
"""Consulta el clima actual de una ciudad."""
async with httpx.AsyncClient() las client:
url = f"https://wttr.in/{cidade}?format=j1"
response = await client.get(url, timeout=10.0)
response.raise_for_status()
dados = response.json()
current = dados["current_condition"][0]
temp = current["temp_C"]
sensacao = current["FeelsLikeC"]
humedad = current["humidity"]
descricao = current["weatherDesc"][0]["value"]
return (
f"Clima en {cidade}:\n"
f"Temperatura: {temp}°C\n"
f"Sensación térmica: {sensacao}°C\n"
f"Humedad: {umidade}%\n"
f"Condición: {descricao}"
)
Observa que usamos async def e await en la tool. FastMCP admite tools asíncronas de forma nativa: basta con declarar la función como async. O timeout=10.0 garantiza que la solicitud no quede pendiente para siempre si la API no responde. Siempre define timeouts en las solicitudes HTTP externas.
La forma en que devuelves los datos de la tool influye directamente en la calidad de la respuesta que el modelo dará al usuario. Devolver una cadena formateada y legible es mucho mejor que devolver el JSON sin procesar de la API. El modelo recibe esta cadena y se la presenta al usuario de forma natural y conversacional.
La respuesta de la tool debe ser una cadena estructurada pero legible. El modelo lee esta cadena y la transforma en lenguaje natural para el usuario. Compara los dos enfoques:
{"temp_C": "25", "humidity": "60", "weatherDesc": [{"value": "Clear"}], ...}
Clima en São Paulo:
Temperatura: 25°C
Humedad: 60%
Condición: Clear
Cuando tu tool depende de una API externa, muchas cosas pueden salir mal: la ciudad puede no existir, la conexión a internet puede fallar, la API puede estar fuera de servicio o la solicitud puede tardar demasiado. Cada uno de estos escenarios debe manejarse por separado para que el server siga funcionando y el modelo reciba un mensaje de error útil.
@mcp.tool()
async def consultar_clima(ciudad: str) -> str:
"""Consulta el clima actual de una ciudad."""
try:
async with httpx.AsyncClient() las client:
url = f"https://wttr.in/{cidade}?format=j1"
response = await client.get(url, timeout=10.0)
response.raise_for_status()
dados = response.json()
# ... extraer y devolver datos
except httpx.HTTPStatusError:
return f"Error: ciudad '{cidade}' no encontrada."
except httpx.ConnectError:
return "Error: no hay conexión a Internet."
except httpx.TimeoutException:
return "Error: la API tardó demasiado en responder."
except Exception las e:
return f"Error inesperado: {str(e)}"
¡Nunca confíes en APIs externas! Toda solicitud HTTP puede fallar. Una tool que funciona perfectamente en tu prueba local puede fallar en producción porque la API dejó de funcionar, cambió el formato de la respuesta o alcanzó el rate limit. El manejo de errores no es opcional: es obligatorio para cualquier tool que haga solicitudes externas.
La API respondió con un código de error. Ciudad no encontrada (404) o error interno de la API (500). Devuelve un mensaje claro que indique el problema.
No fue posible conectarse a la API; generalmente significa que no hay internet o que no se resolvió el DNS. Informa al usuario que la conexión falló.
La API tardó más que el timeout definido (10 segundos). Puede deberse a una sobrecarga del servidor. Indica que la API no respondió a tiempo.
Cualquier error inesperado — JSON mal formado, campo ausente, etc. El except genérico al final garantiza que ningún error quede sin manejar.
Con la tool implementada y el manejo de errores listo, configura el server en Claude Desktop y prueba distintas ciudades y escenarios. El objetivo es verificar que la tool devuelva datos correctos y que los errores se manejen correctamente.
Prueba con preguntas naturales para verificar que Claude use la tool correctamente:
Observa cómo Claude interpreta los datos devueltos por la tool. No solo repite los números: los contextualiza. Si la temperatura es de 35°C, puede decir «hace bastante calor». Si la humedad es del 90%, puede advertir que el ambiente se siente bochornoso. Cuanto más clara sea la respuesta de la tool, mejor será la interpretación del modelo.
Este server del clima es un ejemplo clásico de "API bridge" — un patrón muy común en servers MCP. El server actúa como puente entre el modelo y una API externa: traduce la solicitud del modelo en una llamada HTTP y la respuesta HTTP en texto para el modelo. Este mismo patrón se puede aplicar a cualquier API: pronóstico del tiempo, cotizaciones, noticias, datos financieros, estado de servicios y mucho más.
Siguiente Módulo: 3.6 — Añadiendo recursos