Quando l'assistente AI ha bisogno di leggere il tuo database, controllare un'email, lanciare un report o muoversi nel tuo cloud aziendale, non ti basta una chat: ti serve un'interfaccia tra il modello e i tuoi sistemi. Model Context Protocol (MCP), lo standard aperto lanciato da Anthropic nel novembre 2024 e adottato in 18 mesi da OpenAI, Google, Microsoft e Cursor, è quell'interfaccia. In questa guida costruiamo da zero un MCP server in Python che espone tre strumenti reali — lettura PostgreSQL, ricerca su file PDF locali, invio di Slack — e lo colleghiamo a Claude Desktop, ChatGPT e Cursor.
A chi serve questo tutorial e cosa otterrai
Ti serve se stai costruendo agenti AI per la tua azienda, se vuoi che Claude o ChatGPT abbiano accesso a dati che non sono pubblici sul web (CRM, ticket, log applicativi), o se vuoi capire come funziona davvero il tool use sotto il cofano. Alla fine avrai: un server MCP locale funzionante con tre tool, una resource esposta ai client, prompt riutilizzabili, e l'integrazione con tre client diversi.
Prerequisiti reali
- Python 3.10+ installato (verifica con
python --version). - Una shell (macOS, Linux o WSL su Windows; PowerShell funziona ma alcuni comandi cambiano).
- Un editor: VS Code o Cursor sono ottimi.
- Un client MCP almeno fra: Claude Desktop (gratis), ChatGPT (con piano Pro/Team e MCP attivato), Cursor (free tier sufficiente per test).
- Per i tool d'esempio: PostgreSQL locale (oppure usa SQLite, ti spiego come switchare) e un workspace Slack di test con un bot token.
Quale SDK usare: ufficiale o FastMCP?
Ci sono due strade ufficialmente raccomandate da Anthropic.
- MCP Python SDK: l'SDK ufficiale (modelcontextprotocol/python-sdk). Più verboso, controllo fine, indicato per casi enterprise complessi.
- FastMCP: wrapper di alto livello (jlowin/fastmcp) ispirato a FastAPI. Decoratori semplici, sviluppo rapido. È oggi il modo più veloce per partire ed è ufficialmente supportato.
In questo tutorial usiamo FastMCP 2.x: stessa potenza, metà del codice.
Passo 1 — Setup del progetto
Crea una cartella, un ambiente virtuale e installa le dipendenze:
mkdir mcp-azienda && cd mcp-azienda
python -m venv .venv
source .venv/bin/activate # su Windows: .venv\Scripts\activate
pip install fastmcp psycopg2-binary pypdf slack_sdk python-dotenv
Crea un file .env con le credenziali:
POSTGRES_URL=postgresql://utente:password@localhost:5432/mio_db
SLACK_BOT_TOKEN=xoxb-...
PDF_DIR=/Users/me/documenti
Passo 2 — Lo scheletro del server
Crea il file server.py:
from fastmcp import FastMCP
from dotenv import load_dotenv
import os
load_dotenv()
mcp = FastMCP("azienda-tools")
@mcp.tool()
def ping() -> str:
"""Ritorna pong, utile per verificare che il server funzioni."""
return "pong"
if __name__ == "__main__":
mcp.run()
Avvialo:
python server.py
Se vedi Starting MCP server «azienda-tools» via stdio sei a posto. Stdio è il transport più semplice: il server parla con il client tramite standard input/output. Esistono anche http e sse per server remoti, vedremo dopo come passare.
Passo 3 — Tool 1: leggere PostgreSQL
Aggiungi al file:
import psycopg2
from psycopg2.extras import RealDictCursor
@mcp.tool()
def query_database(sql: str, limit: int = 50) -> list[dict]:
"""Esegue una query SELECT (sola lettura) sul database aziendale.
Per sicurezza vengono accettate solo query SELECT e viene applicato un LIMIT.
"""
if not sql.strip().lower().startswith("select"):
raise ValueError("Solo query SELECT sono permesse")
if "limit" not in sql.lower():
sql = sql.rstrip(";") + f" LIMIT {limit};"
with psycopg2.connect(os.environ["POSTGRES_URL"]) as conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute(sql)
return [dict(r) for r in cur.fetchall()]
Tre cose da notare. Primo: la docstring diventa la descrizione che il modello AI vede. Scrivila bene, perché influenza quando l'assistente decide di usare il tool. Secondo: il type hint sui parametri viene tradotto in schema JSON per il client. Terzo: una guardia di sicurezza elementare (solo SELECT, LIMIT obbligatorio) — gli MCP server hanno accesso reale ai dati, scrivi sempre come se l'altro lato fosse compromesso.
Passo 4 — Tool 2: cercare nei PDF
from pypdf import PdfReader
import pathlib
@mcp.tool()
def cerca_nei_pdf(testo: str, max_risultati: int = 5) -> list[dict]:
"""Cerca un'espressione nei PDF della cartella documenti aziendali.
Ritorna nome file, pagina e estratto attorno al match.
"""
risultati = []
base = pathlib.Path(os.environ["PDF_DIR"])
for pdf in base.glob("**/*.pdf"):
try:
reader = PdfReader(pdf)
for i, page in enumerate(reader.pages, 1):
t = page.extract_text() or ""
idx = t.lower().find(testo.lower())
if idx >= 0:
estratto = t[max(0, idx-100):idx+200]
risultati.append({"file": str(pdf.name), "pagina": i, "estratto": estratto})
if len(risultati) >= max_risultati:
return risultati
except Exception:
continue
return risultati
Passo 5 — Tool 3: postare su Slack
from slack_sdk import WebClient
@mcp.tool()
def posta_slack(canale: str, messaggio: str) -> str:
"""Posta un messaggio in un canale Slack. Il canale va indicato come #nome."""
client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
res = client.chat_postMessage(channel=canale, text=messaggio)
return f"Messaggio inviato (ts={res['ts']})"
Passo 6 — Una resource: il listino aziendale
Le risorse MCP sono file o dati che il client può allegare alla conversazione. Aggiungi:
@mcp.resource("file://listino.csv")
def listino() -> str:
"""Il listino prezzi 2026 in CSV."""
return open("./dati/listino.csv").read()
Anche un prompt riutilizzabile:
@mcp.prompt()
def analizza_ticket(testo_ticket: str) -> str:
return f"Sei un analista di supporto. Classifica per priorità (alta/media/bassa) e categoria questo ticket: {testo_ticket}"
Passo 7 — Collegare Claude Desktop
Apri il file di configurazione di Claude Desktop:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Aggiungi:
{
"mcpServers": {
"azienda-tools": {
"command": "/Users/me/mcp-azienda/.venv/bin/python",
"args": ["/Users/me/mcp-azienda/server.py"]
}
}
}
Riavvia Claude Desktop. Nella barra in basso compare l'icona MCP con i tool elencati. Prova a chiedere: «Cerca nei PDF se compare la parola fattura e poi posta un riassunto su #generale».
Passo 8 — Collegare ChatGPT e Cursor
Per ChatGPT (Pro/Team/Enterprise) vai su Settings → Connectors e aggiungi un Custom MCP Server, scegliendo il transport HTTP. Per esporre il tuo server in HTTP cambia l'avvio:
mcp.run(transport="http", host="127.0.0.1", port=8765)
E indica a ChatGPT l'URL http://127.0.0.1:8765. Per accessi da remoto usa un tunnel sicuro (ngrok, Cloudflare Tunnel) e abilita un'API key tramite l'hook di autenticazione di FastMCP.
Per Cursor: apri Settings → MCP, clicca «Add new MCP Server» e incolla la stessa configurazione di Claude Desktop. Cursor è particolarmente utile per debug: mostra ogni request/response JSON-RPC.
Errori comuni e come risolverli
- «Server not responding» in Claude Desktop: il path del Python è sbagliato. Usa il path assoluto dell'interpreter del tuo venv, non
python. - Tool non chiamato: la docstring è troppo vaga. Riscrivila in italiano spiegando quando il modello dovrebbe usarlo («Usa questo tool quando l'utente chiede di...»).
- «Tool call failed: validation error»: i parametri del modello non rispettano lo schema. Aggiungi type hint più espliciti (es.
Literal["high","med","low"]) e usaFielddi Pydantic per i vincoli. - Risposte JSON troppo grandi: i client MCP hanno un limite. Pagina o tronca i risultati e ritorna anche un campo «has_more».
Sicurezza, varianti e prossimi passi
L'MCP non è un endpoint pubblico per default ma se lo esponi su HTTP devi pensare a tre cose. Autenticazione: FastMCP supporta middleware bearer-token. Permessi per tool: separa un MCP a sola lettura da uno con write. Logging: registra ogni chiamata, con utente e parametri, per audit. Esistono già soluzioni open-source come modelcontextprotocol/registry per gestire più server in un'organizzazione.
Quando NON usare un MCP server: se l'assistente AI deve fare cose semplici e già pubbliche sul web, basta un browser tool come quello incluso in Claude. Se invece i dati sono solo nel tuo gestionale, nei tuoi PDF o nei tuoi database, MCP è il modo pulito per dargli accesso, mantenendo però controllo su cosa succede a ogni chiamata. Una buona prossima tappa è aggiungere OAuth2 per Slack e Microsoft Graph, oppure costruire un MCP che parla con n8n e attiva flussi già configurati. La specifica MCP è pubblica e in evoluzione: vale la pena tenerla sul comodino.




