Connettere Claude Code agli strumenti interni: integrare i sistemi aziendali con MCP

Collega i sistemi interni della tua azienda a Claude Code tramite MCP — da un server fetch generico alla scrittura di un MCP Server dedicato, coprendo piattaforme di deploy, monitoraggio, ticket e principi di progettazione degli strumenti.


Nell'articolo precedente abbiamo parlato di come connettere i database. I database hanno protocolli standard, quindi l'integrazione è relativamente semplice. Ma la maggior parte dei team dipende quotidianamente anche da una serie di sistemi interni: piattaforme di deploy, dashboard di monitoraggio, sistemi di ticket, API interne, servizi di configurazione centralizzata.

Questi sistemi di solito non hanno un MCP Server pronto all'uso, ma quasi tutti espongono un'API HTTP. In questo articolo vediamo come usare MCP per collegare questi strumenti interni a Claude Code, permettendogli di consultare metriche di monitoraggio, verificare lo stato dei deploy e gestire ticket direttamente.

Due approcci

Ci sono due modi per integrare strumenti interni:

Approccio 1: Usare un MCP Server HTTP generico
La community offre alcuni MCP Server generici in grado di trasformare qualsiasi API REST in uno strumento MCP. Scrivi un file di descrizione dell'API e il server lo converte in strumenti che Claude può invocare. Ideale per API con struttura semplice che non richiedono logica complessa.

Approccio 2: Scrivere il proprio MCP Server
Usa l'SDK MCP in TypeScript o Python per creare un server dedicato, con controllo completo sulla definizione degli strumenti, la validazione dei parametri e la gestione degli errori. Ideale quando è necessario combinare più API, trasformare dati o aggiungere logica di business.

Questo articolo copre entrambi gli approcci, partendo dal più semplice.

Approccio 1: Integrazione rapida con mcp-server-fetch

La soluzione più leggera è usare il pacchetto ufficiale @anthropic-ai/mcp-server-fetch, che permette a Claude di inviare richieste HTTP direttamente. La configurazione è minimale:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/mcp-server-fetch"]
    }
  }
}

Una volta configurato, Claude può chiamare direttamente le API interne:

Controlla lo stato attuale del servizio sulla piattaforma di deploy https://deploy.internal.com/api/v1/services/user-service

Claude invierà una richiesta GET, otterrà la risposta e te la presenterà in modo leggibile.

Ma questo approccio ha limiti evidenti:

  • Ogni volta bisogna indicare a Claude l'URL completo e il formato della richiesta
  • Non c'è validazione dei parametri; Claude potrebbe sbagliare il percorso
  • L'autenticazione va passata ogni volta o inserita nel prompt (poco sicuro)
  • Non è possibile combinare più chiamate API

Va bene per un uso occasionale, ma non come soluzione a lungo termine.

Approccio 2: Scrivere un MCP Server dedicato

Quando si usa regolarmente un sistema interno, scrivere un MCP Server dedicato è la scelta migliore. Vediamo un esempio concreto: l'integrazione della piattaforma di deploy aziendale.

Supponiamo che la piattaforma di deploy esponga le seguenti API:

  • GET /api/v1/services — elencare tutti i servizi
  • GET /api/v1/services/:name/status — consultare lo stato di un servizio
  • POST /api/v1/services/:name/deploy — avviare un deploy
  • GET /api/v1/services/:name/logs — visualizzare i log di deploy recenti

Scrivere il MCP Server in TypeScript

Per prima cosa, inizializza il progetto:

mkdir mcp-deploy && cd mcp-deploy
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init

Codice principale in 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",
});

// Elencare tutti i servizi
server.tool("list_services", "列出部署平台上的所有服务及其状态", {}, async () => {
  const data = await api("/api/v1/services");
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
});

// Consultare lo stato di un singolo servizio
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) }] };
  }
);

// Visualizzare i log di 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) }] };
  }
);

// Avviare un 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);

Compilare:

npx tsc

Configurare 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"
      }
    }
  }
}

