rzd-api
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@rzd-apiFind trains from Moscow to St. Petersburg on 2026-09-01"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
RZD API for Bun
Типизированный асинхронный клиент на Bun/TypeScript и MCP-сервер для
неофициального API ticket.rzd.ru. Проект не связан с
ОАО «РЖД»; внутренние endpoint и схемы ответов могут меняться без предупреждения.
Возможности
поиск прямых поездов в одну сторону и туда-обратно;
поиск станций и разрешение названий в коды;
календарь поездов и минимальные цены;
вагоны, места, схемы, изображения и станции маршрута;
поиск полностью свободного купе по диапазону дат;
MCP через STDIO и Streamable HTTP;
retries, таймауты и LRU-кэш станций.
Related MCP server: Koleo MCP Server
Установка
Требуется Bun 1.2 или новее.
bun installTypeScript API
import { RzdClient } from "rzd-api";
const client = new RzdClient();
try {
const routes = await client.searchTickets(
"Москва",
"Санкт-Петербург",
"2026-09-01",
{ adults: 1 },
);
console.log(routes);
} finally {
client.close();
}Основные методы: searchTickets, findStations, resolveStationCode,
getCarriages, getTrainAvailability, getMinimalPrices, getCarScheme,
getCarImages, getRouteStations, searchFullCompartments.
Полное купе
searchFullCompartments и MCP-инструмент search_full_compartments перебирают
диапазон дат (не более 31 дня) и разделяют два разных ответа:
confirmed— API вернул нужные места внутри одного купе (FreePlacesByCompartments), указаны номер вагона, номер купе и сами места. Один физический вагон приходит несколькими записями — нижние и верхние полки одного купе тарифицируются отдельно, — поэтому записи вагона сначала сливаются по номеру, иначе купе из четырёх мест выглядит как два раза по два;candidates— свободных мест в вагоне достаточно, но одно купе не подтверждено:places_not_in_one_compartment(места в разных купе) илиcompartment_layout_missing(в ответе нет разбивки по купе).
Суммарное число свободных мест никогда не переводит вагон в confirmed. Инвариант
закреплён тестами на фикстурах в tests/fixtures/, поэтому изменение парсера не может
незаметно вернуть более смелую формулировку.
Номера мест возвращаются вместе с их маркерами: 36Ж — место в женском купе,
6С — в смешанном. Купе определяет число, маркер сохраняется, потому что именно
его пассажир увидит в билете.
Поле checkedAt содержит момент проверки по московскому времени: наличие мест
устаревает за минуты. Даты, которые не удалось проверить, попадают в errors,
а не молча превращаются в «мест нет».
Готовая инструкция для ассистента лежит в skills/find-full-compartment/.
Схема вагона
getCarScheme возвращает imageUrls — абсолютные ссылки на чертёж вагона
(SVG). MCP-инструмент get_car_scheme с include_image: true вкладывает сам
чертёж в ответ; по умолчанию выключено.
Чертёж отдаётся в PNG, а не в SVG: модели принимают png, jpeg, gif и webp, а
SVG клиент может только сохранить в файл. Растеризацией занимается
@resvg/resvg-wasm; шрифт для номеров мест (сабсет DejaVu Sans, 14 КБ) зашит в
src/scheme-font.ts, иначе resvg не нарисует текст. Если растеризатор не
поднялся, возвращается исходный SVG.
Сам чертёж — шаблон: все полки почти белые, номера на них тоже белые, потому что
сайт перекрашивает места под статус. Поэтому free_places заливаются синим, а
selected_places красным — те же два цвета, что в легенде ticket.rzd.ru.
Номера на незакрашенных полках перекрашиваются в тёмный, на закрашенных
остаются белыми. Без этого картинка нечитаема.
Рисуется весь вагон целиком, около 60 КБ и 300 мс. Кадрирование до одного купе не делается намеренно: resvg режет уже отрисованное, поэтому зум в купе заставил бы его сначала нарисовать вагон в 21000 пикселей шириной — это 17 секунд.
Ссылки строятся от schemeImageBaseUrl (RZD_SCHEME_IMAGE_BASE_URL,
по умолчанию публичный ticket.rzd.ru) и никогда от RZD_BASE_URL: иначе
адрес приватного proxy попадёт в каждый ответ ассистента. Байты, наоборот,
запрашиваются через RZD_BASE_URL, чтобы работать из закрытой сети. Инвариант
закреплён тестом в tests/car-scheme.test.ts.
MCP
Локальный STDIO:
bun run mcpStreamable HTTP на loopback:
bun run mcp:http
curl http://127.0.0.1:8000/healthПри публикации на non-loopback адресе нужен Bearer-токен длиной не менее 32 символов:
MCP_TRANSPORT=streamable-http \
MCP_HOST=0.0.0.0 \
MCP_AUTH_TOKEN="replace-with-a-random-token-at-least-32-characters" \
bun run src/mcp.tsEndpoint: http://localhost:8000/mcp. Переменные окружения:
MCP_PORT, MCP_RATE_LIMIT_PER_MINUTE, MCP_ALLOWED_HOSTS.
Подключение к Codex:
codex mcp add rzd -- bun run /absolute/path/to/rzd-api/src/mcp.tsDocker
export MCP_AUTH_TOKEN="replace-with-a-random-token-at-least-32-characters"
docker compose up -dVercel
Проект содержит Vercel Functions без web-фреймворка:
https://<project>.vercel.app/— страница с краткой документацией;https://<project>.vercel.app/mcp— публичный Streamable HTTP MCP;https://<project>.vercel.app/health— healthcheck.
Страница собирается в src/landing.ts из того же registerMcpTools, что
регистрирует инструменты в MCP: новый инструмент без русского описания роняет
сборку страницы, поэтому таблица не может разойтись с сервером.
Vercel entrypoint использует Elysia, официальный mcp-handler и Bun Runtime.
Маршруты принадлежат самому приложению; Vercel rewrites не используются.
Endpoint РЖД можно переопределить переменными окружения RZD_BASE_URL и
RZD_B2B_BASE_URL. Значения не должны храниться в репозитории.
Под serverless клиент отказывает быстро: при выставленной VERCEL таймаут
становится 8 секунд, повтор один. Иначе зависший upstream съедает всё время
функции, платформа обрывает вызов, и клиент получает не ошибку, а мёртвое
соединение — по нему невозможно понять, что случилось. Переопределяется
RZD_TIMEOUT_MS и RZD_RETRY_TOTAL.
vercel deployОграничение нагрузки
Публичный /mcp открыт без авторизации, поэтому лимит стоит на эдже Vercel, а не
в коде: serverless-инстанс держит счётчики в памяти, масштабируется горизонтально
и теряет их на каждом холодном старте, так что лимит в процессе — это «N на
инстанс». Эдж к тому же отбивает запрос до вызова функции.
./scripts/firewall-rate-limit.sh # применить: 60 запросов в минуту с IP
./scripts/firewall-rate-limit.sh check # показать живое правилоСкрипт идемпотентен: существующее правило редактируется на месте, отсутствующее
создаётся, черновик публикуется. Значения переопределяются переменными
RATE_LIMIT_REQUESTS, RATE_LIMIT_WINDOW, RATE_LIMIT_PATH, RULE_NAME.
Ключ лимита — IP клиента. Vercel сам перезаписывает x-forwarded-for и не
пропускает внешние значения, поэтому подделать адрес нельзя.
Учтите, что лимит считает входящие запросы, а не исходящие: один вызов
search_full_compartments за месяц — это десятки обращений к API РЖД. Сам вызов
ограничен с трёх сторон: диапазон не длиннее 31 дня, поезд не открывается, если
ни одна его купейная группа не дотягивает до нужного числа мест (группа не может
содержать меньше мест, чем любой её вагон, поэтому пропуска подтверждённого купе
не будет), и весь обход ограничен бюджетом maxRequests — по умолчанию 150.
К первому подтверждённому купе прикладывается чертёж вагона с залитыми синим
местами — и картинкой в ответе, и ссылкой image.url внутри JSON. Ссылка нужна
потому, что не всякий клиент показывает image-блоки: ChatGPT их не видит и,
оставшись без изображения, иллюстрирует ответ фотографиями чужих вагонов из
интернета. Отключается include_image: false.
Ответ инструмента несёт чертёж тремя способами, потому что клиенты различаются
в том, что показывают: сама картинка блоком image с аннотацией
audience: ["user"], штатный resource_link со ссылкой на неё и та же ссылка
полем image.url первым в JSON — последним оно быть не может, длинный ответ
клиент обрезает с хвоста.
Ссылка ведёт на собственный endpoint:
GET /scheme/552/PcFirstStorey.png?free=33,34,35,36Он рисует ту же схему и кэшируется на сутки. Специально узкий — числовой номер
схемы, известная раскладка, не больше 36 мест, — чтобы не превратиться в
универсальный прокси. Адрес сервера задаётся RZD_PUBLIC_BASE_URL.
Диапазон, начинающийся в прошлом, не отвергается, а подрезается сегодняшним днём:
«найди купе на август» приходит целым месяцем и в середине августа, и отказывать
из-за прошедших дат бессмысленно. Фактическое начало видно в dateFrom ответа.
Обход ограничен и по времени — maxSeconds, по умолчанию 9 секунд. Serverless-
вызов всё равно обрывается платформой через несколько секунд, а оборванный вызов
не сообщает клиенту ничего и выглядит как недоступный сервис. Лучше вернуть
найденное и назвать даты, до которых не дошли.
Обход останавливается, как только набрано maxResults подтверждённых купе — по
умолчанию три ближайших варианта, а не весь месяц. Даты, до которых он не дошёл,
возвращаются в unchecked, а не выдаются за пустые: с них можно продолжить
поиск. Что не поместилось в ответ, посчитано в omitted, потраченные запросы —
в requests.
Логи
Каждый вызов инструмента и каждый запрос наружу пишутся строкой JSON в stdout — это то, что собирает и показывает Vercel:
{"at":"2026-08-06T11:25:35.095Z","upstream":"suggests","status":200,"ms":504,"attempt":0}
{"at":"2026-08-06T11:25:37.421Z","tool":"search_full_compartments","ms":2331,"ok":true,"blocks":["text","image"]}По upstream видно, какие endpoint РЖД дёргались и сколько отвечали, по tool —
дошёл ли клиент до инструмента вообще. Без этого зависший upstream и клиент,
который инструмент не вызывал, выглядят снаружи одинаково.
Адрес из RZD_BASE_URL в лог не попадает: пишется только путь под ним. Лог —
типовое место, куда утекают секреты, поэтому на это есть тест.
Разработка
bun run checkБезопасность
Проверка TLS-сертификата включена: ticket.rzd.ru предъявляет публично
доверенный сертификат. Отключить её можно только явно, переменной
RZD_INSECURE_TLS=1 — это нужно, если запрос идёт через собственный proxy с
самоподписанным сертификатом. Не передавайте клиенту секреты или учётные данные.
Лицензия
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityCmaintenanceAn MCP server that enables AI assistants to interact with the Netherlands Railways (NS) API for route planning, pricing, and real-time departure information. It provides tools for searching stations, planning trips with connections, and viewing real-time departure boards.31MIT
- FlicenseAqualityFmaintenanceAn MCP server that provides access to the Koleo API for Polish train timetables, including station information, departures, and arrivals. It allows users to search for connections, view train routes, and access real-time timetable data and seat occupancy statistics.146
- FlicenseNot gradedqualityFmaintenanceAn MCP server to retrieve train schedules from National Rail using the Realtime Trains API, providing tools for live departures and arrivals.7
- AlicenseNot gradedqualityDmaintenanceAn MCP server for real-time Italian railway data, enabling natural language queries about train schedules, delays, departures, arrivals, and live tracking via the Viaggiatreno API.5MIT
Related MCP Connectors
Geo-based flight search MCP server. Find more flights between any two places on earth
This MCP server provides seamless access to Malaysia's government open data, including datasets, w…
Hotel booking MCP server. Search, book, and manage reservations across 250K+ properties worldwide.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/slavb18/rzd-api'
If you have feedback or need assistance with the MCP directory API, please join our Discord server