Vai al contenuto
sobolewski.proL'IA di oggi, ogni seraIscriviti

Una volta al giorno. Disiscriviti con un clic. Informativa sulla privacy

Come creare un server MCP in Python passo dopo passo

Come creare un server MCP? Scriviamo i nostri strumenti in Python con l'SDK 2.0, li testiamo in MCP Inspector e li colleghiamo a Claude Code e Claude Desktop.

7 min di lettura

Questo articolo in altre lingue:العربيةDeutschEnglishEspañolFrançais日本語Polski简体中文

In questo articolo
  1. Cosa ci serve?
  2. Passo 1: come preparare il progetto?
  3. Passo 2: come creare un server MCP con il primo strumento?
  4. Passo 3: perché non bisogna usare print()?
  5. Passo 4: come testare il server in MCP Inspector?
  6. Passo 5: come collegare il server a Claude Code?
  7. Passo 6: come aggiungere il server a Claude Desktop?
  8. Passo 7: come avviare un server MCP via HTTP?
  9. A cosa fare attenzione?
  10. FAQ

Come creare un server MCP? In Python bastano un solo file e l’MCP Python SDK ufficiale: scriviamo una normale funzione con i tipi e una docstring, aggiungiamo il decoratore @mcp.tool(), e l’SDK la trasforma da solo in uno strumento che Claude, ChatGPT o Cursor possono chiamare. L’intero server dell’esempio nella documentazione occupa una quindicina di righe.

In questa guida scriviamo un server da zero, lo testiamo in MCP Inspector e lo colleghiamo a Claude Code e a Claude Desktop. Comandi e codice vengono dalla guida ufficiale “Build an MCP server”, dal README dell’SDK e dalla documentazione di Claude Code.

Cosa ci serve?

Do per scontato che sappiamo già cos’è MCP, cioè host, client, server e strumenti (se non è così, partiamo dalla guida Cos’è MCP). Per lavorare ci servono:

  1. Python 3.11+ (è quanto richiede il README dell’SDK attuale),
  2. il gestore di pacchetti uv, quello usato dalla guida ufficiale,
  3. Node.js 22.19.0 o successivo, se vogliamo testare il server in MCP Inspector,
  4. un host MCP per verificare il tutto, per esempio Claude Code o Claude Desktop.

Un’avvertenza prima di cominciare: nella versione 2.0 dell’SDK la classe principale ha cambiato nome. Secondo l’elenco delle novità della v2, il vecchio from mcp.server.fastmcp import FastMCP oggi è from mcp.server import MCPServer. Molte guide in rete mostrano ancora la versione vecchia, mentre la guida ufficiale richiede l’SDK 2.0.0 o successivo.

Passo 1: come preparare il progetto?

Per prima cosa installiamo uv (macOS e Linux):

curl -LsSf https://astral.sh/uv/install.sh | sh

Su Windows, in PowerShell:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Dopo l’installazione riavviamo il terminale, così il comando uv diventa disponibile. Poi creiamo il progetto e installiamo l’SDK:

uv init mio-server
cd mio-server
uv venv
source .venv/bin/activate
uv add "mcp[cli]"
touch server.py

L’extra [cli] installa il comando mcp (con i sottocomandi mcp dev, mcp run e mcp install), che ci tornerà utile per i test (su Windows l’ambiente si attiva con .venv\Scripts\activate).

Passo 2: come creare un server MCP con il primo strumento?

Nel file server.py copiamo l’esempio del README dell’SDK e in fondo aggiungiamo l’avvio del server, come fa la guida ufficiale:

from mcp.server import MCPServer

mcp = MCPServer("Demo")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"


if __name__ == "__main__":
    mcp.run(transport="stdio")