Posiziona il token in .claude/settings.local.json (non committato su git) e l'URL in .claude/settings.json (committato, condiviso con il team).

In pratica

Una volta configurato, la conversazione diventa naturale:

Qual è lo stato attuale di user-service?

→ Claude chiama service_status("user-service")
→ Risposta: in esecuzione, versione v2.3.1, ultimo deploy 2 ore fa, tutti gli health check superati
Qualcuno dei deploy recenti è fallito?

→ Claude chiama deploy_logs("user-service", 20)
→ Analizza i log e ti comunica che il terzo deploy è stato annullato per un timeout dell'health check
Fai il deploy di user-service alla versione v2.3.2

→ Claude chiama trigger_deploy("user-service", "v2.3.2")
→ Dato che la descrizione dello strumento indica "operazione di scrittura", Claude chiederà conferma prima di procedere

Conviene integrare le operazioni di scrittura?

Questa è una domanda che merita una riflessione seria.

Le operazioni di lettura si possono integrare senza problemi. Consultare lo stato, leggere i log, cercare ticket — queste operazioni non hanno effetti collaterali; se Claude sbaglia, non c'è alcun danno.

Le operazioni di scrittura si dividono in due categorie:

Quelle a basso rischio si possono integrare, ma vanno indicate chiaramente nella descrizione dello strumento. Claude chiede automaticamente conferma per le operazioni con effetti collaterali dichiarati. Ad esempio: creare ticket, inviare messaggi, aggiornare configurazioni.

Quelle ad alto rischio è meglio non integrarle. Eliminare risorse, attivare rollback, modificare permessi — le conseguenze sono gravi e irreversibili; è più sicuro eseguirle manualmente.

Se si decide comunque di integrare operazioni di scrittura, vanno fatte almeno due cose:

  1. Indicare chiaramente nella descrizione dello strumento «questa è un'operazione di scrittura che ha impatto sull'ambiente di produzione»
  2. Aggiungere i controlli di sicurezza necessari nel MCP Server (ad esempio, impedire i deploy nel namespace di produzione)

Idee di integrazione per i sistemi interni più comuni

Sistema Strumenti esposti Note
Piattaforma di deploy (K8s / Kamal) Consultare lo stato dei servizi, visualizzare i log, avviare deploy Conferma per le operazioni di scrittura
Monitoraggio (Grafana / Datadog) Consultare metriche, visualizzare lo storico degli allarmi Limitare l'intervallo temporale delle query per evitare sovraccarichi di dati
Ticket (Jira / Linear) Cercare ticket, creare ticket, aggiornare lo stato Creare ticket è una scrittura, ma a basso rischio
Documentazione interna (Notion / Confluence) Cercare documenti, leggere il contenuto delle pagine Attenzione alla paginazione; non caricare troppo in una sola volta
Configurazione centralizzata (Consul / etcd) Leggere le configurazioni, confrontare le differenze tra ambienti Solo lettura; non integrare la scrittura
CI/CD (GitHub Actions / Jenkins) Visualizzare lo stato delle build, avviare build Avviare build è considerata un'operazione di scrittura a rischio medio

Principi di progettazione degli strumenti

Progettare strumenti MCP non è come progettare API. Le API sono pensate per gli sviluppatori; gli strumenti sono pensati per l'IA. Ecco alcuni principi da tenere a mente:

I nomi devono essere espliciti

✗ get_svc_stat     — Claude potrebbe non indovinare il significato dell'abbreviazione
✓ service_status   — immediatamente comprensibile

Le descrizioni vanno scritte pensando all'IA

La descrizione di uno strumento non è documentazione per gli esseri umani; è ciò che Claude usa per decidere quando invocarlo. La descrizione deve chiarire: cosa fa lo strumento, cosa restituisce e quando va usato.

✗ "Ottenere lo stato del servizio"
✓ "Consultare lo stato di deploy attuale, il numero di versione e il risultato degli health check di un servizio specifico. Da usare quando l'utente chiede se un servizio funziona correttamente"

