INTRODUÇÃO:
Se você usa inteligência artificial, sabe que ela tem um limite claro: ela é muito inteligente, mas vive presa dentro de uma caixa de texto. O Claude, por exemplo, pode escrever um poema sobre chuva, mas não sabe se está chovendo agora na minha cidade. Ele pode simular uma venda, mas não consegue dar baixa no meu estoque real. E por que estou dizendo isso, por que neste artigo eu vou te ensinar a criar ferramentas MCP com conexões API de clima e conexão com um banco de dados SQLite.
Decidi resolver isso explorando o MCP (Model Context Protocol). A ideia era simples: dar “mãos” para a IA interagir com meus dados locais e APIs externas. O resultado foi um repositório que foi desde um script Python básico até uma aplicação dockerizada completa.
Meu primeiro desafio foi arquitetural. Eu precisava de duas capacidades distintas:
- Acessar um banco de dados SQLite local (uma operação síncrona).
- Consultar uma API de clima na internet (uma operação assíncrona).
Em vez de criar vários microsserviços complexos, unifiquei tudo em um único arquivo que chamei de “super_server.py”. Utilizando a biblioteca FastMCP, consegui misturar funções normais com funções “async” no mesmo agente. Isso permitiu que o Claude, em uma única resposta, verificasse que estava chovendo em Londres e, baseado nisso, sugerisse vender guarda-chuvas do meu banco de dados local.
Ficando desta forma o código (Ao final vou disponibilizar o link no meu github):
import sqlite3
import os
import httpx
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
from typing import Annotated
from pydantic import Field
1. Carrega as variáveis do arquivo .env
load_dotenv()
2. Inicialização do Servidor
server_name = os.getenv("MCP_SERVER_NAME", "Assistente Padrão")
mcp = FastMCP(server_name)
3. Configuração de Caminhos (Banco de Dados)
BASE_DIR = os.path.dirname(os.path.abspath(file))
db_name = os.getenv("DB_FILENAME", "loja.db")
DB_PATH = os.path.join(BASE_DIR, db_name)
4. URLs da API
GEO_URL = os.getenv("GEO_API_URL")
WEATHER_URL = os.getenv("WEATHER_API_URL")
— BLOCO 1: FERRAMENTAS DE ESTOQUE —
if not items:
return "Nenhum produto encontrado."
resultado = "ID | Produto | Preço (R$) | Estoque\n"
resultado += "-" * 40 + "\n"
for item in items:
resultado += f"{item[0]} | {item[1]} | {item[2]:.2f} | {item[3]}\n"
return resultado
except Exception as e:
return f"Erro ao acessar banco de dados: {str(e)}"
@mcp.tool()
def listar_produtos() -> str:
"""Lista todos os produtos do estoque com preços e quantidades."""
try:
with sqlite3.connect(DB_PATH) as conn:
cursor = conn.cursor()
cursor.execute("SELECT id, nome, preco, estoque FROM produtos")
items = cursor.fetchall()
@mcp.tool()
def vender_produto(
nome_exato: str,
quantidade: Annotated[int, Field(description="Quantidade vendida.")]
) -> str:
"""Registra uma venda e abate do estoque no banco de dados."""
# Validação Manual (Soft Fail)
if quantidade <= 0:
return "Erro: A quantidade para venda deve ser maior que zero. Por favor, tente novamente com um valor positivo."
try:
with sqlite3.connect(DB_PATH) as conn:
cursor = conn.cursor()
cursor.execute("SELECT estoque FROM produtos WHERE nome = ?", (nome_exato,))
res = cursor.fetchone()
if not res:
return f"Erro: Produto '{nome_exato}' não encontrado."
estoque_atual = res[0]
if estoque_atual < quantidade:
return f"Estoque insuficiente. Restam apenas {estoque_atual}."
novo_estoque = estoque_atual - quantidade
cursor.execute("UPDATE produtos SET estoque = ? WHERE nome = ?", (novo_estoque, nome_exato))
conn.commit()
return f"Venda realizada! Saldo de '{nome_exato}': {novo_estoque}."
except Exception as e:
return f"Erro ao processar venda: {str(e)}"
— BLOCO 2: FERRAMENTAS DE CLIMA —
@mcp.tool()
async def obter_previsao(cidade: str) -> str:
"""Consulta API externa para ver o clima atual (Async)."""
async with httpx.AsyncClient() as client:
try:
# Busca Lat/Lon
resp_geo = await client.get(GEO_URL, params={"name": cidade, "count": 1, "language": "pt"})
resp_geo.raise_for_status()
data_geo = resp_geo.json()
if "results" not in data_geo:
return f"Cidade '{cidade}' não encontrada."
local = data_geo["results"][0]
# Busca Clima
params_clima = {
"latitude": local["latitude"],
"longitude": local["longitude"],
"current": ["temperature_2m", "relative_humidity_2m"],
"timezone": "auto"
}
resp_weather = await client.get(WEATHER_URL, params=params_clima)
data_weather = resp_weather.json()
curr = data_weather["current"]
return (f"Clima em {local['name']}: {curr['temperature_2m']}°C, "
f"Umidade: {curr['relative_humidity_2m']}%")
except Exception as e:
return f"Erro na conexão: {str(e)}"
— BLOCO 3: PROMPTS —
@mcp.prompt()
def assistente_vendas() -> str:
“””Prompt pronto para atuar como vendedor proativo.”””
return “””
Você é um assistente de vendas inteligente.
Sua missão é:
1. Verificar o clima da cidade do usuário.
2. Sugerir produtos do estoque que combinem com o clima.
Use as ferramentas disponíveis para consultar os dados reais.
“””
if name == “main“:
mcp.run()`
Para que seja possivel utilizar esse custom MCP no seu Claude Desktop você deve alterar as configurações no claude_desktop_config.json que geralmente fica no diretório Roaming/claude.
{
"mcpServers": {
"super-servidor-docker": {
"command": "wsl.exe",
"args": [
"docker",
"run",
"-i",
"--rm",
"--env-file",
"/home/airtonlirajr/Estudos/mcp_learning/.env",
"mcp-super-server"
]
}
},
"preferences": {
"coworkScheduledTasksEnabled": false,
"sidebarMode": "chat"
}
}
Desta forma basta salvar e abrir novamente o Claude Desktop e veja o resultado que massa:

OBS: Sim sou de JAMPA – João Pessoa
Após tudo estar funcionando resolvi dockerizar o projeto com o seguinte Dockerfile:
`# 1. Usa uma imagem oficial do Python, leve (slim)
FROM python:3.11-slim
2. Define variáveis de ambiente cruciais para Python em Docker
Impede que o Python guarde logs em buffer (queremos ver erros na hora)
ENV PYTHONUNBUFFERED=1
Impede criação de arquivos .pyc desnecessários
ENV PYTHONDONTWRITEBYTECODE=1
3. Define a pasta de trabalho dentro do container
WORKDIR /app
4. Copia a lista de dependências e instala
Fazemos isso ANTES de copiar o código para aproveitar o cache do Docker
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
5. Copia todo o restante do código para dentro do container
COPY . .
6. Cria o banco de dados inicial (caso não exista) dentro do container
RUN python criar_banco.py
7. Comando padrão ao iniciar o container: rodar o servidor
CMD ["python", "super_server.py"]`
Também criei um script python que esta mencionado no Dockefile para alimentar meu SQLite com dados fictícios:
`import sqlite3
def setup_database():
# Cria o arquivo 'loja.db'
conn = sqlite3.connect("loja.db")
cursor = conn.cursor()
# Cria tabela de Produtos
cursor.execute("""
CREATE TABLE IF NOT EXISTS produtos (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
preco REAL NOT NULL,
estoque INTEGER NOT NULL
)
""")
# Insere dados de exemplo (se a tabela estiver vazia)
cursor.execute("SELECT count(*) FROM produtos")
if cursor.fetchone()[0] == 0:
dados = [
("Notebook Gamer", 4500.00, 10),
("Mouse Sem Fio", 120.50, 50),
("Monitor 4K", 1800.00, 15),
("Teclado Mecânico", 350.00, 30),
("Cadeira Ergonômica", 850.00, 5)
]
cursor.executemany("INSERT INTO produtos (nome, preco, estoque) VALUES (?, ?, ?)", dados)
conn.commit()
print("Banco de dados 'loja.db' criado com sucesso!")
else:
print("Banco de dados já existe.")
conn.close()
if name == "main":
setup_database()`
- Montagem de a imagem Docker e execução do server MCP no docker: Vamos mergulhar nos detalhes. No mundo do Docker, existem dois momentos principais: Construir (Build) e Rodar (Run).
É como cozinhar: primeiro você prepara o prato (Build) e depois você serve o prato (Run). O Claude só consegue “comer” o prato se você souber servir corretamente.
Aqui está a anatomia completa dos comandos que usamos:
- O Comando de Construção (docker build)
docker build -t mcp-super-server .
Este comando pega o seu Dockerfile (a receita) e o seu código Python e os funde em um arquivo estático e imutável chamado Imagem.
- docker build: O comando base que diz “quero criar uma nova imagem”.
- -t mcp-super-server: O “t” vem de Tag (etiqueta). Sem isso, sua imagem teria um nome aleatório tipo a1b2c3d4. Aqui estamos batizando ela de mcp-super-server para ficar fácil de chamar depois.
- . (O Ponto Final): Muito importante. Esse ponto diz ao Docker: “Use os arquivos da pasta onde estou agora como contexto”. É aqui que ele acha o Dockerfile, o requirements.txt e o super_server.py
- O Comando de Execução (docker run) Este é o comando que o Claude executa. Ele pega a imagem (que está parada no disco) e cria um Container (um processo vivo na memória).
docker run -i --rm --env-file .env mcp-super-server
Cada “flag” (opção com traço) aqui foi escolhida cirurgicamente para o funcionamento do MCP
CONCLUSÃO
Basicamente, o que fizemos aqui foi dar um corpo físico para o cérebro da IA. Até ontem, o Claude era apenas um consultor inteligente preso numa janela de chat, sonhando com o mundo lá fora. Hoje, com o Docker e o MCP, você deu a ele permissão para tocar nesse mundo.
Agora que você tem essa estrutura rodando, o “brinquedo” virou uma ferramenta poderosa. Pense no que dá para fazer apenas trocando as ferramentas que criamos:
Leve para a Nuvem: Como seu agente já está num container, você pode hospedá-lo em serviços como Render ou Railway. Isso transformaria seu código local em um servidor online 24 horas. Imagine poder puxar o celular na rua, falar com o Claude e ele consultar seu banco de dados que está rodando seguro na nuvem.
Automação da Vida Real: E se, em vez de consultar estoque, você criasse uma ferramenta para controlar as luzes da sua casa? O Claude poderia cruzar a informação de “hora do pôr do sol” da API de clima e acender a luz do seu escritório automaticamente.
O Assistente Financeiro Definitivo: Você poderia substituir o banco de dados da loja pelo seu banco de dados financeiro pessoal. Imagine mandar a foto de uma nota fiscal para o chat, e o agente não apenas ler o valor, mas inserir o gasto na categoria correta do seu banco de dados SQL, verificar se você estourou o orçamento do mês e te dar um puxão de orelha, tudo em segundos.
Você deixou de ser apenas um usuário que digita prompts para se tornar um arquiteto de sistemas inteligentes. A barreira técnica foi quebrada. O código está aí, modular, seguro e pronto. Agora é só escolher qual problema chato do seu dia a dia você quer que a IA resolva para você.
Bebam agua, e me seguem no LinkedIn: https://www.linkedin.com/in/airton-de-souza-lira-junior-6b81a661/
Repositório do Projeto: https://github.com/AirtonLira/mcp_learning

