MCP ETO Travel
MCP-сервер для поиска туров на eto.travel через браузерную автоматизацию Playwright.
Проект подготовлен для двух сценариев использования:
Local / stdio— пользователь запускает MCP локально на своей машине.Remote / self-hosted HTTP— пользователь поднимает MCP у себя на сервере и подключается к нему по URL.
В репозитории нет привязки к вашему серверу, IP или временному домену. Всё оформлено как универсальный self-hosted проект, который любой человек сможет развернуть у себя.
Что умеет сервер
- ищет туры через реальный UI сайта
https://eto.travel/search/ - принимает структурированные фильтры вместо одного свободного текста
- возвращает один тур или небольшую подборку туров
- сообщает клиенту, что долгий поиск начался и может занять около минуты
- просит уточнить критичные данные, если не указаны направление, город вылета или число взрослых
- ослабляет только не-критичные фильтры (
month,departureCity,nights), если строгий поиск не дал результата - отбрасывает нерелевантные карточки для точечных направлений вроде
Сочи
Инструменты
find_any_tour— поиск одного подходящего тураfind_tour_options— поиск подборки туров с категориями:budget— самый бюджетный вариантoptimal— самый сбалансированный вариантpremium— самый дорогой или комфортный вариант из найденных
Общие входные аргументы:
destination— обязательное направление или курорт, напримерТурцияилиСочиdepartureCity— город вылета, напримерМоскваadults— количество взрослыхnights— количество ночей илиnullmonth— месяц в нормализованном виде (январь...декабрь) илиnullrawQuery— исходная пользовательская формулировка илиnull
Быстрый старт для обычного пользователя
Если человек просто хочет поставить проект у себя, у него есть два простых варианта.
Вариант 1. Локально без сервера
git clone https://github.com/nyxandro/mcp-eto-travel.git
cd mcp-eto-travel
npm install
npx playwright install chromium
npm run build
Дальше MCP можно подключать как локальный процесс.
Вариант 2. На своём сервере по URL
git clone https://github.com/nyxandro/mcp-eto-travel.git
cd mcp-eto-travel
cp .env.example .env
После этого нужно открыть .env и заменить хотя бы:
ALLOWED_HOSTSPUBLIC_DOMAIN
Потом запуск:
docker-compose up -d --build
Health check:
curl http://127.0.0.1:3000/health
Если нужен публичный домен без порта, ставится reverse proxy вроде Nginx.
Установка для разработки
Требования:
- Node.js 20+
- npm 10+
- Chromium для Playwright
Если Chromium ещё не установлен:
npx playwright install chromium
Установка:
npm install
npx playwright install chromium
npm run build
Локальный MCP для OpenCode
Запуск вручную:
node dist/index.js
Или в dev-режиме:
npm run dev
Пример OpenCode-конфига:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"eto-travel-local": {
"type": "local",
"command": ["node", "./dist/index.js"],
"enabled": true,
"timeout": 180000
}
}
}
Готовый пример лежит в examples/opencode.local.json.
Классический MCP-конфиг для клиентов с mcpServers:
{
"mcpServers": {
"eto-travel": {
"command": "node",
"args": ["/absolute/path/to/mcp-eto-travel/dist/index.js"]
}
}
}
Или:
{
"mcpServers": {
"eto-travel": {
"command": ["node", "/absolute/path/to/mcp-eto-travel/dist/index.js"]
}
}
}
Self-hosted remote MCP по URL
1. Настройка .env
Скопируйте пример:
cp .env.example .env
Пример .env:
TRANSPORT_MODE=http
HOST=0.0.0.0
PORT=3000
MCP_PATH=/mcp
ALLOWED_HOSTS=mcp.example.com,localhost,127.0.0.1
PUBLIC_DOMAIN=mcp.example.com
Что важно заменить:
ALLOWED_HOSTS— сюда обязательно впишите свой доменPUBLIC_DOMAIN— тоже замените на свой реальный домен
2. Запуск через Docker Compose
docker-compose up -d --build
Это поднимет:
- MCP HTTP endpoint внутри контейнера
- health endpoint на
/health - основной MCP path на
/mcp
3. Проверка
Локально на сервере:
curl http://127.0.0.1:3000/health
Ожидаемый ответ:
{"status":"ok"}
4. Публичный URL через Nginx
В репозитории лежит шаблон nginx.mcp-eto-travel.conf.
Он показывает, как проксировать запросы на контейнер.
Типовой итоговый endpoint будет таким:
https://mcp.example.com/mcp
Важно:
- в
server_nameнужно указать свой домен - в
.envтот же домен должен быть вALLOWED_HOSTS
Пример OpenCode-конфига для remote режима
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"eto-travel-remote": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"enabled": true,
"timeout": 180000
}
}
}
Готовый пример лежит в examples/opencode.remote.json.
Классический MCP-конфиг для обычных клиентов:
{
"mcpServers": {
"eto-travel": {
"url": "https://mcp.example.com/mcp"
}
}
}
Если клиент требует явный transport:
{
"mcpServers": {
"eto-travel": {
"url": "https://mcp.example.com/mcp",
"transport": "streamable-http"
}
}
}
Что лежит в репозитории для удобного деплоя
Dockerfile— готовый образ для запускаdocker-compose.yml— запуск через Compose.env.example— пример env-настроекnginx.mcp-eto-travel.conf— шаблон reverse proxyexamples/opencode.local.json— локальный OpenCode-конфигexamples/opencode.remote.json— remote OpenCode-конфиг
Архитектура
src/index.ts— entrypoint, переключает режим запуска междуstdioиhttpsrc/runtime/config.ts— нормализация runtime env-конфигаsrc/runtime/http-server.ts— HTTP transport для remote MCPsrc/mcp/server.ts— регистрация MCP toolssrc/mcp/find-any-tour.ts— single-result MCP handlersrc/mcp/find-tour-options.ts— multi-result MCP handlersrc/eto-travel/search-service.ts— orchestration сценария поискаsrc/eto-travel/search-results-reader.ts— чтение нескольких карточек из выдачиsrc/eto-travel/tour-collection.ts— категоризацияbudget / optimal / premiumsrc/eto-travel/search-plan.ts— fallback-план ослабления фильтровsrc/eto-travel/tour-normalizer.ts— нормализация карточки и проверка релевантности направленияsrc/shared/query-parser.ts— нормализация направлений, месяцев и городов вылета
Примеры вызова инструментов
Один тур:
{
"tool": "find_any_tour",
"arguments": {
"destination": "Турция",
"departureCity": "Москва",
"adults": 2,
"nights": 7,
"month": "июнь",
"rawQuery": "Найди тур в Турцию из Москвы на июнь на 7 ночей для 2 взрослых"
}
}
Подборка:
{
"tool": "find_tour_options",
"arguments": {
"destination": "Турция",
"departureCity": "Москва",
"adults": 2,
"nights": 7,
"month": "июнь",
"rawQuery": "Подбери несколько туров в Турцию из Москвы на июнь на 7 ночей для 2 взрослых"
}
}
Пример ответа для подборки:
{
"tours": [
{
"category": "budget",
"title": "Budget Hotel",
"price": "90 000 RUB",
"url": "https://example.com/budget"
},
{
"category": "optimal",
"title": "Comfort Hotel",
"price": "115 000 RUB",
"url": "https://example.com/optimal"
},
{
"category": "premium",
"title": "Luxury Resort",
"price": "180 000 RUB",
"url": "https://example.com/premium"
}
]
}
Проверка и разработка
npm test
npm run lint
npm run typecheck
npm run build
Проверка через Inspector для локального режима:
npx @modelcontextprotocol/inspector node ./dist/index.js
Что стоит проверить:
- видны оба tools:
find_any_tourиfind_tour_options - single-tool возвращает одну карточку
- multi-tool возвращает подборку из нескольких карточек, если сайт их отдал
- при нехватке критичных данных сервер просит уточнение
- при нерелевантной карточке сервер не подменяет курорт другим регионом
Важные нюансы
- сайт
eto.travel/search/использует внешний виджетtourvisor.ru - стабильность зависит от DOM-структуры виджета и доступности стороннего контента
- итоговая ссылка на карточку может вести не на
eto.travel, а наtourcart.ru— это штатное поведение виджета - для долгих поисков у MCP-клиента лучше ставить
timeoutне меньше180000мс
Что публиковать на GitHub
Для публичной раздачи проекта достаточно оставить в репозитории:
- исходный код
Dockerfiledocker-compose.yml.env.examplenginx.mcp-eto-travel.confexamples/opencode.local.jsonexamples/opencode.remote.json
И не публиковать:
- временные IP
- приватные dev-домены
- локальные override-файлы под конкретный сервер
В таком виде любой пользователь сможет либо подключить локальный MCP, либо поднять remote MCP у себя почти без ручной доработки.