Definire i parametri con chiarezza usando zod

I parametri con .describe() sono quelli che Claude interpreta correttamente. Senza descrizione, Claude può solo indovinare dal nome.

Restituire dati strutturati

Gli strumenti MCP restituiscono testo, ma è preferibile che sia JSON formattato. Claude elabora dati strutturati con molta più precisione rispetto al testo libero.

Granularità adeguata

Non inserire un flusso complesso in un unico strumento. Ma non dividere nemmeno una query semplice in tre strumenti. Il principio è: uno strumento per ogni operazione indipendente e che abbia senso di per sé.

Dove posizionare il MCP Server

Il codice del MCP Server può essere collocato in diversi punti:

Nel repository del progetto (consigliato per iniziare)

your-project/
├── .claude/settings.json
├── mcp-servers/
│   └── deploy/
│       ├── src/index.ts
│       ├── package.json
│       └── tsconfig.json
└── ...

Il vantaggio è che codice e configurazione sono insieme; il team deve solo clonare e installare le dipendenze per iniziare a usarlo.

Repository indipendente

Quando il MCP Server deve essere usato su più progetti, collocarlo in un repository separato e pubblicarlo come pacchetto npm o immagine Docker.

{
  "mcpServers": {
    "deploy": {
      "command": "npx",
      "args": ["-y", "@yourcompany/mcp-deploy-server"]
    }
  }
}

Installazione globale

Per MCP Server di uso trasversale in tutta l'azienda (ad esempio, autenticazione unificata o piattaforma di log centralizzata), installarli globalmente e configurarli in ~/.claude/settings.json.

Suggerimenti per il debug

I problemi più comuni durante lo sviluppo di un MCP Server sono «Claude non invoca il mio strumento» oppure «lo invoca ma dà errore».

Verificare che il server sia avviato

Dopo aver riavviato Claude Code, digitare /mcp per visualizzare l'elenco dei MCP Server connessi. Se il proprio server non compare, verificare che command e args siano corretti.

Testare il server in modo isolato

I MCP Server comunicano via stdio, quindi si possono testare direttamente nel terminale:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js

Se restituisce l'elenco degli strumenti, il server di per sé funziona correttamente.

Controllare i log delle chiamate di Claude

Claude Code mostra input e output di ogni chiamata di strumento. Se i parametri sono errati, di solito è perché la descrizione dello strumento o la definizione dei parametri non era sufficientemente chiara e Claude ha interpretato male.

Caso pratico: integrare Sentry per il tracciamento degli errori

Vediamo un caso concreto completo. Supponiamo di voler integrare Sentry in Claude Code per poter consultare direttamente gli errori in produzione.

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
          ),
        },
      ],
    };
  }
);

Una volta completata l'integrazione, il debug dei problemi in produzione diventa così:

Ci sono stati nuovi errori 500 nelle ultime 4 ore?

→ Claude interroga Sentry
→ Trova 3 nuove issue, la più grave ha coinvolto 120 utenti
→ Recupera automaticamente lo stack trace e individua un'eccezione di puntatore nullo
→ Localizza il punto corrispondente nel codice e propone una correzione

Dalla scoperta del problema alla localizzazione nel codice, l'intero processo si svolge in un'unica conversazione.

Prossimo passo

In questo articolo abbiamo visto come usare MCP per integrare strumenti interni. L'idea di fondo è: il sistema interno ha un'API HTTP → scrivi un MCP Server che la incapsula → Claude può usarla direttamente.

Tutti gli esempi di questo articolo incapsulano API già esistenti — sia la piattaforma di deploy che Sentry hanno già le proprie interfacce, e il MCP Server funge semplicemente da strato di traduzione e adattamento. Nel prossimo articolo affronteremo uno scenario diverso: cosa fare quando la funzionalità di cui hai bisogno non ha un'API disponibile, e come costruire un MCP Server da zero, implementando la logica, gestendo lo stato e orchestrando interazioni complesse a più passaggi.