Server MCP in Python: guida pratica dal test all'hosting
Crea un server MCP in Python per consultare gli ordini, testalo con Inspector e collegalo a Claude Code e Cursor. Poi passa a HTTP con autenticazione.
Pubblicato il

Con un server MCP puoi far rispondere Claude Code o Cursor alle domande sullo stato degli ordini usando i dati aziendali, senza copiare le informazioni nella chat. Crea un solo strumento MCP in sola lettura, verificane il funzionamento con Inspector, poi scegli tra un processo locale e un servizio HTTP autenticato da condividere con il team.
Il primo server utile ha un compito preciso: ricevere l'ID di un ordine e restituirne lo stato. Parti da qui. Uno strumento generico per interrogare il database lascia al modello troppe decisioni e gli concede più accesso di quanto serva a questo flusso di lavoro.
Questa guida segue il tutorial ufficiale per creare un server. Al 7 ottobre 2026, la documentazione fa riferimento alla specifica MCP 2026-07-28 e all'SDK Python ufficiale, la libreria che gestisce i messaggi MCP, con la sua API MCPServer. L'esempio fissa la versione dell'SDK a 2.3.0, così gli import di un vecchio tutorial non possono modificare a tua insaputa ciò che installi.
Quali funzionalità espone un server MCP?
Un server MCP funziona come uno sportello controllato tra un'applicazione di IA e i tuoi sistemi. L'applicazione può chiedere quali operazioni sono disponibili, inviare una richiesta e ricevere un risultato. È il tuo codice a stabilire che cosa può fare lo sportello.
MCP, il Model Context Protocol, dà a questo scambio un formato comune. L'host è l'applicazione che usi, per esempio Claude Code o Cursor. Il suo client gestisce la comunicazione con il server secondo il protocollo. Sono ruoli, non tre applicazioni aggiuntive da installare.
Uno strumento può essere in sola lettura. Chiamare qualcosa «risorsa» non sostituisce i controlli di accesso. Un prompt fornisce istruzioni, non autorizzazioni. Il supporto dei client e il modo in cui presentano le funzionalità variano: verifica quelle che esponi davvero. Queste sono le tre primitive del server; al server qui sotto basta uno strumento.

