Leporello
MCP remoto para horarios de eventos de ópera y música clásica
Documentación
Leporello
Ópera y música clásica: programación de eventos remota MCP.
También disponible como aplicación web en leporello.app.
Sedes compatibles
¿Falta tu sede favorita? ¡Se aceptan PRs! Lee CONTRIBUTING.md para instrucciones paso a paso sobre cómo añadir un nuevo scraper de sedes.
| Sede | Ciudad |
|---|---|
| Staatsoper Stuttgart | Stuttgart |
| Stuttgarter Philharmoniker | Stuttgart |
| Wiener Staatsoper | Vienna |
| Metropolitan Opera | New York |
| Oper Frankfurt | Frankfurt |
| San Francisco Opera | San Francisco |
| Gran Teatre del Liceu | Barcelona |
| Semperoper | Dresden |
| Opéra National de Paris | Paris |
| Carnegie Hall | New York |
| Teatro Real | Madrid |
| Staatsoper Unter den Linden | Berlin |
| Sydney Opera House | Sydney |
| Philharmonie de Paris | Paris |
| Bayerische Staatsoper | München |
| Berliner Philharmonie | Berlin |
| Gewandhaus | Leipzig |
| Tonhalle-Orchester Zürich | Zürich |
| Lyric Opera of Chicago | Chicago |
| Palau de la Música Catalana | Barcelona |
| Royal Opera House | London |
| Teatro alla Scala | Milano |
| Auditorio Nacional de Música | Madrid |
| Salzburger Festspiele | Salzburg |
| Elbphilharmonie | Hamburg |
| Opernhaus Zürich | Zürich |
| Concertgebouw | Amsterdam |
| Kölner Philharmonie | Köln |
| Musikverein | Wien |
| Wiener Konzerthaus | Wien |
| Barbican Hall | London |
| Hamburgische Staatsoper | Hamburg |
Cómo funciona
Los scrapers específicos de cada sede obtienen las páginas de programación (HTML o JSON) de los teatros de ópera y salas de conciertos, las analizan con Cheerio y almacenan los eventos en una base de datos SQLite local. Un programador node-cron vuelve a rastrear todas las sedes diariamente a las 03:00 UTC, reemplazando los eventos de cada sede con los datos más recientes. El servidor expone los datos a través de un endpoint Model Context Protocol (MCP), de modo que cualquier asistente de IA compatible con MCP pueda consultar las próximas actuaciones. Un frontend estático de Astro lee la misma base de datos en el momento de la compilación y sirve un listado de eventos filtrable en leporello.app.
Herramientas MCP
| Herramienta | Descripción |
|---|---|
list_countries | Todos los países con recuentos de ciudad/sede |
list_cities | Todas las ciudades con sedes, opcionalmente filtradas por país |
list_venues | Todas las sedes, opcionalmente filtradas por país o ciudad |
list_events | Próximos eventos filtrados por país, ciudad o sede |
Privacidad
La instancia alojada en leporello.app registra el uso agregado de MCP (nombre de la herramienta, argumentos, tiempo de respuesta, User-Agent y un hash salado rotativo diario de la IP del cliente) para que pueda ver qué funciona y qué no. Sin IPs sin procesar, sin datos personales, sin tokens de autenticación.
Ejecutar localmente
npm install
npm test # run scraper tests
npm run scrape # one-off scrape all venues
#or
npm run scrape -- wiener-staatsoper # scrape a single venue
npm run dev # start server on http://localhost:3000
# or
npm run dev:fresh # scrape, then serve
Rastrea diariamente a las 03:00 UTC. Ejecuta npm run scrape para poblar la base de datos en el primer uso.
Docker
Dos contenedores: web (servidor HTTP + MCP + frontend estático) y scraper (obtiene datos de las sedes en SQLite). Comparten un volumen de datos. La pila web es docker-compose.yml; el scraper es un trabajo de una sola ejecución en docker-compose.scrape.yml bajo su propio proyecto compose (leporello-scrape) para que su contenedor finalizado nunca se cuente contra la salud de la pila web. Prográmalo con un trabajo cron del host.
# Start the web server
docker compose up -d
# Run all scrapers (one-off, container stops when done)
docker compose -f docker-compose.scrape.yml run --rm scraper
# Scrape a single venue
docker compose -f docker-compose.scrape.yml run --rm scraper node dist/scrape.js wiener-staatsoper
# Rebuild the Astro frontend (e.g. after a manual scrape)
docker compose exec web node -e "
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
await promisify(execFile)('npm', ['run', 'build', '--prefix', 'web'], { cwd: '/app', timeout: 60000 });
console.log('done');
"
# Tail logs
docker compose logs -f web
El contenedor web reconstruye el frontend de Astro en cada inicio. Para ejecutar los scrapers de forma programada, añade un trabajo cron del host que rastree y luego reinicie web:
0 3 * * * cd /home/phil/docker/lep && docker compose -f docker-compose.scrape.yml build && docker compose -f docker-compose.scrape.yml run --rm scraper && docker compose restart web