Voltar ao início

Guia Prático: Criando seu Primeiro Servidor MCP em Python

Olá! Se você quer aprender na prática como conectar aplicações de IA com o mundo real, este guia é para você. Vamos construir, passo a passo, um servidor para o Model Context Protocol (MCP) usando Python.

A ideia é mostrar, de forma prática, como você pode expor funcionalidades do seu código para que qualquer aplicação compatível com o MCP, como um LLM, possa usá-las.

O que vamos construir?

Nosso objetivo é criar um servidor de previsão do tempo que oferece quatro ferramentas simples, mas poderosas:

  • get_current_weather_city: Para buscar o tempo atual em uma cidade brasileira.
  • get_forecast_city: Para obter a previsão do tempo para os próximos dias.
  • get_weather_coordinates: Para quem precisa de precisão, buscando o tempo por latitude e longitude.
  • get_weather_state_capital: Uma ferramenta útil para consultar o tempo na capital de um estado brasileiro.

Ao final, você terá um servidor funcional que poderá ser conectado a um host, como o Claude for Desktop, permitindo que a IA consulte o tempo em tempo real.

Antes de começarmos: o que você precisa ter

Para acompanhar este guia sem problemas, é bom ter algumas coisas à mão:

  • Python 3.10 ou superior instalado na sua máquina.
  • Uma familiaridade básica com o funcionamento de LLMs.
  • Uma conta gratuita no OpenWeatherMap. Vamos precisar de uma chave de API (API key) de lá para buscar os dados do tempo.
  • O Claude for Desktop (ou outro host compatível com MCP) instalado para testarmos nosso servidor no final.

Preparando nosso ambiente de desenvolvimento

Vamos organizar nosso espaço de trabalho. Cada passo aqui tem um propósito claro para manter nosso projeto limpo e funcional.

  1. Instale o uv
    Usaremos o uv, um gerenciador de ambientes e pacotes para Python que é extremamente rápido. Se você ainda não o tem, pode instalá-lo com este comando:
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
  2. Crie a pasta do projeto
    Vamos criar um diretório para nosso projeto e entrar nele.
    uv init weather
    cd weather
    
  3. Crie e ative o ambiente virtual
    Isso isola as dependências do nosso projeto, uma prática essencial em Python.
    uv venv
    source .venv/bin/activate
    
  4. Instale as dependências
    Precisamos de três pacotes: o SDK do MCP, o httpx para fazer requisições HTTP de forma assíncrona e o python-dotenv para gerenciar nossa chave de API.
    uv add "mcp[cli]" httpx python-dotenv
    
  5. Crie os arquivos do projeto
    Vamos criar o arquivo principal do nosso servidor e um arquivo .env para guardar nossa chave de API de forma segura.
    touch weather.py
    echo "OPENWEATHER_API_KEY=SUA_CHAVE_AQUI" > .env
    

    Importante: Abra o arquivo .env e substitua SUA_CHAVE_AQUI pela sua chave de API real do OpenWeatherMap.

Construindo o servidor: passo a passo

Agora vem a parte divertida: escrever o código. Vamos construir o arquivo weather.py em etapas, explicando cada bloco de código.

1. Importações e configuração inicial

Vamos começar importando tudo o que precisamos e configurando as variáveis iniciais. Isso prepara a base do nosso servidor.

# weather.py

import os
from typing import Any, Optional

import httpx
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP

# Inicializa o servidor com um nome descritivo
mcp = FastMCP("weather-brazil-server")

# URLs base das APIs e um User-Agent para boas práticas
OPENWEATHER_API_BASE = "https://api.openweathermap.org/data/2.5"
USER_AGENT = "weather-brazil-mcp-server/1.0"

# Carrega as variáveis de ambiente (nossa API key)
load_dotenv()
OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY")

2. Uma função para buscar dados na web

Para não repetir código, vamos criar uma função auxiliar que faz as requisições para a API do OpenWeatherMap. Ela é assíncrona (async) para não travar o servidor enquanto espera a resposta.

# weather.py (continuação)

