API REST + server MCP per la ricerca treni, disponibilità e prezzi su Trenitalia / lefrecce.it. Solo lettura, no prenotazioni.
API-MCP Trenitalia
API REST + server MCP per la ricerca treni Trenitalia / lefrecce.it
Servizio REST e server MCP (Model Context Protocol) per la ricerca di treni, disponibilità e prezzi sulla rete ferroviaria italiana. Fa da proxy verso il backend pubblico di lefrecce.it, esponendo endpoint puliti via FastAPI per applicazioni web/mobile e tool conversazionali via MCP per agenti AI (Claude, ecc.).
[!IMPORTANT] Scope: solo lettura. Questo servizio è esclusivamente per ricerca, disponibilità e prezzi. Non effettua prenotazioni, non acquista biglietti, non gestisce carrelli, pagamenti, profili utente o e-ticket. Per acquistare un biglietto l'utente deve andare su lefrecce.it o sull'app Trenitalia.
[!CAUTION] Disclaimer legale — Questo progetto è uno strumento indipendente e non è affiliato, approvato o supportato da Trenitalia S.p.A., Ferrovie dello Stato Italiane, Italo o lefrecce.it. L'utente è l'unico responsabile del rispetto dei termini di servizio del portale lefrecce.it e della normativa vigente. L'uso programmatico del backend potrebbe violare i termini d'uso del servizio. Usalo a tuo rischio e per scopi leciti.
[!NOTE] Non è richiesta alcuna registrazione né API key: lefrecce.it espone un BFF (Backend-For-Frontend) pubblico che questo servizio interroga via HTTP. Tutta la logica (paginazione, filtraggio, sanitizzazione) avviene lato server.
Indice
- Panoramica
- Architettura
- Prerequisiti
- Avvio rapido
- Configurazione
- Endpoint REST
- Server MCP
- Esempi d'uso
- Deploy su Railway
- Dettagli tecnici
- Risoluzione dei problemi
- Sviluppo
- Ringraziamenti
- Licenza
Panoramica
L'API espone tre operazioni fondamentali — tutte di sola lettura — accessibili sia via REST che via MCP:
| Operazione | REST | Tool MCP | Descrizione |
|------------|------|----------|-------------|
| Ricerca stazione | GET /stations?q=... | search_stations | Autocompletamento sulle stazioni italiane |
| Soluzioni puntuali | POST /solutions | search_trenitalia_solutions | Una pagina di soluzioni da un orario preciso, con prezzi |
| Soluzioni del giorno | POST /day-solutions | search_trenitalia_day_solutions | Tutte le soluzioni del giorno dall'ora indicata, con prezzi |
Tutte le ricerche delegano al BFF di lefrecce.it (Channels.Website.BFF.WEB/website). Le risposte vengono normalizzate e ripulite prima di essere restituite al client.
Cosa NON fa questo servizio
Per chiarezza, fuori scope:
- ❌ Prenotazione o acquisto di biglietti
- ❌ Gestione carrelli, sessioni utente, login Trenitalia/lefrecce
- ❌ Pagamenti, integrazione Stripe/PayPal, gestione fatture
- ❌ Emissione, download o invio di e-ticket / PDF
- ❌ Gestione posto a sedere, classe, supplementi, tariffe loyalty (CartaFRECCIA)
- ❌ Modifica/rimborso di biglietti già acquistati
- ❌ Real-time status del treno (ritardi, binario di partenza in stazione)
Per tutto quanto sopra, l'utente finale deve usare i canali ufficiali Trenitalia.
Funzionalità principali
- Copertura totale della rete italiana — nessuna whitelist: le stazioni vengono cercate live tramite l'autocompletamento di lefrecce. Funziona per Roma, Milano, Palermo, Bolzano, qualsiasi fermata regionale.
- REST + MCP nello stesso codice base — il modulo
trenitalia.pyè condiviso: una sola implementazione, due interfacce. - Risoluzione CORS — il backend chiama lefrecce per conto del client browser, eliminando i problemi di Same-Origin tipici delle chiamate dirette da frontend.
- Paginazione automatica —
day-solutionsitera le pagine del BFF, deduplica per ID, taglia automaticamente quando si passa al giorno successivo. - Filtri configurabili — solo regionali, solo Frecce, senza cambi, ordinamento per partenza/arrivo/durata/prezzo.
- Risposte MCP "pulite" — i tool per agenti AI restituiscono solo treni
SALEABLE(acquistabili) e nascondono i campi rumorosi del BFF. - Connessione HTTP riutilizzata —
httpx.AsyncClientglobale gestito via FastAPI lifespan: una sola connessione TCP per più richieste. - Errori upstream propagati — i 4xx/5xx di lefrecce vengono trasformati in
HTTPExceptioncon status e body utile invece di 500 opachi.
Limitazioni note
- Nessun endpoint "lista completa stazioni": lefrecce non lo espone, quindi le ricerche sono per nome (minimo 2 caratteri). Cerca per città o per il nome esatto della fermata.
- Nessun endpoint "stazione per ID": una stazione si recupera solo via ricerca testuale. Se conosci già l'ID, lo passi direttamente a
/solutions. - Limiti del BFF: lefrecce restituisce massimo 10 risultati per pagina e tipicamente massimo ~24 pagine per intervallo di giornata.
day-solutionsrispetta questi limiti (page_size ≤ 10, max_pages ≤ 24). - Risultati
NOT_SALEABLE: la REST li include (con campostatus); il MCP li filtra in modo che l'agente non li proponga all'utente. - Nessuna cache: ogni richiesta colpisce lefrecce. Per traffico alto valuta di mettere un layer di cache davanti (Redis, edge cache).
Architettura
┌──────────────────┐ ┌──────────────────┐
│ Browser / App │ │ Agente AI │
│ (HTTP / JSON) │ │ (Claude, ecc.) │
└────────┬─────────┘ └────────┬─────────┘
│ REST │ MCP (stdio | SSE | streamable-http)
▼ ▼
┌────────────────────┐ ┌──────────────────────────┐
│ FastAPI │ │ FastMCP │
│ app/main.py │ │ app/mcp_server.py │
│ • /stations │ │ • search_stations │
│ • /solutions │ │ • search_..._solutions │
│ • /day-solutions │ │ • search_..._day_.. │
└────────┬───────────┘ └────────────┬─────────────┘
│ │
└────────────┬───────────────┘
▼
┌────────────────────────────┐
│ app/trenitalia.py │
│ • Station / Search... │
│ / SolutionRequest │
│ • get_client() (httpx) │
│ • search_locations() │
│ • search_solutions() │
│ • search_day_solutions() │
└────────────┬───────────────┘
│ httpx (async, keep-alive)
▼
┌────────────────────────────┐
│ lefrecce.it BFF │
│ /locations/search │
│ /ticket/solutions │
└────────────────────────────┘
File del progetto
| File | Descrizione |
|------|-------------|
| app/main.py | App FastAPI: endpoint REST, modelli Pydantic di input, CORS, lifespan |
| app/trenitalia.py | Client lefrecce.it: modelli (Station, SearchCriteria, SolutionRequest), httpx.AsyncClient globale, funzioni di ricerca, paginazione day-solutions |
| app/mcp_server.py | Server MCP (FastMCP) con tool search_stations, search_trenitalia_solutions, search_trenitalia_day_solutions |
| app/__init__.py | Marker pacchetto |
| requirements.txt | Dipendenze Python pinnate |
| railway.json | Comando di start per il servizio REST su Railway |
| railway.mcp.json | Esempio di config Railway per il servizio MCP |
Prerequisiti
- Python 3.11+ (uso di
from __future__ import annotations,ZoneInfo,Literal, generics nativi) - Connessione di rete verso
https://www.lefrecce.it - Nessuna API key, nessuna registrazione
Per il deploy:
- Account Railway (o qualsiasi PaaS che supporti Python + variabili
$PORT)
Avvio rapido
Installazione locale
git clone <questo-repo>
cd api-mcp-trenitalia
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
Servizio attivo su:
http://127.0.0.1:8000/docs— Swagger UIhttp://127.0.0.1:8000/health— health check
Server MCP locale (stdio)
.venv/bin/python -m app.mcp_server
Configurazione di esempio per Claude Desktop o altri client MCP:
{
"mcpServers": {
"api-mcp-trenitalia": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "app.mcp_server"],
"cwd": "/path/to/api-mcp-trenitalia"
}
}
}
Configurazione
Nessuna configurazione obbligatoria per la modalità REST. Per il server MCP sono disponibili variabili d'ambiente opzionali:
| Variabile | Default | Descrizione |
|-----------|---------|-------------|
| MCP_TRANSPORT | stdio | Trasporto MCP: stdio, sse o streamable-http |
| MCP_HOST | 127.0.0.1 | Host di bind per trasporti HTTP |
| PORT | 8000 | Porta di bind (Railway la inietta automaticamente) |
| MCP_PORT | 8000 | Alias di PORT se quest'ultima non è impostata |
Endpoint REST
Health check
GET /health
{ "status": "ok" }
Ricerca stazioni
GET /stations?q=<nome>&limit=<n>
Autocompletamento sulle stazioni italiane. Proxy verso lefrecce.it/locations/search.
Query string:
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|-------|------|:------------:|---------|-------------|
| q | string | ✅ | — | Nome (anche parziale) della stazione, minimo 2 caratteri |
| limit | int | | 10 | Numero massimo di risultati (1–50) |
Esempio:
curl 'http://127.0.0.1:8000/stations?q=milano&limit=3'
Risposta:
[
{
"id": 830001650,
"name": "Milano ( Tutte Le Stazioni )",
"displayName": "Milano ( Tutte Le Stazioni )",
"timezone": "Europe/Rome",
"multistation": true,
"centroidId": 830001650
},
{
"id": 830001700,
"name": "Milano Centrale",
"displayName": "Milano Centrale",
"timezone": "Europe/Rome",
"multistation": false,
"centroidId": 830001650
},
{
"id": 830001645,
"name": "Milano Porta Garibaldi",
"displayName": "Milano Porta Garibaldi",
"timezone": "Europe/Rome",
"multistation": false,
"centroidId": 830001650
}
]
Le stazioni
multistation=truesono "metastazioni" che includono tutti gli scali di una città. Sono valide comefrom/toin/solutions.
Soluzioni puntuali
POST /solutions
Restituisce una pagina di soluzioni di viaggio a partire da un orario preciso.
Request body:
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|-------|------|:------------:|---------|-------------|
| fromStationId | int | ✅ | — | ID stazione di partenza (da /stations) |
| toStationId | int | ✅ | — | ID stazione di arrivo |
| departureTime | string (ISO 8601) | ✅ | — | Orario di partenza con timezone, es. 2026-05-20T08:30:00+02:00 |
| adults | int | | 1 | Numero adulti (≥1) |
| children | int | | 0 | Numero bambini (≥0) |
| criteria.frecceOnly | bool | | false | Solo Frecce |
| criteria.regionalOnly | bool | | false | Solo regionali |
| criteria.noChanges | bool | | false | Solo viaggi diretti |
| criteria.order | string | | DEPARTURE_DATE | DEPARTURE_DATE | ARRIVAL_DATE | FASTEST | CHEAPEST |
| criteria.limit | int | | 10 | Risultati per pagina (1–50, ma il BFF tipicamente ne dà max 10) |
| criteria.offset | int | | 0 | Offset per paginazione manuale |
| bestFare | bool | | false | Attiva la ricerca miglior tariffa |
| includeRaw | bool | | false | Include il payload originale del BFF nella risposta |
Esempio:
curl -X POST 'http://127.0.0.1:8000/solutions' \
-H 'Content-Type: application/json' \
-d '{
"fromStationId": 830001700,
"toStationId": 830008409,
"departureTime": "2026-05-20T08:30:00+02:00",
"adults": 1,
"criteria": { "frecceOnly": true, "order": "FASTEST", "limit": 5 }
}'
Risposta (estratto):
{
"searchId": "abc123...",
"cartId": null,
"count": 5,
"solutions": [
{
"id": "...",
"origin": "Milano Centrale",
"destination": "Roma Termini",
"departureTime": "2026-05-20T08:50:00",
"arrivalTime": "2026-05-20T11:55:00",
"duration": "3h 5m",
"status": "SALEABLE",
"price": 89.90,
"currency": "EUR",
"trains": [
{ "category": "FRECCIAROSSA", "acronym": "FR", "name": "9520" }
]
}
]
}
Soluzioni del giorno
POST /day-solutions
Recupera tutte le soluzioni del giorno a partire da un orario, paginando in automatico fino a esaurire la giornata.
Request body:
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|-------|------|:------------:|---------|-------------|
| fromStationId | int | ✅ | — | ID stazione di partenza |
| toStationId | int | ✅ | — | ID stazione di arrivo |
| date | string | ✅ | — | Data YYYY-MM-DD |
| startTime | string | | 00:00 | Ora di partenza HH:MM (Europe/Rome) |
| adults | int | | 1 | Numero adulti |
| children | int | | 0 | Numero bambini |
| regionalOnly | bool | | false | Solo regionali |
| frecceOnly | bool | | false | Solo Frecce |
| noChanges | bool | | false | Solo viaggi diretti |
| order | string | | DEPARTURE_DATE | Ordinamento |
| pageSize | int | | 10 | Risultati per pagina (1–10) |
| maxPages | int | | 12 | Limite di pagine da scaricare (1–24) |
Esempio:
curl -X POST 'http://127.0.0.1:8000/day-solutions' \
-H 'Content-Type: application/json' \
-d '{
"fromStationId": 830001700,
"toStationId": 830008409,
"date": "2026-05-20",
"startTime": "18:00"
}'
Risposta (estratto):
{
"from": "2026-05-20T18:00:00+02:00",
"date": "2026-05-20",
"pageSize": 10,
"pagesFetched": 3,
"count": 18,
"solutions": [ ]
}
pagesFetched indica quante pagine sono state effettivamente richieste al BFF (si ferma prima di maxPages se si esaurisce la giornata o se una pagina è vuota).
Server MCP
Il server MCP è implementato con FastMCP e riusa lo stesso modulo trenitalia.py degli endpoint REST.
Tool disponibili
| Tool | Descrizione |
|------|-------------|
| search_stations(query, limit) | Autocompletamento stazioni — equivalente di GET /stations |
| search_trenitalia_solutions(from_station_id, to_station_id, departure_time, ...) | Soluzioni puntuali — equivalente di POST /solutions ma filtra solo SALEABLE |
| search_trenitalia_day_solutions(from_station_id, to_station_id, date, start_time, ...) | Soluzioni del giorno — equivalente di POST /day-solutions ma filtra solo SALEABLE |
Comportamento agente raccomandato
L'instructions del server impone esplicitamente che l'agente raccolga quattro dati prima di chiamare una ricerca di treni:
- Stazione di partenza
- Stazione di arrivo
- Data di viaggio
- Ora di partenza (da quando cercare)
Se uno di questi manca, l'agente deve chiederlo all'utente prima di invocare il tool.
Risposte MCP pulite
A differenza degli endpoint REST, i tool MCP:
- Rimuovono i campi rumorosi del BFF (
searchId,cartId,origin/destinationridondanti, raw JSON). - Filtrano fuori i treni con
status != "SALEABLE", in modo che l'agente non proponga all'utente treni non acquistabili. - Restituiscono i treni come stringhe compatte (
"FR 9520") invece di oggetti annidati. - Includono un campo
messagein italiano se la lista risultante è vuota.
Trasporti supportati
| Trasporto | Quando usarlo |
|-----------|---------------|
| stdio (default) | Client locali (Claude Desktop, IDE plugin) che lanciano il processo on-demand |
| streamable-http | Server MCP remoto su Railway/altri PaaS, endpoint /mcp |
| sse | Compatibilità con vecchi client basati su Server-Sent Events |
Imposta MCP_TRANSPORT=streamable-http per la modalità remota.
Esempi d'uso
Flusso completo con cURL
# 1. Trova la stazione di partenza
curl -s 'http://127.0.0.1:8000/stations?q=palermo%20centrale&limit=3' | jq .
# 2. Trova la stazione di arrivo
curl -s 'http://127.0.0.1:8000/stations?q=catania&limit=3' | jq .
# 3. Cerca tutte le soluzioni del giorno dalle 14:00
curl -s -X POST 'http://127.0.0.1:8000/day-solutions' \
-H 'Content-Type: application/json' \
-d '{
"fromStationId": 830012002,
"toStationId": 830012332,
"date": "2026-05-20",
"startTime": "14:00"
}' | jq '.solutions[] | {dep:.departureTime, arr:.arrivalTime, price, trains:.trains[].acronym}'
Client Python
import httpx
from datetime import date
BASE = "http://127.0.0.1:8000"
with httpx.Client(base_url=BASE, timeout=30.0) as c:
# 1. Risolvi le stazioni per nome
origine = c.get("/stations", params={"q": "milano centrale", "limit": 1}).json()[0]
arrivo = c.get("/stations", params={"q": "roma termini", "limit": 1}).json()[0]
# 2. Cerca tutte le soluzioni del giorno
r = c.post("/day-solutions", json={
"fromStationId": origine["id"],
"toStationId": arrivo["id"],
"date": str(date.today()),
"startTime": "07:00",
"frecceOnly": True,
}).json()
print(f"Trovate {r['count']} soluzioni in {r['pagesFetched']} pagine")
for s in r["solutions"][:5]:
trains = ", ".join(f"{t['acronym']} {t['name']}" for t in s["trains"])
print(f" {s['departureTime']} → {s['arrivalTime']} €{s['price']} [{trains}]")
Deploy su Railway
Servizio REST
Il file railway.json contiene il comando di start di produzione:
{
"deploy": {
"startCommand": "uvicorn app.main:app --host 0.0.0.0 --port $PORT"
}
}
Procedura:
- Crea un nuovo progetto Railway dal repo GitHub.
- Railway rileva
requirements.txte fa il build automatico. - Vai su Settings → Networking e genera un dominio pubblico.
- Verifica:
https://<tuo-dominio>/health,/docs,/stations?q=roma.
Non committare .venv o .env (sono già in .gitignore).
Servizio MCP remoto
Per un MCP raggiungibile via HTTP da agenti remoti, crea un secondo servizio Railway dallo stesso repo e imposta il comando di start:
MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 python -m app.mcp_server
Railway fornisce $PORT automaticamente. L'endpoint MCP sarà:
https://<tuo-dominio-mcp>/mcp
Il file railway.mcp.json è una config di esempio: impostala dalla dashboard Railway oppure copiala in railway.json su un branch dedicato.
Dettagli tecnici
Client HTTP riutilizzato
Un singolo httpx.AsyncClient viene creato a livello di modulo via get_client() e chiuso nel lifespan di FastAPI:
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
await close_client()
Vantaggi: connessioni TCP keep-alive, pool di connessioni, niente handshake TLS a ogni richiesta. Sotto carico la differenza è significativa.
Paginazione day-solutions
L'algoritmo (app/trenitalia.py):
- Imposta
offset=0,limit=page_size(default 10). - Chiama
/ticket/solutionsripetutamente, incrementando l'offset. - Per ogni risultato:
- Se
departureTimeè oltre la data target → marcareached_next_daye scarta. - Se è prima dello
start_timerichiesto → scarta. - Se l'ID è già stato visto → scarta (deduplica).
- Altrimenti → aggiungi alla lista.
- Se
- Interrompi se:
reached_next_day=True, pagina vuota, pagina sottopage_size, omax_pagesraggiunto.
In pratica per una giornata tipica con treni ogni 10–30 minuti, servono 2–6 pagine.
Gestione errori upstream
_request() traduce sempre gli errori lefrecce in HTTPException:
| Condizione | Status restituito | Body |
|------------|-------------------|------|
| httpx.RequestError (DNS, TCP, TLS, timeout) | 502 | "Errore di rete verso lefrecce.it: <dettagli>" |
| Lefrecce risponde 4xx/5xx | Stesso status | "Errore da lefrecce.it: <primi 200 char del body>" |
Niente 500 Internal Server Error opachi per problemi che vengono dall'upstream.
Modelli Pydantic v2
Tutti i modelli usano populate_by_name=True (implicito tramite Field(alias=...) per accettare sia il nome italiano/camelCase del JSON sia il nome Python snake_case nelle costruzioni interne. Esempio:
SolutionRequest(departureLocationId=830001700, ...) # da JSON / API
SolutionRequest(departure_location_id=830001700, ...) # da codice Python
Trasformazione delle date
startTime di /day-solutions viene combinato con date usando zoneinfo.ZoneInfo("Europe/Rome"), in modo che "18:00" significhi sempre le 18:00 ora italiana indipendentemente dal fuso del server.
I departureTime ricevuti da lefrecce sono parsati con datetime.fromisoformat (sostituendo eventuali Z finali per compatibilità Python <3.11).
Risoluzione dei problemi
| Problema | Causa probabile | Soluzione |
|----------|-----------------|----------|
| GET /stations restituisce [] | Query troppo corta o nome non riconosciuto da lefrecce | Prova con almeno 2–3 caratteri; usa il nome esatto della città (es. roma invece di rm) |
| 502 Bad Gateway dalla nostra API | Lefrecce è down o l'IP del server è bloccato | Verifica con curl https://www.lefrecce.it dal server |
| POST /solutions restituisce count: 0 | Nessun treno disponibile per i criteri scelti | Allarga: rimuovi frecceOnly/regionalOnly, prova un orario più ampio |
| day-solutions molto lento | Stai scaricando molte pagine | Riduci maxPages, stringi la finestra alzando startTime |
| MCP non si avvia | Trasporto non valido | MCP_TRANSPORT deve essere stdio, sse o streamable-http |
| Agente AI cerca stazioni inesistenti | Allucinazioni LLM | Le instructions del server richiedono di passare per search_stations. Verifica che il client MCP carichi le instructions |
| CORS error nel browser | Stai chiamando lefrecce.it direttamente, non questo proxy | Punta il frontend a http://tuo-server:8000, non a lefrecce.it |
Per debug, esponi i log uvicorn (--log-level debug) e/o aggiungi includeRaw: true a /solutions per ispezionare la risposta originale del BFF.
Sviluppo
Setup
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Struttura del codice
app/trenitalia.py — cuore del progetto:
Station,SearchCriteria,SolutionRequest— modelli Pydanticget_client()/close_client()— lifecycle del client HTTP_request(method, path, **kwargs)— wrapper unificato con gestione errorisearch_locations(name, limit)— autocompletamento stazionisearch_solutions(request, include_raw)— ricerca singola paginasearch_day_solutions(request, page_size, max_pages, include_raw)— paginazione + filtro giornatasummarize_solution(item)— normalizza un risultato BFF nel nostro shape
app/main.py — strato HTTP:
- Lifespan FastAPI per chiudere il client httpx
StationRouteRequest,DaySolutionsRequest— input model con alias camelCase- Endpoint
/health,/stations,/solutions,/day-solutions
app/mcp_server.py — strato MCP:
- Istanza
FastMCPconinstructionsin italiano - Tre
@mcp.tool()async che delegano atrenitalia.py _clean_solution()/_clean_saleable_response()— sanitizzazione output
Convenzioni
- Italiano per i messaggi utente-facing, gli
instructionsMCP e la documentazione. - Inglese per identificatori, nomi di endpoint e nomi di funzioni (standard).
- Niente commenti banali: il codice spiega cosa fa, i commenti spiegano solo perché.
- Pydantic v2 ovunque, niente dict non tipizzati ai bordi.
Test rapido senza framework
python3 -c "
import asyncio
from app.trenitalia import search_locations, close_client
async def main():
stations = await search_locations('roma', limit=3)
for s in stations:
print(s.id, s.name)
await close_client()
asyncio.run(main())
"
Ringraziamenti
Questo progetto è partito studiando e prendendo ispirazione da due lavori precedenti della community che hanno fatto reverse-engineering del backend di lefrecce.it. Senza di loro avrei dovuto ricostruire da zero la mappatura degli endpoint e dei payload del BFF — un grazie sincero:
- TrinTragula/api-trenitalia — implementazione di riferimento di un proxy verso lefrecce.it. Mi ha aiutato a capire la struttura del payload di
POST /ticket/solutions(departureLocationId,arrivalLocationId,criteria,advancedSearchRequest.bestFare) e gli ordinamenti supportati (DEPARTURE_DATE,ARRIVAL_DATE,FASTEST,CHEAPEST). - SimoDax/Trenitalia-API — Wiki — documentazione community degli endpoint pubblici di lefrecce.it e dei vari stati di una soluzione (
SALEABLE,NOT_SALEABLE, ecc.). Punto di partenza essenziale per capire cosa filtrare quando si lavora con un agente AI.
Le scelte implementative di questo progetto (architettura REST+MCP condivisa, paginazione day-solutions, sanitizzazione lato MCP, gestione errori upstream) sono originali, ma il debito iniziale verso questi due lavori è importante e va riconosciuto.
Se hai contribuito a uno dei due progetti e vuoi essere citato esplicitamente nel commit history o in questa sezione, apri pure una issue.
Licenza
Distribuito sotto licenza GNU Affero General Public License v3.0 — only (SPDX-License-Identifier: AGPL-3.0-only).
⚠️ Stai forkando? Leggi prima questa sezione
AGPL-3.0 è una strong copyleft network license. La clausola §13 ("Remote Network Interaction") impone obblighi che molti sviluppatori sottovalutano. Se intendi forkare api-mcp-trenitalia e usarlo in un servizio di rete (es. un'app, una dashboard, un'API B2B, un agente AI che esponi a utenti finali), sei tenuto a:
- Mantenere AGPL-3.0 in tutte le distribuzioni del fork. Non puoi rilicenziare ad Apache, MIT, BSD, GPL-2, GPL-3 o altre licenze.
- Preservare il file
LICENSE, gli header SPDX e i credit all'autore originale in ogni copia distribuita o ridistribuita. - Pubblicare le tue modifiche al codice sotto AGPL-3.0, includendo l'intera storia git delle modifiche.
- Se esponi il fork (o un suo derivato) come servizio di rete — SaaS, API pubblica, agente MCP remoto, microservizio interno raggiungibile da utenti esterni — devi offrire a tutti gli utenti del servizio l'accesso pubblico al Corresponding Source completo dell'opera combinata, comprese:
- le tue modifiche al codice base,
- tutte le componenti private linkate o combinate col servizio (autenticazione, frontend, theme, orchestratori, layer di cache, integrazioni Stripe/Clerk/CRM, schema DB e migrations, Dockerfile, Helm chart, IaC),
- le installation information necessarie a ricostruire un deploy comparabile.
- Pubblicare un avviso visibile ("prominent offer") nell'UI o nella documentazione API del servizio, con il link al Corresponding Source.
La mancata conformità ad AGPL §13 termina automaticamente i tuoi diritti sul software (AGPL §8).
Checklist forker (rapida)
- [ ] Il
LICENSEdel mio fork è ancoraAGPL-3.0-only? - [ ] Gli header SPDX nei file sorgente sono preservati?
- [ ] Il
READMEdel mio fork attribuisce esplicitamente il progetto upstream? - [ ] Tutte le dipendenze private che linkero/combinerò sono pronte a essere pubblicate come Corresponding Source, in caso di deploy in rete?
- [ ] Ho preparato la "prominent offer" del Corresponding Source nell'UI/docs del mio servizio?
Perché AGPL e non MIT/Apache
L'obiettivo è impedire che qualcuno prenda questo proxy, lo metti dietro a un paywall come SaaS chiuso, e si tenga per sé le migliorie. Chi lo usa internamente per progetti personali, chi lo studia, chi contribuisce miglioramenti pubblici → non viene toccato dalla licenza. Chi lo trasforma in un prodotto commerciale di rete → deve restituire il codice alla community.
Se l'AGPL non si adatta al tuo caso d'uso (es. integrazione in piattaforma enterprise senza obbligo di pubblicare i moduli combinati), apri una issue per discutere una licenza commerciale separata.
Ultimo aggiornamento: maggio 2026