rail-interop-mcp

rail-interop-mcp

Provides an MCP interface to the ERA RINF railway infrastructure database for Poland and Czech Republic, enabling route planning, train compatibility checks (gauge, axle load, length, clearance), weather risk assessment, and traction planning, with a LangGraph agent integrating a custom MCP server and an OpenWeather MCP server.

Category
访问服务器

README

Rail Interop Planner

MCP-інтерфейс до реєстру інфраструктури ERA RINF (Польща, Чехія) плюс два шари, яких немає в наявних інструментах перевірки сумісності маршруту (зокрема в офіційному ERA Route Compatibility Check): погодне попередження і рекомендація, як саме змінити поїзд, щоб маршрут став прохідним. Два MCP-сервери: власний rail-interop-mcp (маршрут, сумісність поїзда, погодний ризик) і наявний OpenWeather MCP (прогноз в опорних пунктах маршруту), об'єднані агентом на LangGraph.

Індивідуальна лабораторна робота курсу KSE Agentic AI Summer School, тема «MCP Integration». Автор: Тихон Дукарев.

Python 3.12 · uv · MIT

Архітектура

Користувач (CLI) → Агент LangGraph → MCP-клієнт ─ streamable-http ─→ rail-interop-mcp (окремий процес, Python) → data/rinf.sqlite (ERA RINF, PL+CZ)
                                             └─ stdio ───────────→ mcp-openweather (окремий процес, Go)     → api.openweathermap.org
flowchart LR
  U[Користувач CLI] --> A[Агент LangGraph]
  A --> C[MCP-клієнт]
  C -- streamable-http --> S1["rail-interop-mcp\nвласний сервер"]
  C -- stdio --> S2["OpenWeather MCP\nнаявний сервер"]
  S1 --> D[("data/rinf.sqlite\nERA RINF, PL+CZ")]
  S2 --> W[("OpenWeather API")]

Агент бачить дані лише через MCP: жодного прямого доступу до data/rinf.sqlite чи до OpenWeather API з коду агента. Власний сервер стартує першим у своєму терміналі (uv run rail-interop-mcp --transport streamable-http, 127.0.0.1:8765/mcp); агент підключається за адресою з RAIL_MCP_URL, і той самий процес видно в MCP Inspector незалежно від агента. Сервер stateless: інструменти приймають маршрутний запит або явний список section_ids, жодного сесійного стану чи побічних ефектів; граф і індекси завантажуються один раз при старті (близько 0,1 с на 7883 точки). Обидва процеси друкують кожен виклик інструмента: сервер у stderr, агент - у консоль (discovery обох серверів, аргументи й скорочений результат кожного виклику).

Як це працює: чотири питання

Агент відповідає на практичне питання «чи проїде цей конкретний поїзд цим маршрутом», ставлячи кожній ділянці маршруту чотири перевірки проти реєстру RINF:

  1. Чи збігається ширина колії? Колісні пари поїзда (1435 мм) проти колії кожної ділянки: розбіжність - жорстка заборона.
  2. Чи витримає колія вагу? Навантаження на вісь поїзда проти межі ділянки (типово 20-22,5 т): перевищення руйнує колію.
  3. Чи влізе поїзд за габаритом? Профіль GA/GB/GC поїзда проти просвіту ділянки (мости, тунелі).
  4. Чи не задовгий поїзд? Довжина проти ліміту менеджера інфраструктури (PL 750 м / CZ 740 м) - м'яке попередження.

Параметри, яких реєстр не знає (наприклад, ETCS у польській частині заповнений лише на 4,4%), чесно позначаються unknown - це прогалина даних, а не дозвіл і не заборона. Поверх статичного реєстру агент кладе два динамічні шари: план тяги (які локомотиви і де їх міняти через зміну системи струму) і погодний ризик на день запиту (вітер для порожніх вагонів, спека, мороз проти температурного класу ділянок) - при перевищенні порога агент сам перебудовує маршрут.

Quick start

Передумови: uv, Python 3.12 (uv ставить сам), Go 1.24+ (для збірки OpenWeather MCP - brew install go або офіційний tarball), ключ OpenWeather (безкоштовний план, активація 10-60 хв) і ключ OpenRouter для LLM агента. Бінарник наявного сервера: go install github.com/mschneider82/mcp-openweather@e032683574a0723591445462ef7104d360ad0889 (закріплений коміт, його звіряє doctor).

uv sync
cp .env.example .env   # заповнити OWM_API_KEY, OWM_MCP_BIN, OPENROUTER_API_KEY

# Перевірка середовища (toolchain, ключі, зовнішні сервіси, без секретів у виводі)
uv run --python 3.12 --no-project scripts/doctor.py