async def make_request(url: str, params: dict) -> Optional[dict[str, Any]]:
    """Faz uma requisição GET assíncrona e retorna o JSON ou None em caso de erro."""
    headers = {"User-Agent": USER_AGENT}
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, params=params, headers=headers, timeout=30.0)
            response.raise_for_status()  # Lança um erro para respostas 4xx ou 5xx
            return response.json()
        except httpx.HTTPStatusError as e:
            print(f"Erro de HTTP: {e.response.status_code}")
            return None
        except httpx.RequestError as e:
            print(f"Erro na requisição: {e}")
            return None

3. Formatando os dados para humanos

A resposta da API é um JSON cheio de dados. Vamos criar funções para formatar essas informações de um jeito mais legível e amigável para quem for ler.

# weather.py (continuação)

def format_current_weather(data: Optional[dict]) -> str:
    """Formata os dados do tempo atual em uma string amigável."""
    if not data:
        return "Não foi possível obter os dados do tempo."

    weather = data.get("weather", [{}])[0]
    main = data.get("main", {})
    wind = data.get("wind", {})

    return (
        f'Condições Atuais - {data.get("name", "Localização desconhecida")}\n\n'
        f'Temperatura: {main.get("temp", "N/A")}°C (Sensação: {main.get("feels_like", "N/A")}°C)\n'
        f'Condição: {weather.get("description", "N/A").title()}\n'
        f'Umidade: {main.get("humidity", "N/A")}%'
    )

def format_forecast(data: Optional[dict]) -> str:
    """Formata a previsão do tempo em uma string amigável."""
    if not data or "list" not in data:
        return "Não foi possível obter a previsão do tempo."

    city_name = data.get("city", {}).get("name", "Localização desconhecida")
    forecasts = [f"Previsão para {city_name}\n"]

    for item in data["list"][:5]:  # Pega os 5 próximos períodos
        dt_txt = item.get("dt_txt", "")
        main = item.get("main", {})
        weather = item.get("weather", [{}])[0]
        forecasts.append(
            f'\n---\n{dt_txt}\n'
            f'Temperatura: {main.get("temp", "N/A")}°C\n'
            f'Condição: {weather.get("description", "N/A").title()}'
        )

    return "".join(forecasts)

4. Criando as ferramentas (Tools)

Esta é a parte central. Usando o decorador @mcp.tool(), transformamos funções Python em ferramentas que o MCP pode expor.

# weather.py (continuação)

@mcp.tool()
async def get_current_weather_city(city: str, state: str = "") -> str:
    """Obtém as condições meteorológicas atuais para uma cidade brasileira."""
    query = f"{city},{state},BR" if state else f"{city},BR"
    params = {"q": query, "appid": OPENWEATHER_API_KEY, "units": "metric", "lang": "pt_br"}
    data = await make_request(f"{OPENWEATHER_API_BASE}/weather", params)
    return format_current_weather(data)

@mcp.tool()
async def get_forecast_city(city: str, state: str = "") -> str:
    """Obtém a previsão do tempo para uma cidade brasileira."""
    query = f"{city},{state},BR" if state else f"{city},BR"
    params = {"q": query, "appid": OPENWEATHER_API_KEY, "units": "metric", "lang": "pt_br"}
    data = await make_request(f"{OPENWEATHER_API_BASE}/forecast", params)
    return format_forecast(data)

@mcp.tool()
async def get_weather_coordinates(latitude: float, longitude: float) -> str:
    """Obtém as condições meteorológicas para coordenadas geográficas."""
    params = {"lat": latitude, "lon": longitude, "appid": OPENWEATHER_API_KEY, "units": "metric", "lang": "pt_br"}
    data = await make_request(f"{OPENWEATHER_API_BASE}/weather", params)
    return format_current_weather(data)

5. Uma ferramenta extra com um dicionário

Para deixar nosso servidor mais inteligente, vamos criar uma ferramenta que busca o tempo da capital de um estado. Usaremos um dicionário para mapear os estados às suas capitais.

# weather.py (continuação)

