Skip to main content
Glama
dukarev

rail-interop-mcp

by dukarev

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 обох серверів, аргументи й скорочений результат кожного виклику).

Related MCP server: MCP LangChain Weather Agent

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

Агент відповідає на практичне питання «чи проїде цей конкретний поїзд цим маршрутом», ставлячи кожній ділянці маршруту чотири перевірки проти реєстру 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 за завдання.

Available Tools

5 tools
assess_weather_riskA

Turn a weather forecast at points along a route into a structured risk assessment.

Call find_route first (for `route` or its section_ids) and the existing OpenWeather MCP
server's `weather` tool for forecasts at find_route's waypoints, before calling this.
Exactly one of `route` or `section_ids` is required, same as check_train_compatibility.
ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
factorsYes
overallYes
attributionNo
replan_recommendedYes
speed_restrictionsYes
avoid_section_ids_suggestedYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the behavioral burden. It discloses the dependency chain and the exclusive-or input pattern, but it does not state whether the call is read-only, how default thresholds are applied, or what happens to the structured output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no filler: purpose first, then prerequisites and input constraint. The 'same as check_train_compatibility' reference efficiently avoids repeating sibling documentation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with a nested input schema and existing output schema, the description covers the critical cross-tool dependency and the mutually exclusive input requirement. It could add a sentence about weather_points construction or threshold defaults, but the schema already documents those fields.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds meaning for route/section_ids (exclusive-or, prerequisite via find_route) beyond the schema. However, schema description coverage is reported as 0%, and train, weather_points, and thresholds_override semantics are left to the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific operation: turning weather forecasts at points along a route into a structured risk assessment. This clearly separates the tool from the route-finding and compatibility siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly orders prerequisites: call find_route first for route/section_ids, call the OpenWeather weather tool for forecasts at waypoints, then this tool. It also states the exact-one-of route/section_ids requirement, mirroring check_train_compatibility.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_train_compatibilityA

Check whether a train fits the infrastructure of a route (gauge, axle load, clearance, length).

Call find_route first and pass its route as `route`, or pass `section_ids` to check a specific
segment (e.g. after adjusting a route). Exactly one of the two is required.
ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
summaryYes
verdictYes
violationsYes
attributionNo
what_would_make_it_feasibleYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the key constraint that exactly one of route or section_ids must be provided, which is valuable. However, it does not mention whether the operation is read-only, any side effects, or behavior around strict_unknown and im_max_train_length_m. It does not contradict annotations (none exist), but it is missing some behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences, front-loaded with the tool's main purpose and then usage. Every word earns its place, with no fluff or repetition. Strong structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity and the presence of an output schema (so return format need not be explained), the description covers the primary usage pattern adequately. It misses some parameter details like strict_unknown and im_max_train_length_m, but these are defined in the schema. Overall, it is complete enough for an agent to call the tool correctly in the main intent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0% in the description text, so the description must compensate. It explains the relationship and requirement between route and section_ids, adding meaning beyond the bare schema. However, it does not explain train, strict_unknown, or im_max_train_length_m, leaving significant gaps. The partial compensation merits a baseline 3 rather than lower.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool checks train-route compatibility across gauge, axle load, clearance, and length. It also distinguishes itself from find_route by referencing it as a prerequisite, though it does not explicitly differentiate from siblings like assess_weather_risk. The purpose is specific and understandable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs to call find_route first and pass its result, or alternatively pass section_ids for a specific segment. It clearly states that exactly one of the two is required, providing clear when-to-use and when-to-use-alternative guidance. This is exemplary usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_routeA

Compute a route between two operational points over the ERA RINF infrastructure graph.

Call resolve_operational_point first to get valid op_id values. Call this before
check_train_compatibility or assess_weather_risk: both need the route this returns.
ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
routeYes
attributionNo

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that the tool computes a route and that it depends on resolve_operational_point, but it doesn't state any side effects, read-only nature, or error behaviors. However, since the tool is a pure computation with no destructive actions implied, this is adequate but not rich. The dependency advice adds some behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The purpose is front-loaded, and the usage guidance is placed immediately after. Every word earns its place, making it highly scannable for an agent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema (which explains return values), the tool has many parameters (objective, allowed_gauges_mm, avoid_section_ids, detail, waypoints_for_weather) that are entirely undocumented in the description. The description only clarifies the op_id parameters indirectly via the mention of resolve_operational_point. Given the tool's complexity and zero schema coverage, this is a significant gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description fails to compensate. It mentions 'op_id values' but does not explain that from_op_id and to_op_id are required, nor does it explain objective, allowed_gauges_mm, avoid_section_ids, detail, or waypoints_for_weather. An agent cannot infer what these parameters mean from the description alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Compute'), the resource ('a route between two operational points'), and the context ('over the ERA RINF infrastructure graph'). This distinguishes it from siblings like resolve_operational_point (resolves points) and check_train_compatibility (checks compatibility), making the tool's purpose immediately clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs the caller to 'Call resolve_operational_point first' and to call this tool 'before check_train_compatibility or assess_weather_risk' because both need the route. This provides direct ordering and dependency guidance, leaving no ambiguity about when to use it relative to siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

