SNCF MCP Server
# SNCF MCP Server ๐
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for querying **SNCF** (French national railway) train schedules using the official Navitia API. Integrates seamlessly with Claude Desktop and other MCP-compatible clients.
[](https://www.python.org/downloads/)
[](https://github.com/jlowin/fastmcp)
[](https://opensource.org/licenses/MIT)
## โจ Features
- ๐ **Search trains** between any two French cities with real-time data
- ๐ **Find stations** in any city across France
- ๐
**Flexible date parsing** - accepts ISO, European, and written date formats
- โก **Real-time data** - uses official SNCF Navitia API with live timetables
- ๐ฏ **Smart journey planning** - shows transfers, duration, and route details
- ๐ **International routes** - supports cross-border journeys (e.g., Paris-Munich)
- ๐ ๏ธ **Claude Desktop ready** - works out of the box with MCP clients
## ๐ Quick Start
### Prerequisites
- Python 3.12 or higher
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
- SNCF API key (free registration)
### Installation
1. **Clone the repository**
```bash
git clone https://github.com/belgrano9/sncf_mcp_server.git
cd sncf_mcp_server
```
2. **Install dependencies**
```bash
uv sync
```
3. **Get your SNCF API key**
- Register at [SNCF Digital API Portal](https://www.digital.sncf.com/startup/api)
- Request access to the Navitia API
- Copy your API key
4. **Configure environment variables**
Create a `.env` file in the project root:
```env
SNCF_API=your-api-key-here
```
โ ๏ธ **IMPORTANT**: Never commit your `.env` file! It's already in `.gitignore`.
5. **Test the server**
```bash
uv run server.py
```
## ๐ Usage
### Standalone Testing
Test the search functionality directly in Python:
```python
from server import search_trains, find_station
# Search for trains
result = search_trains("Paris", "Lyon", "2025-11-20 14:00")
print(result)
# Find a station
stations = find_station("Paris")
print(stations)
```
Or use the included Jupyter notebook (`test_notebook.ipynb`) for interactive testing.
### Claude Desktop Integration
Add to your Claude Desktop config file:
**Windows** (`%APPDATA%\Claude\claude_desktop_config.json`):
```json
{
"mcpServers": {
"sncf": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\YourName\\path\\to\\sncf_mcp_server",
"run",
"server.py"
]
}
}
}
```
**macOS** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"sncf": {
"command": "uv",
"args": [
"--directory",
"/path/to/sncf_mcp_server",
"run",
"server.py"
]
}
}
}
```
**Linux** (`~/.config/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"sncf": {
"command": "uv",
"args": [
"--directory",
"/path/to/sncf_mcp_server",
"run",
"server.py"
]
}
}
}
```
Restart Claude Desktop, and you can ask:
- *"Show me trains from Paris to Lyon tomorrow at 2pm"*
- *"What's the earliest train from Paris Gare de l'Est to Munich tomorrow?"*
- *"Find all train stations in Paris"*
- *"How long does it take to get from Bordeaux to Marseille?"*
## ๐ ๏ธ MCP Tools
### 1. `search_trains`
Find trains between two stations with real-time data.
**Parameters:**
- `origin` (string): Origin station/city name (e.g., "Paris", "Lyon", "Paris Gare de l'Est")
- `destination` (string): Destination station/city name (e.g., "Mรผnchen Hbf", "Barcelona")
- `departure_datetime` (string, optional): Travel date/time in flexible formats:
- ISO: `"2025-11-28 08:00"` (recommended)
- European: `"28/11/2025 08:00"`
- Written: `"November 28, 2025 8:00am"`
- Default: current time
**Example Output:**
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SNCF JOURNEY SEARCH
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Searched for 'Paris Est':
โ Paris Gare de l'Est (ID: stop_area:SNCF:...)
Paris - Bercy (ID: stop_area:SNCF:...)
Paris Montparnasse (ID: stop_area:SNCF:...)
โ Selected: Paris Gare de l'Est
๐ Searched for 'Mรผnchen Hbf':
โ Mรผnchen Hauptbahnhof (ID: stop_area:OCE:...)
โ Selected: Mรผnchen Hauptbahnhof
๐
Searching for trains departing after: 2025-11-28 08:00
๐ API datetime format: 20251128T080000
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ AVAILABLE TRAINS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Found 5 journey option(s):
1. Depart: 2025-11-28 08:55 โ Arrive: 2025-11-28 14:54
Duration: 5h 59min | Direct
2. Depart: 2025-11-28 10:55 โ Arrive: 2025-11-28 16:54
Duration: 5h 59min | Direct
3. Depart: 2025-11-28 12:55 โ Arrive: 2025-11-28 19:18
Duration: 6h 23min | 1 change(s)
Route: Paris Gare de l'Est โ Stuttgart Hbf | Stuttgart Hbf โ Mรผnchen Hbf
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
```
### 2. `find_station`
Search for train stations in a city or by name.
**Parameters:**
- `station_name` (string): Station/city name to search (e.g., "Paris", "Lyon Part-Dieu")
**Example Output:**
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STATION SEARCH
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Searched for 'Paris':
โ Paris Gare de Lyon (ID: stop_area:SNCF:87686006)
Paris Montparnasse (ID: stop_area:SNCF:87391003)
Paris Gare du Nord (ID: stop_area:SNCF:87271007)
โ Selected: Paris Gare de Lyon
โ
Best match: Paris Gare de Lyon
ID: stop_area:SNCF:87686006
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
```
### 3. `get_train_prices` โ ๏ธ EXPERIMENTAL
**Educational Proof of Concept Only - May Not Work**
Attempts to scrape price information from SNCF's booking system.
**โ ๏ธ IMPORTANT DISCLAIMERS:**
- This is an **experimental feature** for educational purposes only
- **May violate SNCF's Terms of Service**
- Likely blocked by anti-scraping measures (403 Forbidden)
- **NOT recommended for production use**
- For real pricing, use commercial APIs (Lyko, Trainline) or SNCF Connect website
**Parameters:**
- `origin` (string): Origin station/city name
- `destination` (string): Destination station/city name
- `departure_datetime` (string, optional): Travel date/time (same formats as search_trains)
- `page` (integer, optional): Page number (default: 1)
- `per_page` (integer, optional): Results per page (default: 5, max: 20)
**Example Output (if it worked):**
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SNCF PRICE CHECK (EXPERIMENTAL)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๏ธ WARNING: Experimental feature
May not work due to anti-scraping measures
For educational purposes only
๐ Route: Paris Gare de Lyon โ Marseille Saint-Charles
๐
Date: 2025-11-17
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Attempting to fetch prices...
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Price check failed: Access forbidden - anti-scraping measures detected
This feature is experimental and may not work.
For real pricing, please visit:
- SNCF Connect: https://www.sncf-connect.com
- Or use commercial API providers
```
**Why This Probably Won't Work:**
- SNCF employs anti-scraping measures (403 Forbidden)
- Requires reverse-engineering their booking API
- API structure is proprietary and undocumented
- May violate Terms of Service
**Recommended Alternatives for Production:**
- [Lyko SNCF Connect API](https://lyko.tech/en/portfolio/train-api/sncf-connect-api/) - Commercial provider
- [Trainline API](https://www.thetrainline.com) - Multi-operator booking
- Official SNCF partnership programs
See [sncf_scraper/README.md](sncf_scraper/README.md) for technical details and ethical considerations.
## ๐๏ธ Architecture
```
sncf_mcp_server/
โโโ server.py # FastMCP server implementation
โโโ price_checker.py # Price scraping wrapper (experimental)
โโโ pyproject.toml # Dependencies & project config
โโโ .env # API key (not committed)
โโโ .gitignore # Git ignore rules
โโโ README.md # This file
โโโ test_notebook.ipynb # Jupyter notebook for testing
โโโ sncf_scraper/ # Price scraper module (experimental)
โ โโโ __init__.py # Module exports
โ โโโ models.py # TrainOffer, PriceSearchResult models
โ โโโ scraper.py # SNCFPriceScraper implementation
โ โโโ README.md # Scraper documentation & disclaimers
โโโ tests/ # Test suite
โโโ test_simple.py # Simple train search test
โโโ test_pagination.py # Pagination feature test
โโโ test_search.py # MCP wrapper test
โโโ test_price_scraper.py # Price scraper tests (experimental)
โโโ debug_*.py # Debug/diagnostic scripts
โโโ run_all_tests.py # Test runner
โโโ README.md # Test documentation
```
### How It Works
1. **Station Search**: Queries Navitia API's `/places` endpoint with fuzzy matching
2. **Journey Planning**: Uses `/journeys` endpoint with origin, destination, and datetime
3. **Date Parsing**: Flexible parser handles multiple date/time formats
4. **Response Formatting**: Returns human-readable journey information with:
- Departure and arrival times
- Journey duration
- Number of transfers
- Route details for multi-leg journeys
### Key Implementation Details
- โ
Real-time data (no local database needed)
- โ
Flexible date parsing with `python-dateutil`
- โ
European date format support (day-first parsing)
- โ
Automatic station ID resolution from city names
- โ
Shows top 3 station matches for transparency
- โ
Handles both domestic and international routes
- โ
Transfer information with route breakdown
- โ
**Pagination support** - Returns 10 results per page (up to 100 journeys)
- โ
**Comprehensive test suite** - See [tests/README.md](tests/README.md)
## ๐ Supported Routes
The server supports **any route** in the SNCF/Navitia network:
- **High-Speed (TGV)**: Paris-Lyon, Paris-Marseille, Paris-Bordeaux, Paris-Strasbourg
- **International**: Paris-London (Eurostar), Paris-Munich, Paris-Barcelona, Paris-Brussels
- **Long-Distance (Intercitรฉs)**: Regional connections across France
- **TER (Regional Express)**: Local services
- **Cross-border**: Connections to Germany, Italy, Spain, Switzerland, Belgium
**Major Cities:**
- Paris (multiple stations: Gare du Nord, Gare de Lyon, Montparnasse, Est, Austerlitz, Saint-Lazare, Bercy)
- Lyon, Marseille, Bordeaux, Toulouse
- Strasbourg, Nantes, Nice, Lille
- International: London, Munich, Barcelona, Brussels, Geneva, Milan
- **1000+ stations** across France and Europe!
Use `find_station` to discover available stations in any city.
## ๐ง Development
### Project Setup
```bash
# Clone and install
git clone https://github.com/belgrano9/sncf_mcp_server.git
cd sncf_mcp_server
uv sync
# Set up your API key in .env
echo "SNCF_API=your-api-key-here" > .env
# Run the server
uv run server.py
# Or use FastMCP dev mode
fastmcp dev server.py
```
### Dependencies
- **fastmcp** - MCP server framework
- **requests** (>=2.32.5) - HTTP client for API calls
- **python-dotenv** (>=1.2.1) - Environment variable management
- **python-dateutil** - Flexible date/time parsing
- **httpx** (>=0.28.1) - Async HTTP client
- **loguru** (>=0.7.3) - Logging
- **rich** (>=14.2.0) - Terminal formatting
### File Structure
- `server.py` - Main MCP server with `search_trains` and `find_station` tools
- `.env` - API key configuration (never commit!)
- `test_notebook.ipynb` - Interactive testing notebook
- `tests/` - Test suite (see [tests/README.md](tests/README.md))
### Testing
Run the test suite to verify everything works:
```bash
# Run all tests
uv run tests/run_all_tests.py
# Run individual tests
uv run tests/test_simple.py # Simple train search
uv run tests/test_pagination.py # Pagination feature
uv run tests/debug_env2.py # Environment check
```
See [tests/README.md](tests/README.md) for detailed test documentation.
## ๐ Data Source
Real-time data from [SNCF Navitia API](https://www.digital.sncf.com/startup/api):
- **API Base URL**: `https://api.sncf.com/v1`
- **Authentication**: HTTP Basic Auth (API key as username)
- **Coverage**: SNCF network across France and international connections
- **Update Frequency**: Real-time (no manual updates needed)
- **Format**: JSON responses
- **Documentation**: [Navitia API Docs](https://doc.navitia.io/)
### Getting an API Key
1. Visit [SNCF Digital](https://www.digital.sncf.com/startup/api)
2. Create an account
3. Request access to the Navitia API
4. Copy your API key to `.env`
## ๐ Known Issues
- Windows console may show encoding errors with Unicode characters (functionality not affected)
- Station name matching uses first result - use `find_station` for ambiguous names
- International routes may have limited availability depending on API coverage
## ๐ค Contributing
Contributions welcome! Please:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
### Ideas for Contributions
- [ ] Add price information (if available in API)
- [ ] Support for train status/real-time delays
- [ ] Multi-leg journey optimization
- [ ] Visualization of routes on maps
- [ ] Additional query filters (train type, max transfers, etc.)
- [ ] Support for round-trip queries
- [ ] Save favorite routes
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Acknowledgments
- [SNCF](https://www.sncf.com) for providing the Navitia open API
- [FastMCP](https://github.com/jlowin/fastmcp) by [@jlowin](https://github.com/jlowin) for the excellent MCP framework
- [Anthropic](https://www.anthropic.com) for Claude and the Model Context Protocol
- [Navitia](https://www.navitia.io/) for powering the transit data API
## ๐ฎ Support
- **Issues**: [GitHub Issues](https://github.com/belgrano9/sncf_mcp_server/issues)
- **API Documentation**: [Navitia API Docs](https://doc.navitia.io/)
- **MCP Docs**: [Model Context Protocol](https://modelcontextprotocol.io)
## ๐ Related Projects
- [Renfe MCP Server](https://github.com/belgrano9/renfe_mcp_server/tree/master) - Similar server for Spanish railways
- [FastMCP](https://github.com/jlowin/fastmcp) - The framework powering this server
- [MCP Servers](https://github.com/modelcontextprotocol/servers) - Official MCP server implementations
---
**Built with โค๏ธ using [FastMCP](https://github.com/jlowin/fastmcp) and [Claude](https://claude.ai)**
*Voyagez intelligent, voyagez en train! ๐*
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: search_trains finds journeys, find_station resolves station names, and get_train_prices retrieves pricing. Although search_trains and get_train_prices share origin/destination parameters, their descriptions and return types make them unambiguous.
All tool names follow the verb_noun pattern with lowercase and underscores: search_trains, find_station, get_train_prices. This is consistent and predictable.
With 3 tools, the server is minimal but well-scoped for train journey searching. Each tool serves a necessary function and there is no redundancy.
The server covers the core workflow: station lookup, journey search, and price lookup. Missing booking or real-time status, but these are beyond the apparent scope. The experimental nature of get_train_prices introduces some uncertainty, but the surface is otherwise complete for planning.