CAPITAIS_BRASIL = {
    "acre": ("Rio Branco", "AC"), "alagoas": ("Maceió", "AL"),
    "amapá": ("Macapá", "AP"), "amazonas": ("Manaus", "AM"),
    "bahia": ("Salvador", "BA"), "ceará": ("Fortaleza", "CE"),
    "distrito federal": ("Brasília", "DF"), "espírito santo": ("Vitória", "ES"),
    "goiás": ("Goiânia", "GO"), "maranhão": ("São Luís", "MA"),
    "mato grosso": ("Cuiabá", "MT"), "mato grosso do sul": ("Campo Grande", "MS"),
    "minas gerais": ("Belo Horizonte", "MG"), "pará": ("Belém", "PA"),
    "paraíba": ("João Pessoa", "PB"), "paraná": ("Curitiba", "PR"),
    "pernambuco": ("Recife", "PE"), "piauí": ("Teresina", "PI"),
    "rio de janeiro": ("Rio de Janeiro", "RJ"), "rio grande do norte": ("Natal", "RN"),
    "rio grande do sul": ("Porto Alegre", "RS"), "rondônia": ("Porto Velho", "RO"),
    "roraima": ("Boa Vista", "RR"), "santa catarina": ("Florianópolis", "SC"),
    "são paulo": ("São Paulo", "SP"), "sergipe": ("Aracaju", "SE"),
    "tocantins": ("Palmas", "TO"),
}

@mcp.tool()
async def get_weather_state_capital(state: str) -> str:
    """Obtém as condições meteorológicas da capital de um estado brasileiro."""
    state_lower = state.lower()
    capital_info = None
    for state_name, info in CAPITAIS_BRASIL.items():
        if state_lower == state_name or state_lower == info[1].lower():
            capital_info = info
            break

    if not capital_info:
        return f"Estado '{state}' não encontrado. Tente o nome completo ou a sigla."

    return await get_current_weather_city(capital_info[0], capital_info[1])

6. Rodando o servidor

Finalmente, adicionamos o código que inicia o servidor quando executamos o script. Incluímos uma verificação para garantir que a chave da API foi configurada.

# weather.py (continuação)

if __name__ == "__main__":
    if not OPENWEATHER_API_KEY or OPENWEATHER_API_KEY == "SUA_CHAVE_AQUI":
        print("❌ Erro: A chave da API do OpenWeatherMap não está configurada.")
        print("   Por favor, edite o arquivo .env e adicione sua chave.")
        exit(1)

    print("🌦️  Servidor MCP de Previsão do Tempo iniciado.")
    print("   Aguardando conexão de um host como o Claude for Desktop...")
    mcp.run()

Conectando com o Claude for Desktop

Para que o Claude encontre nosso servidor, precisamos dizer a ele onde está. Edite o arquivo de configuração do Claude (os caminhos abaixo são os mais comuns):

  • macOS/Linux: ~/.config/claude_desktop/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Adicione a seguinte entrada, ajustando os caminhos para refletir onde você salvou o projeto e onde seu uv está instalado:

{
  "mcpServers": {
    "weather": {
      "command": "/caminho/para/seu/.local/bin/uv",
      "args": [
        "run",
        "--cwd",
        "/caminho/para/seu/projeto/weather",
        "python",
        "weather.py"
      ]
    }
  }
}

Salve o arquivo e reinicie o Claude for Desktop.

Testando nosso trabalho

Com o Claude aberto, vá para a seção de ferramentas ("Search and tools"). Se tudo deu certo, você verá nossas quatro ferramentas listadas! Agora você pode fazer perguntas como:

  • "Como está o tempo em Salvador, Bahia?"
  • "Qual a previsão do tempo para Curitiba?"
  • "Qual o clima na capital de Goiás?"

Continue sua jornada no MCP

Agora que você construiu seu primeiro servidor, que tal aprofundar seus conhecimentos?

Espero que este guia prático tenha sido útil para desmistificar a criação de servidores MCP. Agora é com você! Experimente, crie e integre.