plan_tractionA

Assign locomotives from a fleet to a route's runs, greedily minimising changes.

Call find_route first and pass its route as `route`, or pass `section_ids`, same as
check_train_compatibility. Give at least one locomotive via `fleet_ids` (ids from
data/locomotives.yaml) or `custom_locomotives`. A run with no locomotive able to cover
it (unknown/non-electrified energy system, or unmatched signalling) is uncovered, not
an error.
ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
uncoveredYes
assignmentsYes
attributionNo
change_pointsYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description usefully reveals that the assignment is greedy, minimizes changes, and treats uncovered runs as non-errors. However, it mentions 'unmatched signalling' as a cause of uncovered runs without noting that this depends on require_signalling_match, which defaults to false in the schema. With no annotations present, this ambiguity matters and weakens transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three tightly written sentences, starting with the core purpose, then moving to prerequisites, then edge-case behavior. Every sentence adds necessary information and there is no redundant repetition of schema details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the essential invocation prerequisites and the unusual 'uncovered, not an error' behavior, and an output schema exists for return-shape information. The main gap is not explicitly clarifying the signalling condition and its default, but the nested schema partially compensates for that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is low at the top level, and the description compensates by explaining the relationship between route, section_ids, fleet_ids, and custom_locomotives. It tells the agent where route comes from, that section_ids follows check_train_compatibility semantics, and where locomotive ids originate. The require_signalling_match parameter is left to the schema, but that field already has a detailed description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence clearly states the verb, resource, and goal: 'Assign locomotives from a fleet to a route's runs, greedily minimising changes.' This distinguishes it from sibling tools like find_route and check_train_compatibility, whose purposes are route discovery and compatibility checking rather than assignment.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit operational guidance: call find_route first and pass its route, or pass section_ids like check_train_compatibility, and provide at least one locomotive via fleet_ids or custom_locomotives. It lacks an explicit 'when not to use this tool' or a direct comparison to a sibling alternative, but the invocation workflow is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_operational_pointA

Find operational points (stations, junctions, border points) by name in Poland or Czechia.

Call this first to turn a place name from the user into an op_id for find_route. Diacritics
are ignored. An empty candidate list is a valid answer (the place was not found), not an error.
ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
candidatesYes
attributionNo

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the disclosure burden. It adds useful behavioral context: diacritics are ignored, and an empty list is a valid 'not found' result. However, it does not disclose response structure, sorting, or any other side effects. Given the absence of annotations, it provides some value but not comprehensive coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the purpose, then immediately gives usage context ('Call this first') and a critical edge-case behavior. Every sentence earns its place with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a name-resolution tool with an output schema (indicated by 'Has output schema: true'), the description covers the core purpose, usage flow, and key edge case. It does not detail how to handle multiple matches, but that is likely covered by the output schema and is not a critical omission for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema itself contains detailed descriptions for all properties (query, limit, types, countries), including the diacritics normalization. The description echoes 'by name' and 'diacritics ignored' but adds nothing materially new beyond the schema. Since the schema already documents parameters well, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Find'), resource ('operational points'), and specifics ('by name in Poland or Czechia'). It also explicitly connects to the sibling tool find_route by stating it produces an op_id for that purpose, distinguishing it from route planning and other tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Call this first' to resolve a place name to an op_id for find_route, which is clear when-to-use guidance. It also clarifies that an empty candidate list is a valid outcome, not an error. However, it doesn't explicitly state when NOT to use it or compare it to other alternatives, so it lacks full exclusion guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedassess_weather_risk
    • First observedcheck_train_compatibility
    • First observedfind_route
    • First observedplan_traction
    • First observedresolve_operational_point

TDQS

A4.2/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinctly different aspect of the rail planning workflow: place resolution, route computation, infrastructure compatibility, weather risk, and traction planning. Their inputs and outputs are clearly separated, with no overlapping responsibilities or ambiguous boundaries.

Naming Consistency5/5

All tool names follow a strict verb_noun pattern in snake_case: resolve_operational_point, find_route, check_train_compatibility, assess_weather_risk, plan_traction. The verbs are specific and the nouns clarify the object, making the naming entirely predictable.

Tool Count5/5

With 5 tools, the server covers the core end-to-end rail interoperability workflow without redundancy. Each tool is necessary and contributes to a different step, so the count is well-calibrated for the stated purpose.

Completeness5/5

The tools form a complete lifecycle: resolve places to op_ids, find a route, validate train compatibility, assess weather risk, and plan traction. No obvious missing operations are apparent for the server's domain, and each tool's output feeds naturally into the next step.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP server that exposes the Deutsche Bahn public transport API to any MCP-compatible client (Claude Desktop, Cursor, Cline, Continue, etc.). Five tools cover station search, departures, journey planning, trip details, and nearby stations.
    6
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    An (unofficial) MCP server for the Deutsche Bahn Timetables API — station search, planned departures, and real-time changes (delays, platform changes, cancellations) as tools for Claude and other MCP clients.
    4
    2
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    MCP server for Indian Railways data, enabling AI agents to search trains, get schedules, live status, PNR info, and more without an API key.
    11
    7
    MIT