Madrid Public Transport
MCP implementation providing real-time ETAs for Madrid's public transport: Metro, Bus, and CercanΓas.
Installation
npx mcp-madrid-public-transportAsk AI about Madrid Public Transport
Powered by Claude Β· Grounded in docs
I know everything about Madrid Public Transport. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Madrid Transport MCP Server πππ
A Model Context Protocol (MCP) server providing real-time public transportation information for Madrid, Spain.
Built with TypeScript, following Clean Architecture principles (DDD + Hexagonal Architecture) and functional programming patterns.
β¨ Features
- π Metro Madrid - Real-time arrivals via official Metro API
- π EMT Buses - Real-time arrivals via EMT OpenAPI
- π CercanΓas Trains - Real-time positions via Renfe GTFS Realtime feed
- π GTFS Integration - Static schedule data from CRTM
- β‘ Optimized Performance - SQLite caching, sub-second response times
- π Smart Station Resolution - Fuzzy matching for station names
π Quick Start
Prerequisites
- Node.js >= 20.0.0
- npm or yarn
Installation
git clone <repository-url>
cd mcp-madrid-public-transport
npm install
Note: GTFS data files are stored compressed (.txt.zip) in the repository to reduce size. The npm install script automatically decompresses them via the postinstall hook. If you need to manually decompress:
npm run setup:data
Configuration
Create a .env file in the project root:
# Required for EMT buses only
EMT_CLIENT_ID=your_client_id_here
EMT_PASS_KEY=your_pass_key_here
# Optional: Debug logging
DEBUG=false
DEBUG_LEVEL=info # error | warn | info | verbose | debug
# Optional: Data paths
GTFS_DATA_PATH=./transport-data
How to get EMT credentials (FREE):
- Visit https://openapi.emtmadrid.es/
- Click "Register" and create an account
- Log in and go to "My Account" > "My Applications"
- Create a new application
- Copy your
Client IDandPass Keyto the.envfile
Note: Metro and train data are publicly available and don't require credentials.
Build & Run
# Build TypeScript
npm run build
# Start MCP server
npm start
# Development mode with auto-reload
npm run dev
π§ Client Configuration
This MCP server can be used with any MCP-compatible client. Below are instructions for the most common clients.
Claude Desktop
Add the server to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%/Claude/claude_desktop_config.json
Configuration
Quick setup: Copy and edit the example configuration file:
# macOS
cp claude_desktop_config.example.json ~/Library/Application\ Support/Claude/claude_desktop_config.json
# Windows (PowerShell)
Copy-Item claude_desktop_config.example.json $env:APPDATA\Claude\claude_desktop_config.json
# Then edit the file to add your EMT credentials and update the path
Manual configuration:
{
"mcpServers": {
"madrid-transport": {
"command": "node",
"args": [
"/absolute/path/to/mcp-madrid-public-transport/dist/index.js"
],
"env": {
"EMT_CLIENT_ID": "your_emt_client_id_here",
"EMT_PASS_KEY": "your_emt_pass_key_here"
}
}
}
}
Important:
- Replace
/absolute/path/to/mcp-madrid-public-transportwith the actual path where you cloned this repository - Add your EMT credentials (get them free at https://openapi.emtmadrid.es/)
- Make sure you've run
npm installandnpm run buildfirst
Docker Option (Alternative)
If you prefer to use Docker, first build the image:
docker build -t mcp-madrid-transport .
Then configure Claude Desktop:
{
"mcpServers": {
"madrid-transport": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "EMT_CLIENT_ID=your_emt_client_id_here",
"-e", "EMT_PASS_KEY=your_emt_pass_key_here",
"mcp-madrid-transport"
]
}
}
}
After configuration:
- Restart Claude Desktop
- Look for the π¨ hammer icon in the bottom right
- Click to see available tools:
get_metro_arrivals,get_bus_arrivals,get_train_arrivals - Start asking questions about Madrid public transport!
Example Queries
Once configured, you can ask Claude:
- "ΒΏCuΓ‘nto tarda el metro en llegar a Colombia?"
- "ΒΏQuΓ© autobuses pasan por la parada 3000?"
- "ΒΏCuΓ‘ndo sale el prΓ³ximo tren de Atocha hacia Fuenlabrada?"
- "Show me the next 5 metro arrivals at Sol station"
- "Are there any buses arriving at Plaza de Castilla in the next 10 minutes?"
Other MCP Clients
For other MCP clients (like mcp-client-cli, custom implementations, etc.), use the stdio transport:
node dist/index.js
The server communicates via stdin/stdout using JSON-RPC 2.0 protocol.
π‘ MCP Tools
get_metro_arrivals
Get real-time Metro arrivals at a station.
Parameters:
{
station: string; // Station name or code (e.g., "Colombia", "par_4_211")
line?: string; // Optional: Line number (e.g., "8", "L8")
direction?: string; // Optional: Direction/destination
count?: number; // Number of arrivals (default: 2, max: 10)
}
Example:
{
"station": "Colombia",
"line": "8",
"count": 3
}
Response:
{
"success": true,
"station": "COLOMBIA",
"stationCode": "par_4_156",
"arrivals": [
{
"line": "8",
"destination": "Nuevos Ministerios",
"estimatedTime": "2 minutos",
"platform": "1"
}
]
}
get_bus_arrivals
Get real-time bus arrivals at a stop.
Parameters:
{
stop: string; // Stop name or number (e.g., "Plaza de Castilla", "3000")
line?: string; // Optional: Line number (e.g., "27")
direction?: string; // Optional: Direction/destination
count?: number; // Number of arrivals (default: 2)
}
Example:
{
"stop": "3000",
"line": "27",
"count": 2
}
Response:
{
"success": true,
"stop": "Plaza de Castilla",
"arrivals": [
{
"line": "27",
"destination": "Embajadores",
"estimatedTime": "5 minutos",
"distance": 1200
}
]
}
get_train_arrivals
Get real-time CercanΓas train positions and arrivals.
Parameters:
{
station: string; // Station name or code (e.g., "Atocha", "10100")
line?: string; // Optional: Line (e.g., "C-2")
direction?: string; // Optional: Destination
count?: number; // Number of arrivals (default: 2)
}
Example:
{
"station": "Atocha",
"line": "C-5",
"count": 3
}
Response:
{
"success": true,
"station": "Atocha",
"arrivals": [
{
"line": "C-5",
"destination": "Fuenlabrada",
"platform": "4",
"departureTime": "14:35",
"status": "on_time"
}
]
}
ποΈ Data Sources
Metro de Madrid
- API: Official Metro de Madrid teleindicadores API
- Endpoint:
https://serviciosapp.metromadrid.es - Authentication: None required β
- Data: Real-time arrivals, platforms, destinations
- Update frequency: ~30 seconds
EMT (Empresa Municipal de Transportes)
- API: EMT OpenAPI v2
- Endpoint:
https://openapi.emtmadrid.es - Authentication: OAuth (Client ID + Pass Key) π
- Data: Real-time arrivals, distances, incidents
- Update frequency: ~10 seconds
- Coverage: Urban buses in Madrid city
Renfe CercanΓas
- API: Renfe GTFS Realtime (Official Open Data)
- Endpoint:
https://gtfsrt.renfe.com/vehicle_positions.json - Authentication: β None required (public API)
- Data: Real-time vehicle positions, trip information, current stop
- Update frequency: ~30 seconds
- License: CC-BY-4.0 (open data)
- Source: https://data.renfe.com/dataset/ubicacion-vehiculos
- Coverage: All Spain (filter Madrid by trip IDs starting with "10")
CRTM (Static Data)
- Format: GTFS (General Transit Feed Specification)
- Data: Schedules, routes, stops, station mappings
- Update frequency: Monthly
ποΈ Architecture
The project follows Clean Architecture principles with Domain-Driven Design (DDD) and Hexagonal Architecture patterns.
src/
βββ index.ts # Application entry point & MCP server setup
β
βββ transport/ # πππ TRANSPORT DOMAIN (Bounded Context)
β βββ metro/ # Metro subdomain
β β βββ domain/ # Entities, value objects, interfaces
β β βββ application/ # Use cases (GetMetroArrivalsUseCase)
β β βββ infrastructure/ # API adapters, repositories
β β
β βββ bus/ # Bus subdomain
β β βββ domain/
β β βββ application/ # Use cases (GetBusArrivalsUseCase)
β β βββ infrastructure/ # EMT API adapter, auth
β β
β βββ train/ # Train subdomain
β β βββ domain/
β β βββ application/ # Use cases (GetTrainArrivalsUseCase)
β β βββ infrastructure/ # Renfe GTFS-RT adapter
β β
β βββ shared/ # Shared domain types
β βββ domain/ # Coordinates, TransportMode, etc.
β
βββ mcp/ # π MCP TOOLS
β βββ tools/ # Tool implementations
β β βββ get-metro-arrivals.ts
β β βββ get-bus-arrivals.ts
β β βββ get-train-arrivals.ts
β βββ formatters/ # Output formatting
β βββ validators/ # Input validation
β
βββ gtfs/ # π GTFS DATA MANAGEMENT
β βββ domain/ # GTFS entities (Stop, Route, Trip)
β βββ infrastructure/ # File loaders, SQLite repository
β
βββ cache/ # πΎ CACHING LAYER
β βββ domain/
β βββ infrastructure/ # InMemoryCache implementation
β
βββ common/ # π§ SHARED UTILITIES
βββ http/ # HTTP client, retry policies
βββ logger/ # Logging (Console, File, Combined)
βββ functional/ # Either, Option, pipe utilities
βββ config/ # Environment configuration
Key Design Patterns
- Domain-Driven Design (DDD): Clear domain boundaries for each transport type
- Hexagonal Architecture: Domain independent from infrastructure
- Functional Programming: Either monad for error handling, pure functions
- SOLID Principles: Single responsibility, dependency inversion
- Repository Pattern: Abstract data access
- Adapter Pattern: External APIs β Domain models
β‘ Performance Optimizations
Sprint 1 Optimizations (Completed β )
- SQLite Persistent DB: Loads GTFS data once on startup (~8ms queries vs 4500ms before)
- GTFS-RT Cache: Global 60-second cache for Renfe feed (0ms vs 200ms per request)
- LRU Cache: Trip destination queries cached (2ms vs 1000ms)
- Station Mapper: All 111 CercanΓas stations pre-loaded (<1ms lookup)
Result: ~1000x performance improvement (3ms end-to-end vs 3750ms before)
π οΈ Development
Running Tests
# Type check
npx tsc --noEmit
# Lint
npm run lint
# Format code
npm run format
Debug Mode
Enable verbose logging:
DEBUG=true DEBUG_LEVEL=debug npm start
Log levels: error | warn | info | verbose | debug
Project Structure
src/- TypeScript source codedist/- Compiled JavaScript (generated)transport-data/- GTFS static data files (compressed as.txt.zip)*.db- SQLite databases (generated on first run, ~246MB)
GTFS Data Management
Compression Workflow
To reduce repository size, large GTFS data files (>100KB) are stored compressed:
# Compress all large GTFS files to .txt.zip
npm run compress:data
# Decompress all .txt.zip files
npm run setup:data
Automatic Decompression
- npm install: Automatically runs
postinstallhook β decompresses GTFS files and SQLite databases - Docker build: Dockerfile runs decompression script during image build
- First run: Application uses the decompressed
gtfs-static.dbdatabase
File Sizes
- Uncompressed GTFS data: ~1.2GB
- Compressed GTFS (
.txt.zip): ~150MB (stored in Git) - Uncompressed SQLite database: ~246MB
- Compressed database (
gtfs-static.db.zip): ~51MB (stored in Git) - Total compressed in Git: ~200MB
- Total uncompressed locally: ~1.4GB
Git Configuration
.gitignoreexcludes*.txtfiles (uncompressed GTFS).gitignoreallows*.txt.zipfiles (compressed GTFS).gitignoreexcludes*.dbfiles (uncompressed SQLite databases).gitignoreallows*.db.zipfiles (compressed databases).dockerignoreproperly configured for Docker builds
π Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
EMT_CLIENT_ID | For buses | - | EMT API client ID |
EMT_PASS_KEY | For buses | - | EMT API pass key |
DEBUG | No | false | Enable debug logging |
DEBUG_LEVEL | No | info | Log level |
GTFS_DATA_PATH | No | ./transport-data | Path to GTFS data |
METRO_API_URL | No | Official URL | Override Metro API URL |
EMT_API_URL | No | Official URL | Override EMT API URL |
CACHE_TTL_METRO | No | 30 | Metro cache TTL (seconds) |
CACHE_TTL_BUS | No | 10 | Bus cache TTL (seconds) |
CACHE_TTL_TRAIN | No | 10 | Train cache TTL (seconds) |
π License
MIT License - See LICENSE file for details.
π Credits & Acknowledgments
Data Providers
- Metro de Madrid - Real-time Metro API and static data
- EMT Madrid - Real-time bus arrivals API
- Renfe - GTFS Realtime feed (Open Data CC-BY-4.0)
- CRTM (Consorcio Regional de Transportes de Madrid) - GTFS static data for all transport modes
- xBaank/MadridTransporte-Backup - GTFS data repository
Development
- Built with Claude π€ - This project was developed with significant assistance from Claude (Anthropic), an AI assistant that helped with:
- Architecture design (DDD + Hexagonal Architecture)
- TypeScript implementation and functional programming patterns
- API integration (Metro, EMT, Renfe GTFS-RT)
- Performance optimizations (1000x speedup)
- Code review and best practices
- Documentation
Technologies
- TypeScript - Type-safe JavaScript
- Node.js - Runtime environment
- MCP SDK (@modelcontextprotocol/sdk) - Model Context Protocol
- fp-ts - Functional programming utilities
- better-sqlite3 - Fast SQLite3 bindings
- csv-parse - GTFS CSV parsing
- zod - Runtime type validation
Made with β€οΈ in Madrid, for Madrid
Real-time public transport data at your fingertips
