MÓDULO 3.5

🌤️ Server de Consulta ao Clima

Crie uma tool que consulta uma API externa real — aprenda a fazer requisições HTTP dentro de um MCP Server.

6
Tópicos
35
Minutos
Intermediário
Nível
Prático
Tipo
1

🌐 Escolhendo a API

Para nosso server de clima, vamos usar a wttr.in — uma API de clima gratuita, sem necessidade de cadastro ou chave de autenticação. Ela retorna dados meteorológicos em texto simples ou JSON, é extremamente simples de usar e perfeita para aprendizado. Basta fazer uma requisição GET e os dados chegam prontos.

💎 Conceito Principal

A wttr.in é acessada via URL simples. Para obter dados em JSON, basta adicionar o parâmetro ?format=j1:

# Exemplos de URL:

https://wttr.in/São Paulo?format=j1

https://wttr.in/Tokyo?format=j1

https://wttr.in/London?format=j1

O JSON retornado contém temperatura, condição climática, umidade, sensação térmica, velocidade do vento e muito mais. Vamos extrair os dados mais relevantes para criar uma resposta útil.

📊 Dados e Pesquisa

A wttr.in é um projeto open source hospedado no GitHub. Suporta mais de 50 idiomas, aceita nomes de cidades em qualquer idioma e também funciona com coordenadas geográficas. É usada por milhões de desenvolvedores e não requer autenticação — ideal para prototipagem rápida e projetos de aprendizado como o nosso.

💡 Dica Prática

Antes de implementar a tool, teste a API diretamente no navegador ou terminal. Acesse https://wttr.in/São Paulo?format=j1 e veja a estrutura do JSON. Isso ajuda a entender quais campos estão disponíveis e como extrair os dados que queremos. Você também pode testar no terminal com curl "wttr.in/São Paulo?format=j1".

2

📡 Requisições HTTP em Python

Para fazer requisições HTTP dentro do nosso server MCP, precisamos de uma biblioteca HTTP. Em Python, as duas opções principais são requests (síncrona) e httpx (com suporte async). Para servers MCP, o httpx é a escolha preferida porque suporta chamadas assíncronas nativamente, o que combina melhor com a arquitetura do MCP.

💎 Conceito Principal

Instale o httpx no seu projeto usando o gerenciador de pacotes uv:

# Adicionar httpx como dependência

uv add httpx

Com o httpx, podemos usar async with httpx.AsyncClient() para fazer requisições HTTP assíncronas dentro de tools MCP. A função da tool será declarada como async def, permitindo que o server continue responsivo enquanto aguarda a resposta da API externa.

❌ Evitar
  • Usar requests (síncrono) em tools async
  • Fazer requisições bloqueantes no event loop
  • Ignorar timeouts nas requisições HTTP
  • Criar clientes HTTP a cada chamada
✅ Fazer
  • Usar httpx com AsyncClient para async
  • Declarar tools como async def quando fazem I/O
  • Sempre definir timeout nas requisições
  • Usar context manager (async with) para o client
3

💻 Implementando a Tool

Agora vamos implementar a tool de consulta ao clima. Ela receberá o nome de uma cidade como string, fará uma requisição HTTP para a wttr.in, parseará o JSON de resposta e extrairá as informações mais relevantes: temperatura, condição climática, umidade e sensação térmica.

💎 Conceito Principal

from mcp.server.fastmcp import FastMCP

import httpx


mcp = FastMCP("Clima")


@mcp.tool()

async def consultar_clima(cidade: str) -> str:

"""Consulta o clima atual de uma cidade."""

async with httpx.AsyncClient() as 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"]

umidade = current["humidity"]

descricao = current["weatherDesc"][0]["value"]


return (

f"Clima em {cidade}:\n"

f"Temperatura: {temp}°C\n"

f"Sensação térmica: {sensacao}°C\n"

f"Umidade: {umidade}%\n"

f"Condição: {descricao}"

)

💡 Dica Prática

Note que usamos async def e await na tool. O FastMCP suporta tools assíncronas nativamente — basta declarar a função como async. O timeout=10.0 garante que a requisição não fique pendurada para sempre se a API não responder. Sempre defina timeouts em requisições HTTP externas.

4

📊 Formatando a Resposta

A forma como você retorna os dados da tool influencia diretamente a qualidade da resposta que o modelo dará ao usuário. Retornar uma string formatada e legível é muito melhor do que retornar o JSON bruto da API. O modelo recebe essa string e a apresenta ao usuário de forma natural e conversacional.

💎 Conceito Principal

A resposta da tool deve ser uma string estruturada mas legível. O modelo lê essa string e a transforma em linguagem natural para o usuário. Compare as duas abordagens:

JSON bruto (ruim)

{"temp_C": "25", "humidity": "60", "weatherDesc": [{"value": "Clear"}], ...}

