trekking-mcp
Server Model Context Protocol (revisione
della spec 2026-07-28, SDK Python mcp 2.x) per l'escursionismo sulle Alpi e sugli Appennini italiani: sentieri numerati, rifugi e bivacchi, bollettini valanghe e meteo di quota, esposti a un assistente AI come tool, resource e prompt.
Avvertenza. I bollettini valanghe sono documenti ufficiali di sicurezza. Questo progetto li rilegge e li normalizza, non li interpreta e non produce valutazioni del rischio. Non sostituisce il bollettino integrale, la formazione specifica, ne' il giudizio sul terreno. Usalo per preparare una gita, mai per decidere se farla.
Cosa mostra questo repo
Non e' un wrapper 1:1 su una API. Copre i tre primitivi del protocollo e un paio di meccanismi che si vedono raramente:
| Tools | 10 tool, due dei quali compongono piu' fonti in un unico risultato |
| Resources | Documenti di riferimento statici + una resource template con URI parametrico |
| Prompts | Workflow riutilizzabili che fissano il metodo, non solo il tono |
| Elicitation | Il server chiede dati all'utente a meta' chiamata, via dependency injection, con le scelte come enum nello schema |
| Completions | Autocompletamento degli ID di zona valanghe, con il provider gia' scelto a restringere |
| Structured output | Ogni tool ha un outputSchema derivato dai modelli Pydantic |
| Dual transport | stdio e Streamable HTTP dallo stesso crea_server() |
| Osservabilita' | Latenza p50/p95, 429 e hit rate per fonte, esposti come resource |
| Cache hints | Ogni risposta dichiara per quanto vale (ttlMs/cacheScope), resource per resource |
| Coalescing | Due chiamate identiche in volo diventano una richiesta sola: un agente chiama i tool in parallelo |
| Client incluso | Un client MCP minimale, per dimostrare di conoscere entrambi i lati |
| Geometria | Point-in-polygon e profili altimetrici in Python puro, senza dipendenze binarie |
Installazione
uvx trekking-mcp
Nessuna API key richiesta: tutte le fonti di default sono aperte.
Per contribuire o sviluppare dal clone:
git clone https://github.com/19Alma98/trekking_mcp
cd trekking_mcp
uv sync # oppure: pip install -e ".[dev]"
Uso con Claude Desktop / Claude Code
{
"mcpServers": {
"trekking": {
"command": "uvx",
"args": ["trekking-mcp"]
}
}
}
In alternativa, come server remoto:
trekking-mcp --transport http --port 8000 # solo 127.0.0.1
# esposto ad altre macchine: va dichiarato chi puo' chiamare
trekking-mcp --transport http --host 0.0.0.0 \
--allow-host trekking.example.org:* \
--allow-origin https://app.example.org
# con piu' repliche: le chiavi che sigillano il requestState vanno condivise,
# altrimenti un'elicitation iniziata su una replica muore sull'altra
export TREKKING_MCP_STATE_KEYS="$(python -c 'import secrets; print(secrets.token_hex(32))')"
# produzione: non usare overpass-api.de; alza la concorrenza solo sul tuo mirror
export OVERPASS_URL="https://overpass.example.org/api/interpreter"
# export OVERPASS_CONCURRENCY=4
Il secondo comando senza --allow-host viene rifiutato: l'SDK attiva la
protezione da DNS rebinding solo quando il bind e' su localhost, cioe' proprio
quando non serve. Vedi DEVELOPMENT.md §3.18.
Su HTTP l'accesso resta aperto (nessuna API key obbligatoria): un tetto per
IP (HTTP_RATE_LIMIT_RPM / HTTP_RATE_LIMIT_BURST) limita tools/call e la
lettura dei bollettini. Dietro un reverse proxy che riscrive
X-Forwarded-For, imposta HTTP_TRUST_PROXY=1 cosi' il tetto e' per client e
non per IP del proxy. Su stdio il limite non si applica. Vedi
DEVELOPMENT.md §3.35.
Docker
docker build -t trekking-mcp .
docker run --rm -p 8000:8000 trekking-mcp
# health: curl -s localhost:8000/health
# MCP: http://localhost:8000/mcp
L'immagine ascolta su 0.0.0.0:8000 con --allow-host per localhost e
127.0.0.1. Per un dominio pubblico passa gli argomenti dopo l'immagine, ad es.
docker run --rm -p 8000:8000 trekking-mcp trekking-mcp --transport http --host 0.0.0.0 --port 8000 --allow-host 'trekking.example.org:*'.
Tool disponibili
| Tool | Cosa fa |
|---|---|
cerca_localita |
Da un toponimo alle coordinate: rifugi, cime, valichi, paesi |
cerca_sentieri |
Sentieri numerati in un raggio, filtrabili per numero, ente e difficolta' massima |
sentieri_verso_localita |
Geocoding + sentieri + rifugi in una chiamata: l'ingresso da preferire quando il punto e' un nome |
dettaglio_sentiero |
Dati completi di una relation OSM |
profilo_altimetrico |
Lunghezza reale e dislivello, campionando le quote sul tracciato |
cerca_ricoveri |
Rifugi gestiti, bivacchi e ripari entro un raggio |
zona_valanghe_da_coordinate |
Da un punto alla micro-regione EAWS del bollettino |
bollettino_valanghe |
Bollettino corrente di una zona, da CAAML v6 |
meteo_quota |
Previsione oraria corretta per l'elevazione, con zero termico e raffiche |
valuta_gita |
Compone tutto quanto sopra per un sentiero e una data |
meteo_quota non risponde con le prime ore della serie di Open-Meteo, che
comincia a mezzanotte: parte dall'ora corrente se la data e' oggi, dalle 6 se e'
un giorno futuro, e da ora_inizio se lo si indica. Una gita non si prepara
guardando la notte. Vedi DEVELOPMENT.md §3.27.
Il flusso tipico non richiede che l'utente conosca un solo codice. Quando il punto
di arrivo e' un nome, sentieri_verso_localita fa da solo geocoding, ricerca sentieri
e rifugi: e' l'ingresso che le instructions del server indicano per primo, al posto
della catena cerca_localita + cerca_sentieri. Da li', valuta_gita per il resto.
La zona del bollettino viene dedotta dalle coordinate.
Le resource sono scala://pericolo-valanghe, scala://difficolta-escursionistica,
metriche://fonti (latenza, errori e hit rate per fonte) e la template
bollettino://{provider}/{zona_id}, i cui due argomenti si autocompletano:
scelto slf, zona_id propone solo le zone svizzere.
L'elicitation, in breve
valuta_gita ha bisogno di sapere che difficolta' regge il gruppo e se ha ARTVA, pala e sonda. Sono informazioni che il modello non puo' dedurre e che non deve inventare. Il parametro e' annotato cosi':
profilo: Annotated[ProfiloUscita, Resolve(chiedi_profilo)]
Il parametro non compare nello schema di input del tool, quindi il modello non sa nemmeno che esiste. Prima di eseguire il corpo, il framework esegue il resolver, che restituisce un marker Elicit[ProfiloUscita]: la domanda viene inoltrata al client, l'utente risponde, il valore viene iniettato. Se l'utente rifiuta, la chiamata si interrompe.
Fonti dati e attribuzioni
| Fonte | Cosa fornisce | Licenza |
|---|---|---|
| OpenStreetMap via Overpass | Sentieri (route=hiking), rifugi, bivacchi |
ODbL, attribuzione obbligatoria |
| AINEVA | Bollettini valanghe dell'arco alpino italiano | Open data, CAAML v6 profilo EAWS |
| WSL-SLF | Bollettini valanghe svizzeri | CC BY 4.0 |
| Open-Meteo | Previsioni orarie e modello di elevazione | CC BY 4.0 |
| EAWS Regions | Perimetri delle zone valanghe | Open data |
| Nominatim | Geocoding dei toponimi | ODbL, usage policy |
I sentieri numerati CAI sono mappati dalla community OSM: questo progetto non accede ad alcun dato proprietario del Club Alpino Italiano, che non espone un'API pubblica. La copertura non e' uniforme e l'assenza di un sentiero non significa che non esista.
Sviluppo
uv run pytest # test
uv run ruff check . # lint
uv run ruff format --check .
uv run mypy # type check
uv run python client/ispeziona.py # client MCP minimale: elenca tool e resource
Sono gli stessi comandi che gira la CI, sulla stessa risoluzione: uv.lock e'
versionato e la CI installa con uv sync --frozen, quindi le versioni degli
strumenti sono identiche a quelle locali.
Architettura, decisioni di progetto e roadmap: DEVELOPMENT.md.
Licenza
MIT.
Release files for trekking-mcp 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| trekking_mcp-1.0.0.tar.gz | 304.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| trekking_mcp-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 372.9 kB
Release files / trekking_mcp-1.0.0.tar.gz
| Download URL | trekking_mcp-1.0.0.tar.gz |
|---|---|
| Size | 304.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e00506db699261f44870f05a0d53ea1aa9925a7a540a52878b8b90f88791331
|
|
BLAKE2b-256 checksum How to use checksums |
beb64f2090d751c7730ff11238f27e152f6a54a713ebeed812e6cbed975cee9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / trekking_mcp-1.0.0-py3-none-any.whl
| Download URL | trekking_mcp-1.0.0-py3-none-any.whl |
|---|---|
| Size | 68.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8491bb1d133f92871a7a2f7997d28d62e3f96608bb8b8852d7c6bb5dfb5f066c
|
|
BLAKE2b-256 checksum How to use checksums |
ee96875fcfae8695a3b0079f1e92206da30675b24a5e1d138ae44d3afa00f6a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|