Prima di iniziare, controlla se esiste già un connettore mantenuto che risponde alla tua esigenza. La nostra selezione dei migliori server MCP del 2026 è un buon punto di partenza. Un server personalizzato ha senso quando i dati interni, le regole di accesso o il flusso di lavoro differiscono da quelli previsti dai connettori disponibili.
Creare in Python una consultazione ordini in sola lettura
Lascia che l'SDK ufficiale gestisca il protocollo e concentra il tuo codice sulla consultazione. Servono Python 3.10 o successivo, uv, il gestore di progetti Python usato nel tutorial ufficiale, e Node.js per Inspector. La versione attuale di Inspector richiede Node 22.19.0 o successivo.
Nel terminale, esegui questi comandi nell'ordine indicato:
uv init orders-mcpcd orders-mcpuv venvuv add "mcp[cli]==2.3.0"
Crea orders.py in quella cartella e incolla il server completo qui sotto. I record sono dati fittizi per l'esercitazione: non contengono nomi di clienti, dettagli di pagamento o credenziali API.
import json
from mcp.server import MCPServer
mcp = MCPServer("orders")
# Fictional training data. No customer records or credentials.
ORDERS = {
"A100": {"status": "shipped", "carrier": "Demo Courier"},
"A101": {"status": "packing", "carrier": "not assigned"},
}
@mcp.tool()
def lookup_order(order_id: str) -> str:
"""Look up a fictional order by ID, such as A100. Read-only.
Args:
order_id: Exact order ID, for example A100 or A101.
"""
key = order_id.strip().upper()
order = ORDERS.get(key)
if order is None:
return json.dumps({"found": False, "order_id": key})
return json.dumps({"found": True, "order_id": key, **order})
if __name__ == "__main__":
mcp.run(transport="stdio")Il codice usa lo schema documentato nel tutorial: MCPServer, @mcp.tool() e mcp.run(transport="stdio"), sostituendo gli strumenti meteo con la consultazione degli ordini. L'annotazione di tipo stringa indica all'SDK che order_id è un campo testuale obbligatorio. La docstring spiega al client quando usare lo strumento. L'SDK genera la definizione dello strumento e gestisce i messaggi del protocollo.
Esegui uv run orders.py. È normale che il processo resti in attesa di input senza mostrare nulla. stdio, l'input e l'output standard, è il canale usato dal client per comunicare con questo processo. Interrompi l'esecuzione manuale prima di lasciare che un client avvii la propria copia.
Non scrivere i log dell'applicazione sull'output standard. Usa il modulo logging di Python, che per impostazione predefinita scrive sull'errore standard. Un print() fuori posto può corrompere il flusso del protocollo. È un vincolo documentato di stdio, non una preferenza estetica nella gestione dei log.
Quando sostituisci il dizionario con un database, mantieni la stessa interfaccia circoscritta. Usa una query parametrizzata, un account del database che possa leggere soltanto i campi necessari e un controllo dei permessi specifico per il chiamante prima di restituire un record. Per questo compito, non accettare mai dal modello un'istruzione SQL arbitraria.
Come testare lo strumento con MCP Inspector
Verifica che lo strumento funzioni prima di chiedere a un modello di usarlo. Dalla cartella del progetto, esegui uv run mcp dev orders.py. Il comando di sviluppo dell'SDK avvia MCP Inspector. Apri nel browser l'URL mostrato dal comando e collega il server, se non è già connesso.
In Tools, seleziona lookup_order. Il modulo dovrebbe mostrare il campo obbligatorio order_id. Esegui una chiamata con A100: il risultato dovrebbe contenere found: true, status: shipped e carrier: Demo Courier. Usa A101 per ottenere packing. Con DOES-NOT-EXIST, il risultato dovrebbe contenere found: false.
Prova anche una richiesta senza order_id. La validazione dell'input dovrebbe respingerla prima di eseguire la consultazione. Le viste Protocol e Console di Inspector aiutano a distinguere una richiesta malformata da un errore del processo server.
Per una verifica ripetibile dal terminale, usa npx @modelcontextprotocol/inspector --cli uv run orders.py --method tools/list. Per chiamare lo strumento, usa npx @modelcontextprotocol/inspector --cli uv run orders.py --method tools/call --tool-name lookup_order --tool-arg order_id=A100. Entrambi seguono la CLI documentata di Inspector.
I criteri per superare il test sono concreti: uno strumento individuabile dal client, lo stato atteso per un ordine noto e una risposta esplicita quando il record non esiste. Un paragrafo plausibile generato dal modello non dimostra che la consultazione sia stata eseguita.
Collegare lo stesso server a Claude Code e Cursor
Ogni client locale avvia un proprio processo server. Non serve lasciare Inspector in esecuzione. Usa percorsi assoluti, così l'avvio non dipende dalla cartella aperta dal client.
Claude Code: esegui claude mcp add --transport stdio --scope local orders -- /ABSOLUTE/PATH/orders-mcp/.venv/bin/python /ABSOLUTE/PATH/orders-mcp/orders.py. Su Windows, l'interprete è .venv\Scripts\python.exe. Il comando segue la sintassi di Claude Code per i server locali, compreso il separatore -- prima del comando di avvio.
Esegui claude mcp get orders per verificare la connessione. In una sessione di Claude Code, apri /mcp, poi chiedi: «Usa lookup_order per controllare A100. Riporta soltanto lo stato e il corriere restituiti». Esamina la chiamata allo strumento e i suoi argomenti.
Cursor: crea .cursor/mcp.json nel progetto. Aggiungi questo JSON, sostituendo entrambi i percorsi assoluti: {"mcpServers":{"orders":{"type":"stdio","command":"/ABSOLUTE/PATH/orders-mcp/.venv/bin/python","args":["/ABSOLUTE/PATH/orders-mcp/orders.py"]}}}.
Apri Customize, abilita il server e fai la stessa domanda in Agent. Esamina la chiamata secondo le tue impostazioni di approvazione. La posizione del file, i campi di avvio e i controlli seguono la configurazione MCP di Cursor. Se la connessione non riesce, verifica il percorso dell'eseguibile e l'output stderr del server prima di modificare lo strumento.
Quando scegliere stdio locale o HTTP remoto
Resta in locale per un flusso di lavoro personale. Scegli HTTP remoto quando più persone o client ospitati hanno bisogno di un unico servizio gestito.
Anche un servizio remoto deve poter raggiungere i dati aziendali via rete. Pubblicare un endpoint non rende raggiungibile un database privato e non ne corregge i permessi.
Il protocollo HTTP 2026-07-28 usa richieste autosufficienti. L'SDK Python attuale può servire anche client più vecchi, le cui sessioni potrebbero richiedere un instradamento persistente verso la stessa replica quando ne aggiungi altre. Prima di scalare, configura consapevolmente le impostazioni documentate per i client legacy; non dare per scontato che ogni client connesso usi la revisione più recente.