String formatada (bom)

Clima em São Paulo:
Temperatura: 25°C
Umidade: 60%
Condição: Clear

❌ Evitar
  • Retornar o JSON bruto da API
  • Incluir campos irrelevantes na resposta
  • Usar códigos ou abreviações técnicas
  • Retornar dados sem unidades de medida
✅ Fazer
  • Formatar string com dados selecionados
  • Incluir apenas informações úteis ao usuário
  • Usar rótulos claros: "Temperatura:", "Umidade:"
  • Sempre incluir unidades (°C, %, km/h)
5

⚠️ Tratamento de Erros

Quando sua tool depende de uma API externa, muitas coisas podem dar errado: a cidade pode não existir, a internet pode cair, a API pode estar fora do ar, ou a requisição pode demorar demais. Cada um desses cenários precisa ser tratado individualmente para que o server continue funcionando e o modelo receba uma mensagem de erro útil.

💎 Conceito Principal

@mcp.tool()

async def consultar_clima(cidade: str) -> str:

"""Consulta o clima atual de uma cidade."""

try:

async with httpx.AsyncClient() as client:

url = f"https://wttr.in/{cidade}?format=j1"

response = await client.get(url, timeout=10.0)

response.raise_for_status()

dados = response.json()

# ... extrair e retornar dados

except httpx.HTTPStatusError:

return f"Erro: cidade '{cidade}' não encontrada."

except httpx.ConnectError:

return "Erro: sem conexão com a internet."

except httpx.TimeoutException:

return "Erro: a API demorou demais para responder."

except Exception as e:

return f"Erro inesperado: {str(e)}"

🚨 Alerta

Nunca confie em APIs externas! Toda requisição HTTP pode falhar. Uma tool que funciona perfeitamente no seu teste local pode quebrar em produção porque a API ficou fora do ar, mudou o formato da resposta ou atingiu o rate limit. O tratamento de erros não é opcional — é obrigatório para qualquer tool que faz requisições externas.

📋 Tipos de erro e como tratar
HTTPStatusError (404, 500, etc.)

A API respondeu com código de erro. Cidade não encontrada (404) ou erro interno da API (500). Retorne mensagem clara indicando o problema.

ConnectError

Não foi possível conectar à API — geralmente significa sem internet ou DNS não resolvido. Informe o usuário que a conexão falhou.

TimeoutException

A API demorou mais que o timeout definido (10 segundos). Pode ser sobrecarga do servidor. Informe que a API não respondeu a tempo.

Exception genérica

Qualquer erro inesperado — JSON mal formatado, campo ausente, etc. O except genérico no final garante que nenhum erro escape sem tratamento.

6

🧪 Testando com Claude

Com a tool implementada e o tratamento de erros no lugar, configure o server no Claude Desktop e teste com diferentes cidades e cenários. O objetivo é verificar que a tool retorna dados corretos e que os erros são tratados graciosamente.

💎 Conceito Principal

Teste com perguntas naturais para verificar que o Claude usa a tool corretamente:

  • 1. "Como está o clima em São Paulo?" — deve retornar temperatura, umidade e condição.
  • 2. "Qual a temperatura em Tokyo?" — teste com cidade em outro idioma.
  • 3. "Preciso de guarda-chuva em Londres hoje?" — o Claude deve inferir da condição climática.
  • 4. "Clima em XyzCidadeInexistente" — deve retornar mensagem de erro tratada.
💡 Dica Prática

Observe como o Claude interpreta os dados retornados pela tool. Ele não apenas repete os números — ele contextualiza. Se a temperatura é 35°C, ele pode dizer "está bastante quente". Se a umidade é 90%, ele pode avisar sobre a sensação abafada. Quanto mais clara a resposta da tool, melhor a interpretação do modelo.

📊 Dados e Pesquisa

Este server de clima é um exemplo clássico de "API bridge" — um padrão muito comum em servers MCP. O server atua como ponte entre o modelo e uma API externa, traduzindo a requisição do modelo em chamada HTTP e a resposta HTTP em texto para o modelo. Esse mesmo padrão pode ser aplicado a qualquer API: previsão do tempo, cotações, notícias, dados financeiros, status de serviços e muito mais.

📝 Resumo do Módulo

  • Escolhemos a API wttr.in — gratuita, sem autenticação, ideal para aprendizado.
  • Usamos httpx (async) para fazer requisições HTTP dentro do server MCP.
  • Implementamos a tool consultar_clima com async def e await.
  • Formatamos a resposta com dados selecionados e unidades de medida claras.
  • Tratamos erros específicos: cidade não encontrada, sem internet, timeout e erros inesperados.
  • Testamos com diferentes cidades e cenários de erro no Claude Desktop.

Próximo Módulo: 3.6 — Adicionando Resources