MCP server caseiro: como dei superpoderes pro Claude no meu workflow
Tinha um problema bobo que me consumia tempo de forma constante. Quando puxava Claude pra resolver uma query complexa no Supabase de um cliente, eu precisava:
- Copiar o schema do banco
- Colar na conversa
- Descrever a estrutura de dados
- Esperar Claude gerar a query
- Testar manualmente no Supabase
- Voltar e ajustar se estava errado
Em três dias de trabalho, isso custava fácil umas 3 a 4 horas. Não era tempo perdido completo, mas era tempo que eu deveria estar usando pra outra coisa.
Aí descobri MCP (Model Context Protocol) no final de 2024. Depois de brincar com alguns servidores prontos, decidi construir o meu. O resultado? Agora eu vejo pra Claude o banco do cliente em tempo real, ele entende a estrutura sozinho e escreve as queries corretas. Ganho: 40% menos tempo nesses momentos.
O que é MCP mesmo
MCP é um protocolo que permite conectar aplicações externas (bancos de dados, APIs, ferramentas) direto no Claude Desktop ou via API. Imagine como um plugin, mas padronizado e que funciona tanto localmente quanto em produção.
Antes do MCP, se eu quisesse que Claude tivesse acesso a dados reais do Supabase, eu precisava:
- Integração manual via prompt (repetitiva)
- Construir uma API customizada só pra isso
- Usar webhooks (complexo demais)
Agora? Eu escrevo um servidor MCP, conecto no Claude Desktop em 2 minutos, e pronto. Claude tem acesso direto.
Como funciona na prática
MCP trabalha com um modelo cliente-servidor. O Claude Desktop é o cliente. Você escreve um servidor (que roda localmente ou numa máquina sua) que expõe “recursos” e “ferramentas” que o Claude pode usar.
Recursos são dados que o Claude pode ler. Ferramentas são ações que ele pode executar.
No meu caso:
- Recurso: schema do Supabase (tabelas, colunas, tipos)
- Ferramenta: executar uma query SELECT (mas com validações de segurança)
- Ferramenta: ver a estrutura de uma tabela específica
Quando você abre uma conversa e quer que Claude quebre a cabeça em SQL complexo, você menciona o contexto e pronto. Claude vê os recursos, entende a estrutura, e escreve.
Construindo o MCP passo a passo
Vou mostrar como criei o meu. Assumo que você conhece Node e TypeScript um mínimo.
Primeiro, instalar o SDK do MCP:
npm init -y
npm install @modelcontextprotocol/sdk
npm install dotenv typescript @types/node
npm install @supabase/supabase-js
Criar um arquivo server.ts:
import Anthropic from "@anthropic-sdk/sdk";
import {
Server,
TextContent,
Tool,
} from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { createClient } from "@supabase/supabase-js";
import * as dotenv from "dotenv";
dotenv.config();
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);
const server = new Server({
name: "supabase-mcp",
version: "1.0.0",
});
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "supabase://schema",
name: "Supabase Database Schema",
description: "Current database schema and structure",
mimeType: "application/json",
},
],
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === "supabase://schema") {
const { data: tables } = await supabase
.from("information_schema.tables")
.select("*")
.eq("table_schema", "public");
return {
contents: [
{
uri: request.params.uri,
mimeType: "application/json",
text: JSON.stringify(tables, null, 2),
},
],
};
}
throw new Error(`Unknown resource: ${request.params.uri}`);
});
const queryTool: Tool = {
name: "execute_query",
description:
"Execute a safe SQL SELECT query on the Supabase database. Use only for SELECT, no mutations.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "SQL query to execute",
},
},
required: ["query"],
},
};
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [queryTool],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "execute_query") {
const query = request.params.arguments.query as string;
if (!query.trim().toUpperCase().startsWith("SELECT")) {
throw new Error("Only SELECT queries are allowed");
}
const { data, error } = await supabase.rpc("execute_query", {
query,
});
if (error) {
throw new Error(`Query error: ${error.message}`);
}
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2),
},
],
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
const transport = new StdioServerTransport();
server.connect(transport);
Isso é o básico. O servidor expõe um recurso (o schema do banco) e uma ferramenta (executar queries seguras).
Conectando no Claude Desktop
No seu ~/Library/Application Support/Claude/claude_desktop_config.json (Mac) ou equivalente no Windows, adicione:
{
"mcpServers": {
"supabase": {
"command": "node",
"args": ["/caminho/pro/seu/server.js"],
"env": {
"SUPABASE_URL": "sua-url",
"SUPABASE_ANON_KEY": "sua-key"
}
}
}
}
Reinicie o Claude Desktop. Pronto.
O ganho real que vi
Antes de ter esse MCP, quando precisava de uma query complexa no Supabase (tipo, “retorna todos os clientes que nunca compraram e que viraram leads há mais de 60 dias”), eu passava a Claude:
- A descrição em linguagem natural
- O schema (3 tabelas: clients, leads, purchases)
- Explicação de relacionamentos
Claude escrevia a query. Eu testava manualmente. Às vezes errava.
Agora, eu abro Claude, menciono o MCP, e falo:
“Preciso de uma query que traga clientes sem compras em mais de 60 dias”
Claude vê a estrutura real do banco. Entende os tipos de dados. Escreve uma query com JOINs corretos. Me mostra de primeira. Economizo uns 10-15 minutos por query complexa. Num mês, peguei uns 8 clientes diferentes. Uns 2 dias de tempo economizado.
Segurança: o que você não pode ignorar
Um MCP que tem acesso ao banco é perigoso se não tiver validações. Eu coloquei:
- Apenas SELECT é permitido (no handler de tool)
- Todas as queries rodam num usuário Supabase com permissões limitadas
- Rate limiting (máximo 10 queries por minuto)
- Log de tudo (query, resultado, timestamp)
- Sem exposição de schema de tabelas internas (information_schema fica local)
const RATE_LIMIT = 10;
const TIME_WINDOW = 60 * 1000;
const queryLog: Array<{ timestamp: number; query: string }> = [];
function checkRateLimit(): boolean {
const now = Date.now();
const recent = queryLog.filter((log) => now - log.timestamp < TIME_WINDOW);
if (recent.length >= RATE_LIMIT) {
return false;
}
queryLog.push({ timestamp: now, query: "" });
return true;
}
Simples, mas efetivo.
Casos de uso além do Supabase
Criei dois MCPs diferentes até agora:
- Supabase (este)
- Um que puxa dados de um CRM customizado que um cliente usa
O segundo foi útil pra gerar relatórios. Claude entendia a estrutura do CRM, me dava insights que eu demoraria horas digitando queries.
Você pode fazer MCP pra qualquer coisa que tenha API: Stripe, GitHub, seu próprio banco, webhooks customizados.
Custo e performance
Uma query no Supabase via MCP não tem custo extra. O MCP roda localmente (ou no seu servidor). Cada execução pela Claude API conta como uma chamada normal.
Performance: uma query que antes demoraria 5 minutos pra eu descrever, testar e corrigir agora demora 30 segundos.
O que aprendeu
Três coisas que não são óbvias:
-
Inputs do usuário ainda precisam ser validados no handler de tool. Claude vai tentar usar a ferramenta dentro das diretrizes, mas erro humano acontece. Sempre valide.
-
MCP não substitui autenticação real. Se o recurso que você expõe é sensível, coloque uma camada de segurança antes do MCP funcionar.
-
Documentação é crítica. Quanto melhor você descrever um recurso ou ferramenta, melhor Claude usa. Se disser “schema do banco”, Claude entende pouco. Se disser “tabela clients com colunas: id (uuid), email (text), created_at (timestamp), subscription_status (enum: active, inactive, cancelled)”, Claude navega melhor.
Próximos passos
Agora estou pensando em fazer MCPs pra:
- Pulsar informações ao Slack direto (quando Claude precisa alertar sobre algo)
- Conectar no Stripe pra ver métricas de pagamento
- Um que lê arquivos do Google Drive (pra Claude ter contexto de documentos)
A ideia é transformar Claude não num chatbot, mas numa ferramenta que funciona integrada ao meu próprio workflow de dev.
Checklist: construir um MCP
- Instalar SDK do MCP e dependências (@modelcontextprotocol/sdk, dotenv, typescript)
- Criar server.ts com handlers básicos (ListResources, ReadResource)
- Expor recursos (schema, dados, estrutura)
- Criar ferramentas (tools) com descrições claras
- Implementar validações de segurança (tipos de query, rate limit, permissões)
- Testar localmente com o Claude Desktop
- Configurar no claude_desktop_config.json
- Adicionar logging para auditar uso
- Documentar cada recurso e ferramenta com clareza
- Considerar performance (cache de schema, índices)
Problemas reais ao buildar MCP servers caseiros
Construir um MCP server parece simples at� estar em produ��o. Aqui est�o 3 problemas que enfrentei:
Problema 1: Erro de serializa��o em tipos customizados
Voc� define um tipo customizado no seu MCP (tipo, um objeto de clinica com 15 campos). Tudo funciona local. Em produ��o, Claude tenta serializar e falha porque um dos campos � uma data ou um Decimal que n�o � JSON-compat�vel.
Solu��o: sempre use tipos primitivos (string, number, boolean) ou serialize manualmente. Se tem Date, converta pra ISO 8601 string. Se tem Decimal (Prisma), converta pra float.
Problema 2: Timeout na primeira requisi��o
Seu MCP server tarda 2-3 segundos pra responder � primeira requisi��o (porque t� abrindo conex�o com DB, carregando dados em cache). Claude espera m�ximo 30 segundos, mas timeout padr�o de algumas requisi��es � 5 segundos.
Solu��o: aqueca o servidor na inicializa��o. Conecte no DB, carregue dados cr�ticos. Primeira requisi��o real vai ser fast.
Problema 3: Gerenciamento de estado entre requisi��es
Voc� quer manter um cache em mem�ria (lista de clientes, lista de procedimentos). Primeira requisi��o, popula cache. Segunda requisi��o deveria usar cache. Mas por algum motivo, cache foi limpo ou n�o existe.
Motivo: cada requisi��o roda em um novo processo ou contexto isolado.
Solu��o: n�o confie em mem�ria. Use Redis se precisa de cache compartilhado. Se � dados est�ticos, carregue no inicializar do server.
Template: estrutura segura de MCP server
1. Inicializar: conectar DB, carregar dados est�ticos
2. Validar: toda entrada vem parsed, sanitized
3. Processar: l�gica de neg�cio
4. Serializar: converter tipos pra JSON-compat�vel
5. Retornar: sempre retorna formato esperado
Esse padr�o evita 90% dos problemas em produ��o.
Leia também: Os 5 prompts que economizam minha semana | Vibe coding vs pair programming com IA | Entregar projetos rápido com IA