Esporre la consultazione via HTTP con autenticazione
Proteggi l'accesso HTTP con token emessi per questo servizio. OAuth 2.1 è il framework di autorizzazione previsto dalla specifica MCP: un provider di identità autentica l'utente ed emette un token, mentre il server MCP lo verifica. Uno scope è un permesso identificato da un nome, per esempio orders:read. L'audience indica quale servizio può accettare il token.
L'SDK fornisce l'integrazione per il resource server, non il sistema di login aziendale. Per questo esempio, configura un provider di identità con discovery OAuth, registrazione dei client scelti, PKCE per il login degli utenti ed endpoint di introspezione dei token. PKCE dimostra che l'applicazione che completa l'accesso è quella che lo ha avviato. L'introspezione chiede all'emittente se un token è attivo e che cosa autorizza.
Questo adattatore si aspetta un endpoint di introspezione HTTPS con autenticazione del client tramite HTTP Basic e una risposta che contenga active, aud, exp, client_id e scope. Configura l'emittente perché includa in aud l'URL pubblico esatto di questo endpoint e conceda orders:read. Se il provider usa un metodo diverso per autenticare le richieste di introspezione, adatta la richiesta seguendo la sua documentazione. Se invece fornisce JWT, cioè token firmati, implementa la verifica di firma, emittente, scadenza e audience nella stessa interfaccia TokenVerifier.
Aggiungi uvicorn con uv add uvicorn, poi crea remote.py accanto a orders.py. Il codice segue l'esempio ufficiale di introspezione dell'SDK e le interfacce HTTP e di autenticazione documentate. Riutilizza la consultazione che hai appena verificato.
import os
import time
from urllib.parse import urlsplit
import httpx2
from pydantic import AnyHttpUrl
from mcp.server import MCPServer
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
from mcp.server.transport_security import TransportSecuritySettings
from orders import lookup_order as local_lookup
RESOURCE = os.environ["MCP_RESOURCE_URL"]
ISSUER = os.environ["MCP_ISSUER_URL"]
INTROSPECT = os.environ["MCP_INTROSPECTION_URL"]
if any(urlsplit(url).scheme != "https" for url in (RESOURCE, ISSUER, INTROSPECT)):
raise ValueError("Public auth and resource URLs must use HTTPS")
class OrderTokenVerifier(TokenVerifier):
async def verify_token(self, token: str) -> AccessToken | None:
try:
async with httpx2.AsyncClient(timeout=5.0) as client:
response = await client.post(
INTROSPECT,
data={"token": token},
auth=(os.environ["MCP_INTROSPECTION_CLIENT_ID"],
os.environ["MCP_INTROSPECTION_CLIENT_SECRET"]),
)
response.raise_for_status()
data = response.json()
audiences = data.get("aud", [])
if isinstance(audiences, str):
audiences = [audiences]
expiry = data.get("exp")
if (data.get("active") is not True or RESOURCE not in audiences
or not isinstance(expiry, int) or expiry <= time.time()):
return None
if data.get("iss", ISSUER) != ISSUER:
return None
return AccessToken(
token=token, client_id=data["client_id"],
scopes=data.get("scope", "").split(), expires_at=expiry,
resource=RESOURCE, subject=data.get("sub"),
)
except Exception:
return None
mcp = MCPServer(
"orders",
token_verifier=OrderTokenVerifier(),
auth=AuthSettings(
issuer_url=AnyHttpUrl(ISSUER),
resource_server_url=AnyHttpUrl(RESOURCE),
required_scopes=["orders:read"], validate_token_resource=True,
),
)
@mcp.tool()
def lookup_order(order_id: str) -> str:
"""Look up a fictional order by ID, such as A100. Read-only."""
return local_lookup(order_id)
hostname = urlsplit(RESOURCE).hostname
security = TransportSecuritySettings(
allowed_hosts=[hostname, f"{hostname}:*"],
allowed_origins=[os.environ["MCP_ALLOWED_ORIGIN"]],
)
app = mcp.streamable_http_app(transport_security=security)Imposta questi valori nell'ambiente di deployment o nell'archivio dei segreti:
L'elenco degli host consentiti è esplicito perché, altrimenti, l'SDK accetta localhost per impostazione predefinita e rifiuta un hostname pubblico con 421 Misdirected Request. Le origini del browser sono un controllo distinto: elenca soltanto quelle che usi davvero. L'applicazione restituita include già il ciclo di avvio e arresto. Questi dettagli seguono la documentazione sul deployment dell'SDK e sull'applicazione ASGI. ASGI è l'interfaccia con cui i server web Python eseguono questa applicazione.
Avviala dietro il proxy HTTPS del tuo hosting con uv run uvicorn remote:app --host 0.0.0.0 --port 8000. L'URL pubblico della risorsa resta HTTPS anche se il proxy comunica in HTTP con il processo. Configura la fiducia negli header inoltrati in base al confine effettivo del proxy di quell'hosting.
Prima di usare record reali, esegui queste verifiche sull'accesso HTTP:
- Token assente, scaduto o destinato a un'altra audience: l'accesso viene negato.
- Token valido senza
orders:read: l'accesso viene negato. - Token valido con audience e scope corretti:
lookup_orderrestituisce lo stato dimostrativo. /.well-known/oauth-protected-resource/mcp: i metadati identificano la risorsa e l'emittente corretti.
Usa npx @modelcontextprotocol/inspector --server-url https://orders.example.com/mcp --transport http per esaminare l'endpoint pubblicato e completarne il flusso di autenticazione. Un test dello strumento in memoria salta l'autorizzazione HTTP, quindi non può dimostrare che questo controllo di accesso funzioni.
Per Claude Code, aggiungi una connessione separata con claude mcp add --transport http orders-remote https://orders.example.com/mcp, poi autenticati tramite /mcp. Per Cursor, aggiungi sotto mcpServers una voce remota con "url":"https://orders.example.com/mcp" e completa il flusso OAuth. Cursor documenta anche un oggetto auth con CLIENT_ID e scopes per i client preregistrati. Registra presso l'emittente i callback corretti dei client. Consulta l'autenticazione di Claude Code e la configurazione OAuth remota di Cursor.
Questo è un piccolo adattatore autenticato, non l'intero sistema di produzione. Prima di sostituire il dizionario dimostrativo, applica i permessi relativi a tenant e record usando l'identità verificata, registra un evento di audit per la chiamata, riutilizza le connessioni HTTP e limita le richieste. Uno scope autorizza l'operazione; non dimostra che l'utente possa accedere a qualsiasi ordine.
Pubblicare il server su Render o Cloudflare Workers
Per il server Python qui sopra, partirei da Render. Un servizio web Python ti permette di mantenere l'applicazione già costruita. Cloudflare Workers è una buona opzione se vuoi implementare lo stesso strumento circoscritto nel suo handler Worker documentato.
Questi sono i prezzi pubblicati dai fornitori, verificati il 7 ottobre 2026:
Fonti: prezzi di Cloudflare Workers e prezzi di Render. Se scegli le funzionalità per il team, il workspace Pro di Render aggiunge $25/mese più le risorse di calcolo. Archiviazione, servizi di identità, utilizzo dei modelli e altri extra vanno preventivati a parte: questi sono prezzi di hosting, non il costo di un flusso di lavoro con l'IA.
Deployment su Render: inserisci orders.py, remote.py e requirements.txt nel repository. Il file dei requisiti deve contenere mcp[cli]==2.3.0 e uvicorn, ciascuno su una riga separata. Crea un Python Web Service, usa pip install -r requirements.txt come comando di build e uvicorn remote:app --host 0.0.0.0 --port $PORT come comando di avvio. Aggiungi le variabili d'ambiente indicate sopra, poi usa l'hostname assegnato o il tuo dominio personalizzato in MCP_RESOURCE_URL. Questa procedura adatta il deployment documentato da Render per i servizi web Python all'applicazione ASGI dell'SDK.
Il servizio gratuito di Render va bene per una demo, ma entra in sospensione dopo 15 minuti di inattività e impiega circa un minuto a riattivarsi. Per uno strumento interattivo condiviso userei risorse di calcolo a pagamento.
Deployment su Cloudflare: segui la documentazione attuale dell'handler MCP e la guida ai server remoti. L'implementazione TypeScript attuale usa createMcpHandler da agents/mcp/server con @modelcontextprotocol/server. Implementa lì la stessa consultazione degli ordini e configura l'autenticazione prima di condividere l'URL. Il comando di avvio Python con uvicorn è destinato a un hosting Python; non è una procedura di deployment per un Worker.
Limitare il perimetro di accesso
Concedi al server soltanto l'accesso necessario allo strumento. Per lo stato degli ordini significa usare credenziali del backend in sola lettura, selezionare i campi e verificare i permessi per ogni record. Tieni rimborsi, annullamenti e modifiche degli indirizzi dietro strumenti e permessi separati. Gli argomenti scelti dal modello non costituiscono mai un'autorizzazione.
Verifica che i token HTTP rispettino emittente, scadenza, audience e scope previsti. Usa HTTPS e credenziali separate quando il server chiama un'API a valle. Le indicazioni di sicurezza MCP vietano il token passthrough: un token presentato al tuo endpoint MCP non è automaticamente una credenziale per il sistema degli ordini. Per stdio locale, limita il processo che avvia il server, il suo ambiente e l'accesso al filesystem.
Registra nei log il chiamante verificato, il nome dello strumento, un riferimento al record con i dati sensibili opportunamente oscurati, l'esito, la latenza e l'ID della richiesta. Evita token e record completi dei clienti. Per stdio, scrivi i log su stderr; per HTTP, usa il sistema di log dell'hosting. Tratta il testo recuperato dai record come dati: una nota dentro un ordine non deve concedere il permesso di compiere un'altra azione.
Inserisci un gateway quando più server o team hanno bisogno di criteri comuni per l'identità, limiti di frequenza, raccolta degli audit o revoca degli accessi. Un gateway può centralizzare questi controlli; ogni backend deve comunque applicare correttamente i permessi sui record. La nostra guida ai gateway MCP spiega come valutare questa scelta.

