Server MCP¶
Spazio Comune include un server MCP (Model Context Protocol) in Python,
basato sull'SDK ufficiale mcp (FastMCP). Con questo server, assistenti e sistemi agentici come Claude
Desktop, Claude Code, gli IDE o un orchestratore del Comune consultano il luogo con gli stessi dati del sito:
orari liberi, tariffe, regole e, per l'ufficio, i numeri di prenotazione.
flowchart LR
AG([Agente IA<br/>Claude · IDE · orchestratore]) -->|"MCP<br/>stdio o HTTP"| MCP["spazio-comune-mcp<br/>mcp_server/server.py"]
MCP -->|"API REST /api/v1"| APP["spazio-comune-app"]
APP --> DB[("PostgreSQL<br/>prenotazioni · slot")]
APP --> INF["spazio-comune-inference"]
P([Persona]) -->|"apre il link<br/>SPID/CIE + pagoPA"| APP
L'agente propone, la persona decide
Nessuno strumento prenota o paga. link_prenotazione verifica lo slot e restituisce il link che la persona
apre per completare la prenotazione con SPID/CIE e pagoPA. Il server MCP passa solo dalle API REST
dell'app: non legge il database e non conosce i segreti dell'app.
Strumenti¶
| Strumento | Per | Cosa fa |
|---|---|---|
info_spazio |
tutti | luogo, Comune, ufficio, orari, attività e tariffe |
disponibilita(dal, giorni, solo_liberi) |
tutti | calendario con lo stato di ogni slot: libero, occupato, in_pagamento, riservato, passato |
cerca_orari(richiesta) |
tutti | dalla frase "calcetto venerdì sera alle 20" a un massimo di 4 orari liberi, ognuno con il suo link |
domanda(testo) |
tutti | FAQ su tariffe, pagamento, annullamento, privacy, con le fonti ufficiali |
link_prenotazione(inizio, attivita) |
tutti | controlla che lo slot sia libero e restituisce il link di prenotazione |
occupazione_slot(dal, giorni) |
ufficio | per ogni slot, il numero di prenotazione registrato, lo stato e l'intestatario |
dettaglio_prenotazione(codice) |
ufficio | dettaglio di una prenotazione dal suo numero, senza codice fiscale |
Gli strumenti dell'ufficio esistono solo se MCP_UFFICIO_USER e MCP_UFFICIO_PASSWORD sono valorizzati nel
.env, con le stesse credenziali del back-office.
Collegare un agente¶
Il container spazio-comune-mcp parte con lo stack e risponde su http://localhost:8090/mcp
(trasporto streamable HTTP, porta pubblicata solo su 127.0.0.1).
{
"mcpServers": {
"spazio-comune": {
"command": "python",
"args": ["/percorso/spazio-comune/mcp_server/server.py"],
"env": { "SPAZIO_COMUNE_URL": "http://localhost:8000" }
}
}
}
Requisiti: pip install -r mcp_server/requirements.txt.
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(params={"url": "http://localhost:8090/mcp"}, name="spazio-comune") as mcp:
agente = Agent(name="Prenotazioni", instructions="Aiuta a trovare un orario libero.", mcp_servers=[mcp])
print((await Runner.run(agente, "C'è posto per un calcetto venerdì sera?")).final_output)
npx @modelcontextprotocol/inspector # trasporto Streamable HTTP, URL http://localhost:8090/mcp
curl -i -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
Esempio¶
Richiesta di un agente a cerca_orari con "tennis doppio domani sera":
{
"intento": {"attivita": "tennis_doppio", "data": "2026-11-09", "fascia": "sera", "backend": "rules"},
"proposte": [
{"data": "2026-11-09", "ora": "18:00", "stato": "libero",
"link_prenotazione": "http://localhost:8000/prenota?inizio=2026-11-09T18%3A00%3A00%2B01%3A00&attivita=tennis_doppio"},
{"data": "2026-11-09", "ora": "19:00", "stato": "libero", "link_prenotazione": "…"}
]
}
Variabili¶
| Variabile | Predefinito | Descrizione |
|---|---|---|
SPAZIO_COMUNE_URL |
http://localhost:8000 |
Indirizzo dell'app (in Docker http://app:8000). |
MCP_TRANSPORT |
stdio |
stdio oppure http (streamable HTTP su /mcp). Il container usa http. |
MCP_HOST / MCP_PORT |
127.0.0.1 / 8090 |
Interfaccia e porta del trasporto HTTP. |
MCP_ALLOWED_HOSTS |
localhost:*,127.0.0.1:* |
Header Host ammessi; le richieste con altri host ricevono 400 (protezione da DNS rebinding). |
SPAZIO_COMUNE_UFFICIO_USER / _PASSWORD |
vuote | Credenziali dell'ufficio; nel .env si chiamano MCP_UFFICIO_USER / MCP_UFFICIO_PASSWORD. |
Sicurezza¶
- Minimo privilegio. Il server usa le API pubbliche; gli strumenti dell'ufficio sono spenti finché non si danno le credenziali, e non restituiscono mai il codice fiscale.
- Rete. La porta 8090 è pubblicata solo su
127.0.0.1. Per agenti su altre macchine, va esposta dietro reverse proxy con autenticazione e con il suo host inMCP_ALLOWED_HOSTS. - Nessun dato personale richiesto. Le istruzioni del server dicono all'agente di non chiedere né inviare codice fiscale, telefono o email.
- Test.
app/tests/test_mcp.pyprova tutti gli strumenti contro l'app reale tramite il protocollo MCP.