Agentic Travel Recommendations Service
Agentic Travel Recommendations Service
Этот проект представляет собой прототип на TypeScript и Node.js для мультитенантного сервиса туристических рекомендаций. Он предоставляет общие возможности рекомендаций через REST API, потоковый HTTP MCP-эндпоинт и интерфейс командной строки.
Ключевые функции
REST API для проверки состояния, профилей участников и рекомендаций
Потоковый HTTP MCP-эндпоинт
MCP-инструмент:
get_member_profileMCP-инструмент:
get_recommendationsАвторитетное определение тенанта на основе профиля участника
Специфичные для партнёра лимиты рекомендаций
Специфичные для партнёра исключения категорий
Детерминированная генерация рекомендаций
Поведение fail-closed для конфигурации партнёра
Идентификаторы запросов и структурированные JSON-логи
Минимальная демонстрация CLI
Многоэтапная Docker-сборка
Автоматические тесты
Архитектура вкратце
REST / MCP / CLI
|
v
RecommendationService
|
v
MemberDataService
|
| member.partnerId
v
PartnerConfigurationService
|
v
CandidateGenerator
|
v
RecommendationPolicy
|
| exclusions then cap
v
Final RecommendationsВызывающие стороны предоставляют только memberId; они не выбирают авторитетный partnerId. Профиль участника определяет конфигурацию партнёра, а REST, MCP и CLI используют один и тот же бизнес-слой.
Быстрый старт
npm ci
npm run devСервис по умолчанию доступен по адресу http://localhost:3000.
Проверка типов
npm run typecheck
npm run typecheck:test
npm run typecheck:allТесты
npm testТекущая подтверждённая базовая версия — 46 успешных тестов в 6 файлах.
Производственная сборка
npm run build
npm startREST API
GET /health
GET /api/members/:memberId
GET /api/recommendations/:memberIdПримеры запросов:
curl http://localhost:3000/api/members/MEMBER-001
curl http://localhost:3000/api/recommendations/MEMBER-001MCP
MCP-сервер доступен через:
POST /mcpОн предоставляет следующие инструменты:
get_member_profileget_recommendations
Оба инструмента принимают только идентификатор участника:
{
"memberId": "MEMBER-001"
}Реализация использует официальный потоковый HTTP-транспорт из @modelcontextprotocol/sdk. Вызывающая сторона не передаёт partnerId; он определяется из авторитетного профиля участника.
CLI
npm run cli -- MEMBER-001Демонстрационные участники:
MEMBER-001→BANK_AMEMBER-002→BANK_BMEMBER-003→CREDIT_UNION_C
Docker
docker build -t agentic-travel-recommendations .
docker run --rm -p 3000:3000 agentic-travel-recommendationsОбраз использует многоэтапную сборку, среду выполнения Node 24 и непривилегированного пользователя времени выполнения. HTTP-сервер обрабатывает сигналы корректного завершения работы.
Раздел A — Архитектура и компромиссы
Обзор архитектуры
Сервис представляет собой приложение на TypeScript и Node.js без сохранения состояния, которое предоставляет один и тот же процесс рекомендаций через REST, потоковый HTTP MCP и CLI. Каждый транспорт проверяет входные данные и делегирует выполнение общему RecommendationService; обработчики транспорта не реализуют партнёрскую политику самостоятельно.
Авторитетный поток определения тенанта:
memberId
→ MemberDataService
→ MemberProfile.partnerId
→ PartnerConfigurationService
→ CandidateGenerator
→ RecommendationPolicy
→ final recommendationsВызывающие стороны передают memberId и никогда не выбирают авторитетный partnerId. Профиль участника, возвращаемый MemberDataService, определяет, какая конфигурация партнёра будет получена. Оба вышестоящих сервиса также сопоставляют идентичность, встроенную в ответ, с запрошенной идентичностью, а RecommendationService выполняет дополнительную проверку соответствия партнёра перед генерацией.
Генерация кандидатов намеренно не зависит от партнёрской политики. Детерминированный генератор сначала создаёт исходных кандидатов на основе профиля участника; затем общий слой политики удаляет кандидатов из excludedCategories и применяет recommendationCap — в таком порядке. Возвращаются только итоговые рекомендации. В этом прототипе сервис данных участников и сервис конфигурации партнёра являются моками, а доступ к конфигурации партнёра доступен только для чтения.
Проектные компромиссы
Приоритет корректности над доступностью. Если авторитетная конфигурация партнёра отсутствует, недоступна, не соответствует схеме или не совпадает по идентичности, запрос завершается ошибкой (fail-closed). Сервис не подставляет разрешающие значения по умолчанию и не возвращает рекомендации без ограничений. Это может снизить доступность при сбое вышестоящего сервиса, но предотвращает выход рекомендаций за пределы корректной политики тенанта.
Свежая конфигурация вместо кэширования. Первый релиз получает конфигурацию партнёра для каждого запроса рекомендаций, а не добавляет инфраструктуру кэша. Это сохраняет простоту поведения и гарантирует, что каждый успешный запрос использует актуальную политику. Это допускает дополнительную задержку и нагрузку на вышестоящий сервис; короткоживущий кэш станет уместен позже, только если измеренная производительность оправдает компромисс с согласованностью.
Детерминированная генерация вместо внешней LLM. Генерация кандидатов воспроизводима, тестируема, бесплатна и предсказуема в эксплуатации. Это ограничивает сложность персонализации, но упрощает проверку поведения политики и результатов оценки. В будущем компонент на основе LLM или ранжирования сможет заменить генерацию кандидатов без изменения детерминированного применения политики.
Обработка изменений конфигурации партнёра
Сервис конфигурации партнёра — зависимость, доступная только для чтения. Если партнёр изменит свой лимит рекомендаций с безлимитного на 3 или добавит cruise в excludedCategories, сервису рекомендаций не потребуется изменение кода или ветвление для конкретного тенанта. Следующий успешный запрос прочитает текущую конфигурацию, и общая логика политики применит новые значения исключений и лимита.
Пока конфигурация остаётся совместимой с существующей схемой, приложение не требует повторного развёртывания. Если позже будет введено кэширование конфигурации, оно должно иметь намеренно короткий TTL или надёжную стратегию инвалидации, поскольку устаревшая конфигурация может временно нарушить текущую политику партнёра.
Раздел B — Производственная готовность и реагирование на инциденты
Запись в runbook для инцидентов
Сценарий: Участник сообщает, что AI Concierge показал рекомендацию круиза, хотя партнёр этого участника исключает круизы.
Идентификация и сопоставление. Получите идентификатор запроса или корреляции из отчёта, если он доступен, и найдите соответствующие структурированные логи. Зафиксируйте
operation,memberId, авторитетныйpartnerIdпосле его определения,resultCodeи, где применимо, HTTP-статус. История поездок и полезная нагрузка рекомендаций намеренно не логируются, поэтому для корреляции используйте идентификаторы и метаданные результатов.Проверьте авторитетного тенанта. Получите затронутого участника через
MemberDataServiceи подтвердите, что запрошенныйmemberIdравен возвращённомуmember.memberId. Определяйте тенанта только изmember.partnerId. Не доверяйте идентификатору партнёра, переданному из фронтенда, MCP-вызова, параметра запроса или отчёта поддержки.Проверьте конфигурацию партнёра. Получите конфигурацию с использованием
member.partnerId, затем подтвердитеconfiguration.partnerId === member.partnerId. ИзучитеexcludedCategoriesиrecommendationCapи определите, исключён лиcruiseв текущей авторитетной конфигурации. Отсутствующая, недоступная, некорректная или не совпадающая по идентичности конфигурация должна приводить к закрытию сервиса с ошибкой (fail-closed), а не к использованию разрешающих значений по умолчанию.Воспроизведите конвейер. Прогоните участника через тот же процесс рекомендаций. Круиз в исходном выводе
CandidateGeneratorсам по себе не является дефектом, потому что генерация намеренно игнорирует партнёрскую политику. Убедитесь, чтоRecommendationPolicyобрабатываетraw candidates → remove excluded categories → apply recommendation cap → final recommendations, и подтвердите, что круизы отсутствуют в итоговом результате.Локализуйте место сбоя. Если круизы появляются в исходных кандидатах, но отсутствуют в итоговых рекомендациях, политика работает корректно. Исследуйте устаревший ответ клиента, ответ, связанный с неправильным участником, другого потребителя или эндпоинт, которые обходят ожидаемый процесс, или расхождение между временем сообщения и текущей конфигурацией. Если круиз проходит через
RecommendationPolicy, проверьте сравнение или нормализацию категорий, содержимое и идентичность авторитетной конфигурации, а также недавние изменения политики или регрессии.Сдерживание. Если корректную партнёрскую политику невозможно установить или безопасно воспроизвести, закрывайтесь с ошибкой (fail-closed), а не возвращайте потенциально несоответствующие рекомендации. Не пытайтесь изменять сервис конфигурации партнёра (доступный только для чтения) из этого приложения.
Исправьте и проверьте. Устраните дефект в ответственном слое и добавьте регрессионный тест, воспроизводящий точный сбой. Выполните:
npm run typecheck:all npm test npm run buildПроверьте затронутого партнёра, как минимум одного незатронутого тенанта, поведение REST и, при необходимости, поведение MCP.
Дальнейшие действия. Зафиксируйте корневую причину, затронутых партнёров и участников, окно воздействия, меры по исправлению, покрытие регрессионными тестами и профилактические действия.
Часть B2 — Обязательный вопрос для размышления
AI-ассистент для программирования вполне мог бы создать реализацию, которая проверяет записи участника и партнёра из вышестоящих сервисов с помощью Zod, но никогда не сопоставляет возвращённые идентичности с запрошенными. Код был бы типобезопасным, проверка схем и сценарии счастливого пути проходили бы, а поверхностная проверка увидела бы разумную защитную валидацию. Отсутствие инварианта кросс-тенантной проверки, тем не менее, создавало бы серьёзный риск нарушения политики.
Например, MEMBER-001 принадлежит BANK_A. RecommendationService запрашивает конфигурацию BANK_A, но ошибочный или неправильно маршрутизированный вышестоящий сервис возвращает полностью валидную по схеме конфигурацию BANK_B с безлимитным лимитом и без исключений категорий. Zod корректно принимает её форму, но применение этой политики к MEMBER-001 может обойти ограничения BANK_A.
Я бы выявил это с помощью состязательного регрессионного теста: запросить BANK_A, пока намеренно некорректный двойник сервиса конфигурации возвращает валидную конфигурацию BANK_B. Я бы ожидал InvalidUpstreamDataError, проверил, что результат рекомендаций не создаётся, и явно убедился, что CandidateGenerator.generate не вызывался. Я бы добавил соответствующий тест сервиса данных участников, доказывающий, что запрошенный memberId должен равняться возвращённому member.memberId.
Прежде чем действовать на основе сгенерированного ИИ кода, я бы проследил источник власти и порядок выполнения, а не полагался только на типы. Я бы проверил, что тенанта выбирает member.partnerId, а не ввод вызывающей стороны; что обе возвращённые идентичности соответствуют своим авторитетным запросам; и что отсутствующая, недоступная или несовпадающая конфигурация приводит к закрытию с ошибкой (fail-closed) без разрешающего запасного варианта. Я бы также подтвердил, что генерация не может начаться до того, как идентичность политики безопасно установлена, и что негативные состязательные тесты покрывают эти случаи наряду с обычными сценариями счастливого пути.
Раздел C — Журнал использования ИИ
Взаимодействие 1 — Обзор архитектуры
Что я спросил
Я попросил AI-ассистента для программирования рассмотреть задачу и помочь спроектировать минимальную архитектуру, которую один инженер мог бы реалистично реализовать. Запрошенный объём включал доменные модели, моки вышестоящих сервисов, логику рекомендаций, REST, MCP, CLI, автоматизированное тестирование и контейнеризацию, при этом избегая инфраструктуры, не требовавшейся для прототипа.
Что предоставил ИИ
Он предложил разделить доменные модели, контракты сервисов, моки вышестоящих сервисов, генерацию кандидатов, партнёрскую политику, оркестрацию и транспортные адаптеры. Изначально он предложил stdio как самый простой MCP-транспорт.
Что я сохранил, изменил или отклонил
Я сохранил многослойное разделение, потому что оно позволяет REST, MCP и CLI вызывать один бизнес-слой вместо независимой реализации правил. Я отклонил stdio как основной MCP-транспорт и перенаправил дизайн на потоковый HTTP-транспорт официального MCP SDK по адресу POST /mcp. Задание описывает внутренний API, который агенты должны обнаруживать и вызывать, а HTTP соответствует контейнеризованной сервисной архитектуре. Я сделал этот выбор после сравнения предложения с требованиями интеграции из задания, а не автоматически приняв самый простой вариант.
Взаимодействие 2 — Инкрементальная реализация
Что я спросил
Я не просил создать всё приложение одним запросом. Я разделил реализацию на ограниченные шаги: доменные модели, схемы и ошибки, контракты и моки вышестоящих сервисов, генерация кандидатов, политика рекомендаций, оркестрация, REST, MCP, CLI, наблюдаемость и Docker. После каждого шага я проверял заявленное поведение и требовал проверки типов и тестов перед продолжением.
Что предоставил ИИ
Ассистент реализовал каждый ограниченный компонент с целевыми тестами и сообщал об изменённых файлах и результатах проверки. Это сделало отдельные проектные решения видимыми и доступными для проверки, а не спрятанными внутри большого сгенерированного патча.
Что я сохранил, изменил или отклонил
Я сохранил общий RecommendationService, детерминированный CandidateGenerator, отдельный RecommendationPolicy, определение тенанта на основе участника, контракт конфигурации только для чтения и общую бизнес-логику REST/MCP/CLI. Такая структура делает тенантную политику независимо тестируемой и предотвращает реализации правил, зависящие от транспорта. Я также намеренно сохранил детерминированную генерацию вместо добавления внешней зависимости от LLM. Оценка сосредоточена на проектировании сервиса и соблюдении политики, а воспроизводимый вывод легче тестировать, отлаживать и демонстрировать. Каждый инкремент принимался только после того, как его поведение соответствовало архитектурным инвариантам и проверки проходили.
Взаимодействие 3 — Аудит продакшена и безопасности
Что я спросил
Когда приложение заработало, я попросил ИИ перестать добавлять функции и провести аудит репозитория с точки зрения старшего инженера, рецензента безопасности мультитенантных систем, дежурного владельца продакшена и рецензента REST/MCP API.
Что предоставил ИИ
Аудит показал, что ответы вышестоящих систем, валидные по схеме, изначально не соотносились с запрошенной идентичностью участника или партнёра. Также было обнаружено, что некорректный JSON мог приводить к сбою до того, как промежуточное ПО request-ID установило контекст запроса. Были предложены дополнительные улучшения с более низким приоритетом.
Что я сохранил, изменил или отклонил
Я принял оба ценных вывода, поскольку они влияли на корректность тенанта и безопасную эксплуатацию. Для корреляции идентичности реализация теперь проверяет, что возвращённый memberId соответствует запрошенному участнику, что configuration.partnerId соответствует авторитетному member.partnerId, и что RecommendationService защитно повторяет проверку идентичности конфигурации. Несовпадения завершаются отказом (fail closed), а негативные регрессионные тесты проверяют, что генерация кандидатов никогда не начинается, когда авторитетную конфигурацию невозможно установить.
Для некорректного JSON контекст запроса и ID запроса теперь устанавливаются до разбора. Недопустимые тела получают безопасный структурированный ответ 400 без деталей парсера, стектрейсов, путей файловой системы или исходного содержимого запроса.
Я отложил идеи с более низким приоритетом, такие как постоянные MCP-сессии, дополнительная распределённая инфраструктура и более продвинутая наблюдаемость, поскольку они были не нужны для четырёхнедельного прототипа и увеличили бы эксплуатационный объём. Я оценивал каждую рекомендацию с учётом требований задания, корректности тенанта, тестируемости, операционного риска и объёма поставки. Ассистент предлагал варианты и помогал с реализацией, но я проверял обоснование, выбирал изменения и подтверждал их с помощью целенаправленных тестов и сквозных проверок.
Первый четырёхнедельный этап
Что поставляется первым
Цель на четыре недели — первый поставляемый внутренний прототип. Он демонстрирует требуемый рабочий процесс с безопасным применением тенантной политики и основами эксплуатации; это не заявление о том, что все возможности, необходимые для широкого развёртывания в продакшене, полностью реализованы.
Неделя 1 — Фундамент сервиса
Заложить фундамент сервиса на TypeScript и Node.js.
Определить доменные модели, строгую проверку границ с помощью Zod и типизированные ошибки.
Добавить контракт
MemberDataServiceи мок-реализацию.Добавить контракт
PartnerConfigurationServiceтолько для чтения и мок-реализацию.Установить авторитетное определение тенанта на основе участника и первоначальную основу для модульных тестов.
Цель: Установить безопасные границы сервиса и авторитет тенанта до реализации логики рекомендаций.
Неделя 2 — Рабочий процесс рекомендаций
Реализовать детерминированный
CandidateGeneratorнезависимо от правил партнёров.Реализовать
RecommendationPolicy, включая исключения категорий и ограничения количества рекомендаций.Обеспечить требуемый порядок: сначала исключения, затем ограничение.
Добавить оркестрацию
RecommendationServiceи поведение конфигурации с отказом по умолчанию (fail closed).Покрыть политику и оркестрацию целенаправленными модульными тестами.
Цель: Доказать, что контрактные правила партнёров детерминированы и независимы от генерации кандидатов.
Неделя 3 — Интерфейсы и сквозной поток
Открыть REST-эндпоинты и конечную точку Streamable HTTP MCP.
Предоставить MCP-инструменты
get_member_profileиget_recommendations.Добавить демонстрацию CLI.
Направить REST, MCP и CLI через общий бизнес-слой.
Добавить интеграционные тесты REST/MCP и тесты переопределения тенанта.
Цель: Продемонстрировать полный процесс рекомендаций через интерфейсы, требуемые заданием.
Неделя 4 — Готовность к продакшену и поставка
Добавить ID запросов, поля корреляции, структурированное JSON-логирование и безопасную обработку ошибок.
Безопасно обрабатывать некорректный JSON и реализовать корректное завершение работы.
Добавить многоступенчатую сборку Docker и запуск от непривилегированного пользователя.
Выполнять проверку типов продакшен-исходников и тестов отдельно.
Провести аудит продакшена и безопасности и добавить негативные тесты корреляции идентичности.
Выполнить финальную сквозную проверку и проверку контейнера.
Подготовить README, инструкцию по инцидентам и демонстрационное видео.
Цель: Сделать прототип поддерживаемым командой, которая отвечает за него на дежурстве.
Что появится позже
Следующая работа намеренно отложена до завершения первого четырёхнедельного релиза:
Реальные интеграции с вышестоящими системами. Заменить мок-реализации
MemberDataServiceиPartnerConfigurationServiceреальными REST-клиентами arrivia, сохранив существующие контракты сервисов и инварианты корреляции идентичности.Интеграция с существующей аутентификацией и авторизацией. Интегрироваться с существующими механизмами идентичности и шлюза arrivia, а не внедрять новую платформу идентичности. Авторизация должна сохранять авторитет тенанта, определяемый на основе участника.
Устойчивость сети. Для реальных внешних HTTP-зависимостей проверить и настроить таймауты запросов и соединений, ограниченные повторные попытки там, где операции безопасно повторять, и явное поведение при сбоях. Неопределённость конфигурации должна по-прежнему завершаться отказом (fail closed).
Проверка производительности. Выполнить реалистичные нагрузочные тесты и тесты производительности перед оптимизацией. Рассматривать кратковременное кэширование конфигурации партнёра только при наличии измерений, которые это оправдывают. Устаревшая политика — это риск для корректности, поэтому любое кэширование требует чёткой стратегии свежести и инвалидации.
Интеллект рекомендаций. Потенциально заменить или дополнить детерминированный
CandidateGeneratorLLM, моделью ранжирования или более богатой персонализацией.RecommendationPolicyдолжен оставаться детерминированным и находиться вне модели, чтобы сгенерированный вывод не мог переопределять правила партнёра.Производственная наблюдаемость. Подключить существующие структурированные события и ID корреляции к одобренным arrivia метрикам, трассировке, оповещению и эксплуатационным инструментам.
Эволюция MCP. Рассматривать MCP с сохранением состояния или возобновляемый MCP только тогда, когда конкретное требование продукта нуждается в состоянии между запросами. Текущая реализация Streamable HTTP без сохранения состояния является намеренной для этого сервиса.
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 Connectors
Hotel booking MCP server. Search, book, and manage reservations across 250K+ properties worldwide.
AI marketplace — flights, tours, activities, transport & more via MCP. No auth required.
MCP server exposing the Backtest360 engine API as tools for AI agents.
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/Varma904/agentic-travel-recommendations'
If you have feedback or need assistance with the MCP directory API, please join our Discord server