# Термінал 1: власний сервер, окремий процес
uv run rail-interop-mcp --transport streamable-http

# Термінал 2: агент
uv run rail-agent --scenario A

# Тести (235, без мережі й ключів)
uv run pytest

# Discovery незалежно від агента (Термінал 1 має бути запущений)
npx -y @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp --method tools/list

Демо-сценарії

Кожен сценарій - готовий профіль поїзда (data/orders/*.yaml) плюс маршрут; звіт українською пишеться в out/. Точні визначення - rail_agent/scenarios.py.

Сценарій Команда Маршрут Результат (реальний прогін)
A uv run rail-agent --scenario A Katowice - Ostrava, контейнерний поїзд 81,4 км, PL-CZ через EU00072, увесь шлях dc_3kv, feasible_with_conditions (2 невизначені випадки габариту), погода спокійна (вітер до 7,7 м/с), низький ризик
B uv run rail-agent --scenario B Katowice - Breclav, той самий поїзд 265,4 км, перехід dc_3kvac_25kv_50hz на лінії Přerov-Břeclav (90,1 км під 25 кВ), feasible_with_conditions (16 невизначених випадків через прогалини RINF на коротких ділянках), погода спокійна; plan_traction показує зміну локомотива ET22 → Vectron MS саме на стику (CZ35966)
B2 uv run rail-agent --scenario B2 той самий маршрут, порожні платформи, знижений поріг вітру як демо штормового попередження IM реальний прогноз вітру (3,9-8,0 м/с) перевищує знижений поріг 3,0 м/с → overall=high, replan_recommended=true → агент переплановує маршрут в об'їзд (373,8 км через EU00074)
C uv run rail-agent --scenario C той самий маршрут, поїзд 25 т/вісь спершу infeasible (перевищення осьового навантаження), adjust пропонує і перевіряє нижче навантаження - фінальний звіт feasible_with_conditions, 81,4 км

Failure demo

Реалістичний збій наявного сервера і контрольована деградація (docs/EXISTING_SERVER.md розділ 5), без падіння всього потоку:

Сценарій Команда Що відбувається
D1 uv run rail-agent --scenario D1 шлях до бінарника OpenWeather MCP навмисно зламаний → з'єднання не встановлюється, discovery показує weather як failed, звіт про маршрут (81,4 км) виходить без погодного шару з явним поясненням
D2 uv run rail-agent --scenario D2 погода для Katowice підмінена на неіснуюче місто Kyivvvv → сервер повертає «тихі нулі» (isError=False, порожня назва, всі значення 0), агент розпізнає це як збій і пропускає цю точку, погода для Ostrava надходить нормально - звіт частковий, а не порожній

Джерело числа маршруту в будь-якому звіті можна простежити до сирого запису ERA:

uv run python scripts/trace_section.py c0fcd67d79

Інструменти

Інструмент Що робить Тип Контракт
resolve_operational_point знаходить операційні пункти PL/CZ за назвою (діакритика ігнорується) пошук з обмеженим контрактом docs/TOOL_CONTRACTS.md
find_route обчислює маршрут по інфраструктурному графу ERA RINF (Dijkstra) обчислення, первинне джерело docs/TOOL_CONTRACTS.md
check_train_compatibility перевіряє поїзд проти маршруту (габарит, осьове навантаження, довжина, просвіт) валідація docs/TOOL_CONTRACTS.md
assess_weather_risk перетворює прогноз на маршруті на структурований ризик і рекомендацію оцінка ризику docs/TOOL_CONTRACTS.md
plan_traction призначає локомотиви з парку (data/locomotives.yaml) на прогони маршруту, мінімізуючи кількість змін планування docs/TOOL_CONTRACTS.md
weather (наявний сервер, mschneider82/mcp-openweather) поточна погода і прогноз на 5 діб для міста наявний зовнішній сервіс docs/EXISTING_SERVER.md

Усі 5 назв вище дослівно збігаються з tools/list живого сервера (перевірено через MCP Inspector і тестом tests/test_server_contract.py::test_tool_contracts_doc_names_match_tools_list).

Бонус: вебінтерфейс

Локальна вебапка поверх того самого агента, без жодних змін у rail_agent/ чи rail_mcp/: форма з фільтрами (країна -> станція з пошуком по всіх 7883 точках -> пресет поїзда) або запит вільним текстом, живий прогрес по вузлах графа через SSE (умовні кроки adjust/replan видно як вставні), карта Leaflet з маршрутом (розфарбовка за енергосистемою або за сумісністю), лінійна схема, картки сумісності/тяги/погоди і журнал викликів інструментів.

# Термінал 1: власний сервер (як у Quick start)
uv run rail-interop-mcp --transport streamable-http

# Термінал 2: вебсервер бонусного інтерфейсу
uv run rail-web

# Браузер: http://127.0.0.1:8080

Це необов'язкове доповнення: основний спосіб роботи з проєктом - CLI вище. Фронтенд - vanilla JS + vendored Leaflet 1.9.4 (BSD-2), без node і кроку збірки; єдина зовнішня залежність у браузері - тайли OpenStreetMap (потрібен інтернет; перемикач «схема» працює і без них).

Структура репозиторію

rail_mcp/            власний MCP-сервер: схеми, доменна логіка, main.py
  domain/            чисті функції без залежності від MCP (graph, search, rules, weather_rules, traction, errors)
  build/             збірка датасету з ERA SPARQL у data/rinf.sqlite
rail_agent/          агент LangGraph: граф, вузли, конфіг обох MCP-з'єднань, CLI
rail_web/            бонус: вебінтерфейс (FastAPI + SSE, static/ з vendored Leaflet 1.9.4)
data/                датасет (rinf.sqlite), довідники з джерелами (включно з locomotives.yaml), README.md
scripts/             doctor.py, smoke_weather.py, gen_tool_contracts.py, trace_section.py
tests/               235 тестів: домен, контракт сервера (реальний stdio-виклик), агент, веб, дані
docs/                TOOL_CONTRACTS, contracts_extra.yaml, EXISTING_SERVER, DESIGN_RATIONALE
out/                 звіти агента (ігнорується git)

Design rationale

Кожен із 5 власних інструментів на межі MCP тому, що дані й правила сумісності (пороги, класифікація габариту, погодні ризики) мають жити біля датасету, а не в промпті: модель отримує вже структурований вердикт, не сирі рядки RINF. Компроміси, обмеження і причина, чому OpenWeather - єдиний потрібний наявний сервер для цього потоку, розписані в docs/DESIGN_RATIONALE.md (разом з розділом про співвідношення з офіційним ERA Route Compatibility Check).

Дані й ліцензії

Реєстр інфраструктури ЄС (RINF), Європейське залізничне агентство, знімок Польщі й Чехії від 2026-08-18, ліцензія CC BY 4.0. Атрибуція («Data: European Union Agency for Railways, Register of Infrastructure (RINF), CC BY 4.0») додається до кожної відповіді власних інструментів і до звітів агента. Погодні дані - OpenWeather, атрибуція «Weather data provided by OpenWeather». Джерело, дата знімка, точні числа й команда відтворення (uv run build-dataset --from-raw) - data/README.md.

Обмеження

  • RINF - реєстр інфраструктури, не розклад руху: find_route дає фізичний профіль маршруту, не час у дорозі й не наявність вільних вікон.
  • ETCS майже відсутній у польській частині знімка (4726 з 4946 секцій без запису) - etcs_level там здебільшого unknown, це властивість джерела, не помилка сервера.
  • Довідники load_categories.yaml, train_length_limits.yaml, weather_thresholds.yaml - вторинні джерела (EN 15528, Network Rail, RAIB), не офіційний реєстр ERA; кожен файл має посилання на джерело.
  • plan_traction - жадібна евристика (тримає поточний локомотив, поки можливо), не доведений глобальний оптимум і не пошук по графу станів; фіксований демо-парк із двох локомотивів (data/locomotives.yaml); допуски локомотивів по країнах (габарит кузова, маса на вісь по типу локомотива) не моделюються.
  • Погода: вітер відомий лише на день відправлення (наступні дні мають wind_ms=null); опорний пункт зводиться до найближчого великого міста зі словника (rail_agent/city_names.py), не координат - задокументоване обмеження контракту наявного сервера.
  • Об'їзд закритої довільної ділянки і багатоітераційний цикл перепланування свідомо не реалізовані (один прохід кожного); зроблено лише перепланування, тригероване погодним ризиком (сценарій B2).
  • Проєкт не намагається замінити офіційний ERA Route Compatibility Check - різниця й межі застосування розписані в docs/DESIGN_RATIONALE.md (розділ про prior art).

Захист

Сценарій демонстрації на 12 хвилин буде в docs/DEMO_SCRIPT.md; коміт для захисту буде позначений тегом v1.0.0.

Ліцензія

Код - MIT, LICENSE. Дані ERA RINF - CC BY 4.0 (Європейське залізничне агентство). Погодні дані - OpenWeather. Дякую курсу KSE Agentic AI Summer School за завдання.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选