Renfe MCP Server
Enables querying Renfe train schedules, checking prices, and finding stations across Spain using official GTFS data.
README
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
- [x]
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 LICENSE 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! 🚄
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。