belgrano9/renfe_mcp_server
If you are the rightful owner of renfe_mcp_server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The Renfe MCP Server is a Model Context Protocol server designed to query Renfe train schedules using official GTFS data, integrating seamlessly with Claude Desktop and other MCP-compatible clients.
Renfe MCP Server 🚄
A Model Context Protocol (MCP) server for querying Renfe (Spanish national railway) train schedules using official GTFS data. Integrates seamlessly with Claude Desktop and other MCP-compatible clients.
✨ Features
- 🔍 Search trains between any two Spanish cities on a specific date with pagination
- 💰 Check prices - real-time price scraping from Renfe website with pagination support
- 🚉 Find stations in any city (Madrid has 7 stations!)
- 📅 Flexible date parsing - accepts ISO, European, and written date formats
- 🔄 Auto-updates - automatically downloads latest GTFS schedules from Renfe
- ⚡ Fast & accurate - uses official Renfe GTFS data with complete timetables
- 🎯 Smart filtering - handles service calendars, holidays, and exceptions
- 🛠️ Claude Desktop ready - works out of the box with MCP clients
🚀 Quick Start
Prerequisites
- Python 3.12 or higher
- uv (recommended) or pip
Installation
-
Clone the repository
git clone https://github.com/yourusername/renfe_mcp.git cd renfe_mcp -
Install dependencies
uv sync -
Run the server (GTFS data downloads automatically)
uv run python -m renfe_mcp.server
📖 Usage
Standalone Testing
Test the search functionality directly:
from renfe_mcp.schedule_searcher import ScheduleSearcher
from renfe_mcp.price_checker import check_prices
# Search trains
searcher = ScheduleSearcher("renfe_schedule")
trains = searcher.search("Madrid", "Barcelona", "2025-11-20")
# Check prices
prices = check_prices("Madrid", "Barcelona", "2025-11-20")
Claude Desktop Integration
Add to your Claude Desktop config file:
Windows (%APPDATA%\Claude\claude_desktop_config.json):
{
"mcpServers": {
"renfe": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\YourName\\path\\to\\renfe_mcp",
"run",
"python",
"-m",
"renfe_mcp.server"
]
}
}
}
macOS (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"renfe": {
"command": "uv",
"args": [
"--directory",
"/path/to/renfe_mcp",
"run",
"python",
"-m",
"renfe_mcp.server"
]
}
}
}
Linux (~/.config/Claude/claude_desktop_config.json):
{
"mcpServers": {
"renfe": {
"command": "uv",
"args": [
"--directory",
"/path/to/renfe_mcp",
"run",
"python",
"-m",
"renfe_mcp.server"
]
}
}
}
Restart Claude Desktop, and you can ask:
- "Show me trains from Madrid to Barcelona tomorrow"
- "What's the earliest train from Barcelona to Valencia on December 1st?"
- "What train stations are in Madrid?"
- "How many trains run between Madrid and Sevilla in the afternoon?"
- "Check prices for trains from Madrid to Barcelona on December 1st"
- "What are the ticket prices for the first 5 trains?"
🛠️ MCP Tools
1. search_trains
Find trains between two cities on a specific date with pagination support.
Parameters:
origin(string): Origin city name (e.g., "Madrid", "Barcelona")destination(string): Destination city name (e.g., "Valencia", "Sevilla")date(string, optional): Travel date in flexible formats:- ISO:
"2025-11-28" - European:
"28/11/2025" - Written:
"November 28, 2025" - Default: today's date
- ISO:
page(integer, optional): Page number to display (default: 1)per_page(integer, optional): Results per page (default: 10, max: 50)
Example Output:
Found 36 train(s) total
Showing page 1 of 4 (10 trains)
1. AVE
Madrid Pta.Atocha - Almudena Grandes → Barcelona-Sants
Departs: 6:16:00 | Arrives: 9:05:00
Duration: 2h 49min
2. AVE
Madrid Pta.Atocha - Almudena Grandes → Barcelona-Sants
Departs: 6:27:00 | Arrives: 9:25:00
Duration: 2h 58min
...
─────────────────────────────────
To see more trains, use page=2
Total pages: 4
2. find_station
Search for train stations in a city.
Parameters:
city_name(string): City name to search (e.g., "Madrid")
Example Output:
Found 7 stations:
All stations found:
1. Madrid-Chamartín-Clara Campoamor (ID: 17000)
2. Madrid - Atocha Cercanías (ID: 18000)
3. Madrid Pta.Atocha - Almudena Grandes (ID: 60000)
...
3. get_train_prices
Check actual ticket prices by scraping the Renfe website with pagination support.
Parameters:
origin(string): Origin city name (e.g., "Madrid", "Barcelona")destination(string): Destination city name (e.g., "Valencia", "Sevilla")date(string, optional): Travel date (same formats assearch_trains)page(integer, optional): Page number to display (default: 1)per_page(integer, optional): Results per page (default: 5, max: 20)
Example Output:
PRICE CHECK RESULTS
From: Madrid -> Barcelona
Date: 2025-11-17
Showing 5 train(s)
1. AVE
Departs: 06:16 | Arrives: 09:05
Duration: 2h 49min
Price: 94.90 EUR | [Available]
2. AVE
Departs: 06:27 | Arrives: 09:25
Duration: 2h 58min
Price: 118.60 EUR | [Available]
...
To see more prices, try page=2
Note: This tool scrapes the Renfe website and may take a few seconds to complete. Pagination now matches search_trains so you can get prices for trains on any page (e.g., page 2 shows prices for trains 6-10).
📊 Data Updates
The server includes automatic GTFS data updates from Renfe's open data portal.
Automatic Updates
On server startup, it checks for new data and downloads if needed:
uv run python -m renfe_mcp.server
# [CHECK] Checking data versions:
# Server: 2025-11-15T00:40:21
# Local: 2025-11-10T00:30:15
# [UPDATE] Server has newer data
# [DOWNLOAD] Downloading GTFS data...
# [OK] GTFS data updated successfully!
Manual Updates
Check and update if needed:
uv run python -m renfe_mcp.update_data
Force update (download regardless of version):
uv run python -m renfe_mcp.update_data --force
The update system:
- ✅ Compares server version with local version
- ✅ Only downloads when new data is available
- ✅ Stores version info in
renfe_schedule/.last_updated - ✅ Automatically extracts GTFS CSV files
🏗️ Architecture
renfe_mcp/
├── pyproject.toml # Dependencies & build config
├── README.md # This file
├── src/renfe_mcp/ # Main package
│ ├── __init__.py # Package exports
│ ├── server.py # FastMCP server implementation
│ ├── config.py # Pydantic configuration
│ ├── exceptions.py # Exception hierarchy
│ ├── logging.py # Structured logging
│ ├── security.py # Auth & rate limiting
│ ├── price_checker.py # Price checking module
│ ├── schedule_searcher.py # GTFS schedule search
│ ├── station_service.py # Unified station lookups
│ ├── update_data.py # GTFS data updater
│ └── scraper/ # Price scraper package
│ ├── __init__.py # Package exports
│ ├── scraper.py # RenfeScraper with DWR protocol
│ ├── dwr.py # DWR utilities
│ ├── models.py # Pydantic models
│ ├── exceptions.py # Scraper exceptions
│ └── stations.json # Station code database
├── tests/ # Test suite
│ ├── test_final_integration.py
│ ├── test_security.py
│ └── ...
└── renfe_schedule/ # GTFS data (auto-downloaded)
├── stops.txt # Station information
├── routes.txt # Train routes
├── trips.txt # Trip schedules
├── stop_times.txt # Arrival/departure times
├── calendar.txt # Service schedules
├── calendar_dates.txt # Holiday exceptions
└── .last_updated # Version tracking
How It Works
- City to Station Mapping: Fuzzy matches city names to station IDs
- Date Filtering:
- Checks
calendar.txtfor service schedules (day of week) - Applies exceptions from
calendar_dates.txt(holidays, special dates)
- Checks
- Route Finding:
- Joins trips, stop_times, and stops tables
- Validates stop sequences and pickup/dropoff permissions
- Ensures origin comes before destination
- Results: Returns all trains sorted chronologically with proper numeric time sorting
Key Implementation Details
- ✅ Handles multiple stations per city (e.g., Madrid has 7)
- ✅ Respects service exceptions (holidays, maintenance)
- ✅ Validates passenger boarding/alighting permissions (
pickup_type,drop_off_type) - ✅ Fixed sorting bug: Proper numeric time sorting (not lexicographic!)
- ✅ CSV column whitespace handling (strips on load)
- ✅ Complete result sets (no truncation)
🗺️ Supported Routes
The server supports any route in the Renfe network:
- High-Speed (AVE): Madrid-Barcelona, Madrid-Sevilla, Madrid-Valencia
- Long-Distance (ALVIA, Intercity): Major city connections
- International (AVE INT): Cross-border services
- Regional: Local routes across Spain
- Commuter (Cercanías): Urban networks
Popular Cities:
- Madrid (7 stations), Barcelona, Valencia
- Sevilla, Málaga, Bilbao, Zaragoza
- Alicante, Córdoba, Granada, Murcia
- 100+ cities across Spain!
Use find_station to discover available stations in any city.
🔧 Development
Project Setup
# Clone and install
git clone https://github.com/yourusername/renfe_mcp.git
cd renfe_mcp
uv sync
# Run the server
uv run python -m renfe_mcp.server
# Run tests
uv run python tests/test_final_integration.py
Dependencies
- fastmcp (>=0.7.0) - MCP server framework
- pandas (>=2.3.3) - GTFS data processing
- pydantic (>=2.11.7) - Data validation and models
- pydantic-settings (>=2.0.0) - Environment-based configuration
- httpx (>=0.27.0) - Modern HTTP client for price scraping
- python-dateutil (>=2.8.2) - Flexible date parsing
- json5 (>=0.12.0) - JavaScript object parsing for DWR responses
- python-dotenv (>=1.0.0) - Environment variables
Configuration
Configure via environment variables (prefix RENFE_) or .env file:
# Authentication
RENFE_ENABLE_AUTH=true
RENFE_API_KEY=your-secret-key
# Rate Limiting
RENFE_RATE_LIMIT_ENABLED=true
RENFE_MAX_REQUESTS_PER_MINUTE=30
RENFE_MAX_REQUESTS_PER_HOUR=200
# Development
RENFE_DEV_MODE=false
RENFE_LOG_LEVEL=INFO
📝 Data Source
GTFS data from Renfe's Open Data Portal:
- API Endpoint:
https://data.renfe.com/api/3/action/resource_show - Resource ID:
25d6b043-9e47-4f99-bd91-edd51d782450 - Update Frequency: Updated regularly by Renfe (checked on server startup)
- Format: GTFS (General Transit Feed Specification)
- Size: ~800 KB compressed, ~40 MB extracted
🐛 Known Issues
- Windows console may show encoding errors with Unicode characters (functionality not affected)
- Very large result sets (100+ trains) can be verbose but complete
🤝 Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Ideas for Contributions
-
Add price information(✅ Implemented via web scraping) - Support for train status/delays
- Multi-leg journey planning
- Visualization of routes on maps
- Additional query filters (train type, duration, etc.)
- Return trip price checking
📄 License
This project is licensed under the MIT License - see the file for details.
🙏 Acknowledgments
- Renfe Operadora for providing open GTFS data
- FastMCP by @jlowin for the excellent MCP framework
- Anthropic for Claude and the Model Context Protocol
- The GTFS community for standardizing transit data
📮 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- MCP Docs: Model Context Protocol
🔗 Related Projects
- SNCF MCP Server - Similar server for French railways
- FastMCP - The framework powering this server
- MCP Servers - Official MCP server implementations
🙏 Sources & Inspiration
- renfe-bot by @emartinez-dev - Telegram bot for Renfe train ticket monitoring. The DWR (Direct Web Remoting) protocol reverse-engineering and price scraping techniques were inspired by renfe-bot's implementation. This MCP server features a custom-built scraper using modern Python (httpx, pydantic) while preserving the core DWR protocol logic.
Built with ❤️ using FastMCP and Claude
Travel smart, travel by train! 🚄