Construindo Seu Primeiro Servidor MCP
O Model Context Protocol (MCP) é o mais próximo que o ecossistema de IA tem de um adaptador universal. Ele define como modelos de IA descobrem e chamam ferramentas, e como essas ferramentas retornam resultados estruturados. Uma vez que você tem um servidor MCP rodando, qualquer cliente compatível — Claude, Claude Code, ou qualquer framework de agentes que fale MCP — pode usá-lo sem código de integração adicional.
Este guia percorre a construção de um servidor MCP real do zero. Não um exemplo de brinquedo, mas um padrão que você pode estender para uso em produção.
O Que o MCP Realmente É
MCP é um protocolo JSON-RPC 2.0 sobre stdio (ou HTTP+SSE para servidores remotos). O cliente e o servidor trocam um pequeno conjunto de tipos de mensagens:
tools/list— o cliente pergunta quais ferramentas estão disponíveistools/call— o cliente invoca uma ferramenta com argumentosresources/list/resources/read— o cliente lê arquivos, bancos de dados, ou qualquer contexto que o servidor expõe
O servidor declara suas ferramentas antecipadamente com um JSON Schema para a entrada de cada ferramenta. O cliente usa esse esquema para construir chamadas válidas. Toda a interação é stateless da perspectiva do cliente — o servidor gerencia internamente qualquer estado necessário.
Essa simplicidade é o ponto. MCP não é um framework. É um contrato que permite envolver qualquer coisa — um banco de dados Postgres, um cluster Kubernetes, um repositório GitHub, uma API interna proprietária — e expô-la como um conjunto de funções tipadas e chamáveis que qualquer modelo de IA pode usar.
Configuração
Você precisará de Node.js 18+ e um projeto TypeScript. O SDK oficial do MCP torna o código padrão mínimo:
mkdir mcp-server && cd mcp-servernpm init -ynpm install @modelcontextprotocol/sdk zodnpm install -D typescript @types/node tsxAdicione um tsconfig.json:
{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "strict": true, "outDir": "./dist" }, "include": ["src"]}E um script de início em package.json:
{ "scripts": { "dev": "tsx src/index.ts", "build": "tsc", "start": "node dist/index.js" }}Construindo o Servidor
Crie src/index.ts. A estrutura é sempre a mesma: inicializar o servidor, registrar ferramentas, conectar ao transporte stdio.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod";
const server = new McpServer({ name: "my-mcp-server", version: "1.0.0",});Agora registre ferramentas. Cada ferramenta tem um nome, uma descrição que o modelo usa para decidir quando chamá-la, e um esquema de entrada:
server.tool( "get_weather", "Obter condições climáticas atuais de uma cidade", { city: z.string().describe("O nome da cidade para obter o clima"), units: z.enum(["celsius", "fahrenheit"]).default("celsius"), }, async ({ city, units }) => { // Sua implementação real aqui const data = await fetchWeather(city, units); return { content: [ { type: "text", text: `Clima em ${city}: ${data.temp}°${units === "celsius" ? "C" : "F"}, ${data.condition}`, }, ], }; });Conecte o servidor ao transporte stdio:
const transport = new StdioServerTransport();await server.connect(transport);Esse é o esqueleto completo do servidor. Todo o resto é adicionar ferramentas.
Padrões de Design de Ferramentas
A qualidade do seu servidor MCP depende de quão bem você projeta as ferramentas. Alguns padrões que importam:
Ferramentas estreitas e componíveis em vez de monolitos amplos
Uma ferramenta que faz uma coisa só é mais útil do que uma que faz muitas. O modelo pode combinar ferramentas estreitas de formas inesperadas; uma ferramenta ampla o limita.
Evite isso:
server.tool("manage_database", "Fazer qualquer coisa com o banco de dados", { operation: z.enum(["read", "write", "delete", "schema"]), query: z.string(), // ... muitos parâmetros opcionais})Prefira isso:
server.tool("query_records", "Executar uma consulta SELECT e retornar linhas como JSON", { table: z.string(), where: z.string().optional().describe("Cláusula SQL WHERE, ex. 'status = active'"), limit: z.number().default(50),})
server.tool("get_schema", "Retornar as definições de colunas de uma tabela", { table: z.string(),})Descreva as entradas com precisão
O modelo lê suas strings describe() para saber o que passar. Trate-as como documentação para um desenvolvedor que nunca viu seu código:
{ date_range: z.string().describe( "Intervalo de datas ISO 8601 no formato YYYY-MM-DD/YYYY-MM-DD. " + "Exemplo: 2025-01-01/2025-03-31" ),}Descrições vagas produzem entradas incorretas. Descrições precisas produzem entradas corretas.
Retorne texto estruturado, não JSON bruto
Modelos analisam texto de forma mais confiável do que blobs JSON brutos nos resultados de ferramentas. Formate sua saída:
return { content: [ { type: "text", text: [ `Encontrados ${rows.length} registros:`, ...rows.map(r => `- ${r.id}: ${r.name} (${r.status})`), ].join("\n"), }, ],};Tratamento de erros
Retorne erros como resultados de ferramentas, não como exceções lançadas. O flag isError: true sinaliza a falha ao agente:
async ({ table, where }) => { try { const rows = await db.query(table, where); return { content: [{ type: "text", text: formatRows(rows) }] }; } catch (err) { return { content: [ { type: "text", text: `Consulta falhou: ${err instanceof Error ? err.message : "erro desconhecido"}. ` + `Verifique se o nome da tabela está correto e se a cláusula WHERE usa nomes de colunas válidos.`, }, ], isError: true, }; }}Um Exemplo Real: Servidor de Ferramentas GitHub
Aqui está uma ferramenta concreta que envolve a API do GitHub para permitir que um agente leia o conteúdo de repositórios:
import { Octokit } from "@octokit/rest";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
server.tool( "get_file_contents", "Ler o conteúdo de um arquivo de um repositório GitHub", { owner: z.string().describe("Proprietário do repositório (usuário ou organização)"), repo: z.string().describe("Nome do repositório"), path: z.string().describe("Caminho do arquivo dentro do repositório"), ref: z.string().optional().describe("Branch, tag ou SHA de commit. Padrão é a branch principal."), }, async ({ owner, repo, path, ref }) => { try { const { data } = await octokit.rest.repos.getContent({ owner, repo, path, ref });
if (Array.isArray(data)) { const listing = data.map(f => `${f.type === "dir" ? "📁" : "📄"} ${f.name}`).join("\n"); return { content: [{ type: "text", text: `Listagem de ${path}:\n${listing}` }], }; }
if (data.type !== "file" || !data.content) { return { content: [{ type: "text", text: `${path} não é um arquivo legível` }], isError: true, }; }
const content = Buffer.from(data.content, "base64").toString("utf-8"); return { content: [{ type: "text", text: `Conteúdo de ${owner}/${repo}/${path}:\n\`\`\`\n${content}\n\`\`\`` }], }; } catch (err: unknown) { const message = err instanceof Error ? err.message : "Erro desconhecido"; return { content: [{ type: "text", text: `Falha ao ler ${path}: ${message}` }], isError: true, }; } });Conectando ao Claude Desktop
Uma vez que seu servidor está rodando, conecte-o ao Claude Desktop editando sua configuração em ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) ou %APPDATA%\Claude\claude_desktop_config.json (Windows):
{ "mcpServers": { "my-server": { "command": "node", "args": ["/caminho/absoluto/para/dist/index.js"], "env": { "GITHUB_TOKEN": "seu_token_aqui" } } }}Reinicie o Claude Desktop. Suas ferramentas aparecem automaticamente no seletor de ferramentas.
Conectando ao Claude Code
Servidores MCP se conectam ao Claude Code via CLI:
claude mcp add my-server node /caminho/absoluto/para/dist/index.jsO Claude Code usará automaticamente suas ferramentas ao trabalhar em tarefas que se beneficiem delas.
O Que Construir a Seguir
Um servidor MCP é essencialmente um limite de capacidades: o que o agente pode ver e fazer. Algumas direções que valem a pena explorar:
- Servidores de banco de dados — Exponha acesso somente leitura ao Postgres, SQLite ou DynamoDB com ferramentas de introspecção de esquema
- Servidores de análise de código — Envolva tree-sitter ou um servidor de protocolo de linguagem para dar aos agentes compreensão semântica de bases de código
- Servidores de monitoramento — Conecte sistemas de métricas (Prometheus, Datadog) para que agentes investiguem incidentes com dados reais de observabilidade
- Servidores de documentação — Indexe e sirva documentação interna, permitindo que agentes respondam perguntas operacionais
O ecossistema MCP ainda é jovem. A maioria dos servidores úteis ainda não foi construída. Construa o que resolve um problema real no seu ambiente, e o protocolo cuidará do resto.
Artigos Relacionados
- Introdução ao Desenvolvimento Agêntico
- Padrões de Uso de Ferramentas: Interfaces Agente-Ferramenta Confiáveis
- Padrões Multi-Agente: Orquestradores, Workers e Pipelines