Sei flussi di lavoro, in ordine di utilità immediata
Sono possibili estensioni dello stesso schema. Parti dai casi in cui una persona recupera spesso un'informazione precisa e sa riconoscere una risposta corretta.
I conti devono partire dal tuo flusso di lavoro. Esempio puramente illustrativo: 80 consultazioni al giorno da 2 minuti ciascuna richiedono 160 minuti. Se le misurazioni mostrano poi che il flusso integrato fa risparmiare 1 minuto per consultazione, recuperi 80 minuti al giorno. È aritmetica, non un benchmark di prestazioni. Misura la correttezza delle risposte e il tempo risparmiato prima di dichiarare un ritorno sulla spesa di hosting.
Due idee di prodotto da sviluppare
L'opportunità più promettente è un adattatore che porta il contesto degli ordini ai team di assistenza. Un team potrebbe acquistare un'integrazione mirata che recupera le informazioni corrette sulla spedizione nell'assistente già in uso. DataForSEO stima 260 ricerche mensili su Google negli Stati Uniti per “customer support automation”, dato verificato il 7 ottobre 2026. È un segnale di interesse generale per il compito, non un conteggio di acquirenti MCP. Intercom pubblica per Fin un prezzo di $0.99 per risultato, che mostra l'esistenza di un budget per l'automazione dell'assistenza; questo piccolo adattatore fornisce contesto, senza sostituire quel prodotto.
La versione minima vendibile potrebbe includere un backend per gli ordini, lookup_order, login con scope, una traccia di audit e una bozza di risposta che cita i campi restituiti. Il vantaggio sarebbe adattarsi ai dati e alle regole di accesso di un'azienda specifica. Il limite è che i fornitori esistenti potrebbero avere già il connettore, mentre le licenze dei modelli per il team, la pulizia dei dati e l'assistenza restano costi da sostenere. Verifica questa lacuna con un responsabile dell'assistenza prima di aggiungere altri strumenti.
La seconda opportunità è una consultazione delle policy che rispetti i permessi. Un operatore potrebbe dare accesso a una raccolta di policy aziendali approvate tramite risorse o uno strumento di ricerca circoscritto. DataForSEO stima 390 ricerche mensili negli Stati Uniti per “enterprise search”, dato verificato nella stessa data. L'MVP potrebbe comprendere una raccolta, citazioni delle fonti, controlli sull'aggiornamento dei contenuti e filtri basati sui permessi della persona autenticata. Il punto critico è che il prodotto consiste nella qualità del recupero delle informazioni e nel controllo degli accessi: rendere una cartella accessibile tramite MCP è facile da replicare. Il volume di ricerca segnala una domanda per il compito più ampio, non dimostra che gli utenti pagheranno per questa implementazione.
Quali problemi restano da risolvere?
MCP standardizza l'accesso. Restano sotto la tua responsabilità la qualità dei dati, l'autorizzazione, l'affidabilità del backend e la scelta delle operazioni consentite a uno strumento. Un modello può interpretare male un risultato valido e i client connessi possono differire per funzionalità supportate o criteri di approvazione.
Costruisci questo server quando un'interfaccia condivisa per gli strumenti migliora un flusso di lavoro misurabile. Per un job batch fisso, in cui non serve mai che un assistente scelga uno strumento, una normale chiamata API o uno script potrebbe essere la soluzione migliore.
Da fare lunedì: scegli una consultazione ricorrente insieme a un operatore dell'assistenza, usa record fittizi per collegare entrambi i client, poi sostituisci i dati di esempio accedendo al backend con un account in sola lettura e verifica i permessi sui record. Escludi le operazioni di scrittura da questo primo rilascio. Passa a HTTP condiviso quando il flusso di lavoro e i controlli sull'identità sono pronti.
È difficile creare un server MCP?
Un piccolo server in sola lettura è semplice da realizzare con l'SDK ufficiale: definisci la funzione, descrivi l'input e scegli il trasporto. Rendere disponibili i dati aziendali in sicurezza richiede più lavoro, perché devi applicare i permessi, gestire le credenziali e mantenere il servizio operativo.
Posso usare un server MCP gratuito per fare dei test?
Il server con ordini fittizi di questa guida può funzionare in locale senza costi di hosting. MCP Inspector permette di chiamarlo senza un abbonamento a un modello. L'hosting cloud e il client di IA che sceglierai in seguito hanno prezzi propri.
Quanto costa un server MCP?
Un processo locale non richiede un piano di hosting separato. Cloudflare Workers pubblica un piano gratuito e un minimo di $5/mese per quello a pagamento. Render pubblica un costo di $7/mese per le risorse di calcolo del suo piccolo servizio web a pagamento. Questi importi escludono utilizzo dei modelli, servizi di identità, archiviazione e lavoro di sviluppo.
Devo installare un server MCP?
Con stdio, il server gira sulla macchina del client: codice e runtime devono quindi essere disponibili lì. Con HTTP remoto, configuri un endpoint e ti autentichi; il server gira sull'hosting. Usa la modalità di deployment supportata dal client scelto.
Se vuoi un servizio MCP sui dati aziendali sviluppato e gestito per il tuo team, il nostro servizio per i sistemi di IA in produzione comprende l'integrazione e i relativi controlli di accesso.
- Pubblicato
- Categoria
- Build
- Lingua







