Conectar o Claude Code a ferramentas internas: integre seus sistemas com MCP

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.

Dois caminhos

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.

Opção 1: Integração rápida com mcp-server-fetch

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:

  • Toda vez você precisa informar ao Claude a URL completa e o formato da requisição
  • Não há validação de parâmetros; o Claude pode errar o caminho
  • A autenticação precisa ser passada a cada vez ou colocada no prompt (inseguro)
  • Não é possível combinar múltiplas chamadas de API

Serve pra uso pontual, mas não como solução de longo prazo.

Opção 2: Escrever um MCP Server dedicado

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ços
  • GET /api/v1/services/:name/status — consultar o status de um serviço
  • POST /api/v1/services/:name/deploy — disparar um deploy
  • GET /api/v1/services/:name/logs — ver os logs de deploy recentes

Escrevendo o MCP Server em TypeScript

Primeiro, 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

Configurar o Claude Code

{
  "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).

Na prática

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

Vale a pena integrar operações de escrita?

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:

  1. Indique claramente na descrição da ferramenta «esta é uma operação de escrita que afeta o ambiente de produção»
  2. Adicione as validações de segurança necessárias no MCP Server (por exemplo, impedir deploys no namespace de produção)

Ideias de integração para sistemas internos comuns

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

Princípios de design de ferramentas

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ó.

Onde colocar o MCP Server

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.

Dicas de depuração

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.

Caso prático: integrar o Sentry para rastreamento de erros

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.

Próximo passo

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.