Conecte os sistemas internos da sua empresa ao Claude Code via MCP — de um servidor fetch genérico a um MCP Server dedicado, cobrindo plataformas de deploy, monitoramento, tickets e princípios de design de ferramentas.
No artigo anterior, falamos sobre conectar bancos de dados. Bancos de dados têm protocolos padronizados, então a integração é relativamente direta. Mas a maioria dos times também depende de vários sistemas internos no dia a dia: plataformas de deploy, painéis de monitoramento, sistemas de tickets, APIs internas, serviços de configuração centralizada.
Esses sistemas normalmente não têm um MCP Server pronto pra usar, mas quase todos expõem uma API HTTP. Neste artigo, vamos ver como usar o MCP para conectar essas ferramentas internas ao Claude Code, permitindo que ele consulte métricas de monitoramento, verifique o status de deploys e gerencie tickets diretamente.
Existem duas formas de integrar ferramentas internas:
Opção 1: Usar um MCP Server HTTP genérico
A comunidade tem alguns MCP Servers genéricos que conseguem encapsular qualquer API REST como uma ferramenta MCP. Você escreve um arquivo de descrição da API e o servidor transforma isso em ferramentas que o Claude pode chamar. Ideal para APIs com estrutura simples que não precisam de lógica complexa.
Opção 2: Escrever seu próprio MCP Server
Use o SDK do MCP em TypeScript ou Python para escrever um servidor dedicado, com controle total sobre a definição de ferramentas, validação de parâmetros e tratamento de erros. Ideal quando você precisa combinar múltiplas APIs, transformar dados ou adicionar lógica de negócio.
Neste artigo, cobrimos as duas opções, começando pela mais simples.
A solução mais leve é usar o pacote oficial @anthropic-ai/mcp-server-fetch, que permite ao Claude fazer requisições HTTP diretamente. A configuração é mínima:
{
"mcpServers": {
"fetch": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-fetch"]
}
}
}
Depois de configurado, o Claude já consegue chamar APIs internas diretamente:
Verifica o status atual do serviço na plataforma de deploy https://deploy.internal.com/api/v1/services/user-service
O Claude vai enviar uma requisição GET, pegar a resposta e interpretar pra você.
Mas essa abordagem tem limitações claras:
Serve pra uso pontual, mas não como solução de longo prazo.
Quando você precisa usar um sistema interno com frequência, escrever um MCP Server dedicado é a melhor escolha. Vamos demonstrar com um caso real: integrar a plataforma de deploy da empresa.
Suponha que sua plataforma de deploy expõe as seguintes APIs:
GET /api/v1/services — listar todos os serviçosGET /api/v1/services/:name/status — consultar o status de um serviçoPOST /api/v1/services/:name/deploy — disparar um deployGET /api/v1/services/:name/logs — ver os logs de deploy recentesPrimeiro, inicialize o projeto:
mkdir mcp-deploy && cd mcp-deploy
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init
Código principal em src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API_BASE = process.env.DEPLOY_API_URL!;
const API_TOKEN = process.env.DEPLOY_API_TOKEN!;
async function api(path: string, method = "GET", body?: unknown) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
throw new Error(`API error: ${res.status} ${await res.text()}`);
}
return res.json();
}
const server = new McpServer({
name: "deploy-platform",
version: "1.0.0",
});
// Listar todos os serviços
server.tool("list_services", "列出部署平台上的所有服务及其状态", {}, async () => {
const data = await api("/api/v1/services");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
});
// Ver o status de um serviço específico
server.tool(
"service_status",
"查看指定服务的当前部署状态、版本号和健康检查结果",
{ name: z.string().describe("服务名称,如 user-service") },
async ({ name }) => {
const data = await api(`/api/v1/services/${name}/status`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// Ver logs de deploy
server.tool(
"deploy_logs",
"查看指定服务最近的部署日志",
{
name: z.string().describe("服务名称"),
limit: z.number().optional().default(10).describe("返回条数,默认 10"),
},
async ({ name, limit }) => {
const data = await api(`/api/v1/services/${name}/logs?limit=${limit}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// Disparar um deploy
server.tool(
"trigger_deploy",
"触发指定服务的部署。这是一个写操作,会实际影响生产环境",
{
name: z.string().describe("服务名称"),
version: z.string().describe("要部署的版本号或 git ref"),
},
async ({ name, version }) => {
const data = await api(`/api/v1/services/${name}/deploy`, "POST", {
version,
});
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
const transport = new StdioServerTransport();
server.connect(transport);
Compilar:
npx tsc
{
"mcpServers": {
"deploy": {
"command": "node",
"args": ["/path/to/mcp-deploy/dist/index.js"],
"env": {
"DEPLOY_API_URL": "https://deploy.internal.com",
"DEPLOY_API_TOKEN": "your-api-token"
}
}
}
}
Coloque o token em .claude/settings.local.json (sem commitar no git) e a URL em .claude/settings.json (commitado, compartilhado com o time).
Depois de configurado, a conversa fica natural:
Qual o status atual do user-service?
→ Claude chama service_status("user-service")
→ Resposta: rodando, versão v2.3.1, último deploy há 2 horas, health checks aprovados
Algum dos deploys recentes falhou?
→ Claude chama deploy_logs("user-service", 20)
→ Analisa os logs e informa que o terceiro deploy foi revertido por timeout no health check
Faz o deploy do user-service na versão v2.3.2
→ Claude chama trigger_deploy("user-service", "v2.3.2")
→ Como a descrição da ferramenta indica "operação de escrita", o Claude vai pedir confirmação primeiro
Essa é uma pergunta que merece reflexão.
Operações de leitura podem ser integradas sem preocupação. Consultar status, ver logs, buscar tickets — essas operações não têm efeitos colaterais; se o Claude errar, não há prejuízo.
Operações de escrita se dividem em duas categorias:
As de baixo risco podem ser integradas, mas é preciso sinalizar claramente na descrição da ferramenta. O Claude solicita confirmação automaticamente para operações marcadas com efeitos colaterais. Por exemplo: criar tickets, enviar mensagens, atualizar configurações.
As de alto risco é melhor não integrar. Excluir recursos, disparar rollbacks, alterar permissões — essas operações têm consequências graves e irreversíveis; é mais seguro fazê-las manualmente.
Se mesmo assim você decidir integrar operações de escrita, faça pelo menos duas coisas:
| Sistema | Ferramentas expostas | Observações |
|---|---|---|
| Plataforma de deploy (K8s / Kamal) | Consultar status de serviços, ver logs, disparar deploys | Confirmar operações de escrita |
| Monitoramento (Grafana / Datadog) | Consultar métricas, ver histórico de alertas | Limitar o intervalo de tempo das consultas pra evitar excesso de dados |
| Tickets (Jira / Linear) | Buscar tickets, criar tickets, atualizar status | Criar tickets é escrita, mas de baixo risco |
| Documentação interna (Notion / Confluence) | Buscar documentos, ler conteúdo de páginas | Atenção com a paginação; não puxar dados demais de uma vez |
| Configuração centralizada (Consul / etcd) | Ler configurações, comparar diferenças entre ambientes | Somente leitura; não integrar escrita |
| CI/CD (GitHub Actions / Jenkins) | Ver status de builds, disparar builds | Disparar builds é considerado escrita de risco médio |
Projetar ferramentas MCP não é a mesma coisa que projetar APIs. APIs são feitas para programadores; ferramentas são feitas para a IA. Alguns princípios importantes:
Nomes devem ser descritivos
✗ get_svc_stat — o Claude pode não adivinhar o significado da abreviação
✓ service_status — fica claro na hora o que faz
Descrições devem ser escritas pensando na IA
A descrição de uma ferramenta não é documentação pra humanos; é o que o Claude usa pra decidir quando chamá-la. A descrição deve explicar: o que a ferramenta faz, o que retorna e quando deve ser usada.
✗ "Obter status do serviço"
✓ "Consultar o status de deploy atual, número da versão e resultado dos health checks de um serviço específico. Usar quando o usuário perguntar se um serviço está funcionando corretamente"
Definir parâmetros com clareza usando zod
Parâmetros com .describe() são os que o Claude entende corretamente. Sem descrição, o Claude só consegue adivinhar pelo nome.
Retornar dados estruturados
Ferramentas MCP retornam texto, mas o ideal é que seja JSON formatado. O Claude processa dados estruturados com muito mais precisão do que texto puro.
Granularidade adequada
Não coloque um fluxo complexo inteiro numa única ferramenta. Também não divida uma consulta simples em três ferramentas. O princípio é: uma ferramenta por operação independente e que faça sentido por si só.
O código do MCP Server pode ficar em vários lugares:
Dentro do repositório do projeto (recomendado pra começar)
your-project/
├── .claude/settings.json
├── mcp-servers/
│ └── deploy/
│ ├── src/index.ts
│ ├── package.json
│ └── tsconfig.json
└── ...
A vantagem é que código e configuração ficam juntos; o time só precisa clonar e instalar as dependências pra começar a usar.
Repositório independente
Quando o MCP Server precisa ser usado em múltiplos projetos, coloque-o num repositório separado e publique como pacote npm ou imagem Docker.
{
"mcpServers": {
"deploy": {
"command": "npx",
"args": ["-y", "@yourcompany/mcp-deploy-server"]
}
}
}
Instalação global
Para MCP Servers de uso geral em toda a empresa (por exemplo, autenticação unificada ou plataforma de logs centralizada), instale globalmente e configure em ~/.claude/settings.json.
Os problemas mais comuns ao desenvolver um MCP Server são «o Claude não chama minha ferramenta» ou «chamou mas deu erro».
Verificar se o servidor iniciou
Após reiniciar o Claude Code, digite /mcp pra ver a lista de MCP Servers conectados. Se o seu servidor não estiver na lista, verifique se o command e os args estão corretos.
Testar o servidor isoladamente
MCP Servers se comunicam por stdio, então dá pra testar direto no terminal:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js
Se retornar a lista de ferramentas, o servidor em si está funcionando.
Verificar os logs de chamada do Claude
O Claude Code mostra a entrada e a saída de cada chamada de ferramenta. Se os parâmetros estiverem errados, geralmente é porque a descrição da ferramenta ou a definição dos parâmetros não estava clara o suficiente e o Claude interpretou errado.
Vamos ver um caso real completo. Suponha que queremos integrar o Sentry ao Claude Code pra que ele consiga consultar erros em produção diretamente.
server.tool(
"search_errors",
"在 Sentry 中搜索最近的错误。用于排查线上问题、查看错误趋势",
{
query: z.string().describe("搜索关键词,如错误信息、函数名"),
hours: z.number().optional().default(24).describe("查看最近多少小时的错误"),
},
async ({ query, hours }) => {
const since = new Date(Date.now() - hours * 3600000).toISOString();
const data = await api(
`/api/0/projects/${ORG}/${PROJECT}/issues/?query=${encodeURIComponent(query)}&start=${since}&sort=date`
);
const summary = data.map((issue: any) => ({
title: issue.title,
count: issue.count,
firstSeen: issue.firstSeen,
lastSeen: issue.lastSeen,
link: issue.permalink,
}));
return {
content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
};
}
);
server.tool(
"error_details",
"查看 Sentry 中某个错误的详细信息,包括堆栈和最近一次事件",
{ issueId: z.string().describe("Sentry issue ID") },
async ({ issueId }) => {
const [issue, latest] = await Promise.all([
api(`/api/0/issues/${issueId}/`),
api(`/api/0/issues/${issueId}/events/latest/`),
]);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
title: issue.title,
count: issue.count,
users: issue.userCount,
stacktrace: latest.entries?.find(
(e: any) => e.type === "exception"
),
},
null,
2
),
},
],
};
}
);
Depois de integrado, a depuração de problemas em produção fica assim:
Teve algum erro 500 novo nas últimas 4 horas?
→ Claude busca no Sentry
→ Encontra 3 issues novos, o mais grave afetou 120 usuários
→ Puxa automaticamente o stack trace e localiza uma exceção de ponteiro nulo
→ Encontra o local correspondente no código e sugere uma correção
Da descoberta do problema à localização no código, todo o processo acontece numa única conversa.
Neste artigo, vimos como usar o MCP para integrar ferramentas internas. A ideia central é: se o sistema interno tem uma API HTTP → você escreve um MCP Server que a encapsula → o Claude consegue usar diretamente.
Todos os exemplos deste artigo encapsulam APIs já existentes — tanto a plataforma de deploy quanto o Sentry já têm interfaces próprias, e o MCP Server atua apenas como uma camada de tradução e adaptação. No próximo artigo, vamos abordar um cenário diferente: o que fazer quando a funcionalidade que você precisa não tem uma API disponível, e como construir um MCP Server do zero, implementando a lógica, gerenciando estado e lidando com interações complexas de múltiplos passos.