Questo è un server MCP completo, con uno strumento (add) e una risorsa il cui indirizzo è un modello (greeting://{name}). Non scriviamo nessun JSON Schema: secondo il README la firma a: int, b: int è già lo schema, e la docstring diventa la descrizione dello strumento che il modello legge. Del parsing delle richieste, della validazione e del protocollo si occupa l’SDK.

Per questo le docstring vanno curate. Il modello sceglie lo strumento in base al nome e alla descrizione, quindi “Add two numbers.” è il minimo, e negli strumenti veri descriviamo anche gli argomenti, come nell’esempio ufficiale del meteo:

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """

(questo esempio usa l’API del servizio meteorologico statunitense NWS, quindi funziona solo per località negli Stati Uniti)

Passo 3: perché non bisogna usare print()?

È l’errore più frequente con il primo server. Un server locale comunica con l’host tramite standard input e output (stdio), e i messaggi sono in formato JSON-RPC. La documentazione lo dice senza giri di parole: scrivere su stdout corrompe i messaggi JSON-RPC e manda fuori uso il server, e print() scrive proprio su stdout, se non gli diciamo altro.

Al suo posto usiamo il modulo logging, che scrive su stderr:

import logging

logger = logging.getLogger(__name__)

logger.info("Processing request")  # finisce su stderr

Con i server HTTP il problema non si pone, perché i log su stdout non si mescolano alle risposte HTTP.

Passo 4: come testare il server in MCP Inspector?

MCP Inspector è lo strumento ufficiale per testare i server. Dall’SDK si avvia con un solo comando:

uv run mcp dev server.py

Si apre il browser con l’Inspector. Secondo il README, chiamando add con gli argomenti a=1 e b=2 otteniamo 3. Nella stessa schermata vediamo l’elenco di strumenti e risorse e i messaggi JSON-RPC grezzi, che aiutano parecchio quando qualcosa non funziona.

L’Inspector ha anche una versione da terminale (--tui) e una modalità per gli script (--cli). Quest’ultima stampa, per esempio, l’elenco degli strumenti e poi termina, quindi va bene per la CI:

npx @modelcontextprotocol/inspector --cli uv run server.py --method tools/list

(controlla la versione di Node.js con node --version: l’Inspector richiede la 22.19.0 o successiva)

Passo 5: come collegare il server a Claude Code?

Un server locale si aggiunge come server di tipo stdio. Tutto ciò che segue -- è il comando che Claude Code eseguirà:

claude mcp add --transport stdio demo -- uv --directory /PERCORSO/COMPLETO/mio-server run server.py

Il percorso deve essere assoluto (lo ricaviamo con pwd nella cartella del progetto). Lo stato della connessione lo mostra claude mcp get demo, e dentro una sessione di Claude Code il comando /mcp. A questo punto possiamo chiedere, per esempio, “somma 1234 e 5678 con lo strumento add” e approvare la chiamata.

Due cose dalla documentazione di Claude Code servono fin da subito:

  1. il tempo di avvio del server si regola con la variabile MCP_TIMEOUT, per esempio MCP_TIMEOUT=10000 claude concede al server 10 secondi,
  2. Claude Code avvisa quando il risultato di uno strumento supera i 10 000 token e per impostazione predefinita lo tronca a 25 000 token (il limite si cambia con la variabile MAX_MCP_OUTPUT_TOKENS), quindi gli strumenti non dovrebbero restituire interi database.

Passo 6: come aggiungere il server a Claude Desktop?

In Claude Desktop i server locali si aggiungono al file di configurazione claude_desktop_config.json. Si trova in ~/Library/Application Support/Claude/ su macOS, in ~/.config/Claude/ su Linux e in %AppData%\Claude\ su Windows (se non c’è, lo creiamo). La voce da inserire è questa:

{
  "mcpServers": {
    "demo": {
      "command": "uv",
      "args": [
        "--directory",
        "/PERCORSO/COMPLETO/mio-server",
        "run",
        "server.py"
      ]
    }
  }
}

Poi chiudiamo del tutto l’app (su macOS con Cmd+Q, chiudere la finestra non basta) e la riapriamo. Il server dovrebbe comparire nel menu “Connectors”, sotto l’icona del più. Se non c’è, la documentazione consiglia di controllare la sintassi del JSON, di verificare che il percorso sia assoluto e di scrivere nel campo command il percorso completo di uv (lo mostra which uv). I log sono in ~/Library/Logs/Claude (macOS) oppure in ~/.config/Claude/logs/ (Linux), nei file mcp.log e mcp-server-NOME.log.

Passo 7: come avviare un server MCP via HTTP?

Un server stdio funziona solo sul nostro computer. Per esporlo come server remoto lo avviamo con Streamable HTTP, il trasporto che secondo il README dell’SDK si usa in produzione:

uv run mcp run server.py --transport streamable-http

Il server resta allora in ascolto all’indirizzo http://localhost:8000/mcp, e in Claude Code lo aggiungiamo come qualsiasi server remoto:

claude mcp add --transport http demo-http http://localhost:8000/mcp

Perché un server del genere funzioni nell’app Claude nel browser come “custom connector”, deve essere raggiungibile da internet, dato che Claude si collega dal cloud di Anthropic. A quel punto serve anche il login: la documentazione di MCP descrive l’autorizzazione dei server tramite OAuth 2.1.

A cosa fare attenzione?

Primo: a ciò che lo strumento permette di fare. Un server MCP dà al modello reali capacità di agire sul nostro computer, quindi scriviamo strumenti il più possibile ristretti: meglio “leggi un file da questa cartella” che “esegui un comando qualsiasi”.

Secondo: ai dati che arrivano da fuori. Se uno strumento scarica contenuti da internet o dalle email, può portarsi dietro un prompt injection, cioè istruzioni nascoste rivolte al modello. Ne ho scritto di più nella guida Sicurezza degli agenti AI.

Terzo: ai segreti. Le chiavi API passiamole tramite variabili d’ambiente (--env in Claude Code) e non nel codice. Secondo la documentazione, l’Inspector salva i token nel portachiavi di sistema e, quando questo manca (per esempio in un container), in un file non cifrato, ~/.mcp-inspector/secrets.json.

Un server così possiamo poi collegarlo anche a un nostro agente, come mostro nella guida Come creare un agente AI in Python.

FAQ

Come creare un server MCP nel modo più semplice?

Il server più semplice, quello del README dell’SDK, occupa una quindicina di righe: un oggetto MCPServer, una funzione con il decoratore @mcp.tool() e mcp.run(). Il resto, cioè lo schema degli argomenti e la gestione del protocollo, lo fa l’SDK.

Cos’è FastMCP?

È il vecchio nome della classe principale del server nell’MCP Python SDK. Nella versione 2.0 è diventata MCPServer, e l’import è from mcp.server import MCPServer.

Un server MCP si può scrivere in un linguaggio diverso da Python?

Sì. La guida ufficiale ha versioni, tra gli altri, per TypeScript, Java, Kotlin, C#, Ruby, Rust e Go.

Un server MCP funziona solo con Claude?

No. MCP è uno standard aperto, quindi lo stesso server funziona in qualsiasi host che supporta il protocollo, per esempio in ChatGPT, Visual Studio Code o Cursor.

  • AI
  • agenti AI
  • Claude Code
  • programmazione
  • guida

L'IA di oggi, ogni sera

Ogni sera le notizie più importanti sull'IA e i nuovi articoli del blog. In breve, con le fonti.

Una volta al giorno. Disiscriviti con un clic. Informativa sulla privacy