Skip to main content
Glama
sholts2026

flexible-hotels-mcp-server

by sholts2026

flexible-hotels-mcp-server

一个 MCP(Model Context Protocol)服务器,让 AI(Claude 或任何其他 MCP 客户端)能够按灵活日期范围内的住宿晚数搜索酒店房间,而不是按固定的入住/退房日期。

例如:"特拉维夫 3 晚,9 月 1 日到 9 月 20 日之间的某个时间"——服务器会扫描该范围内所有可能的入住日期,为每个日期获取真实价格,并返回按价格排序的最优惠交易。

联盟营销模式:这只是一个比价工具——它不收集任何付款信息,也不执行预订。每条结果都包含一个链接(bookingUrl),指向用户可自行完成购买(如果愿意的话)的来源(目前是 Booking.com)。

数据来源:Amadeus for Developers——旅游领域领先的免费 API,提供完全免费的 test(沙盒)环境,无需信用卡。

这里有什么

四个暴露给 AI 的工具(tools):

工具

功能

flexible_hotels_resolve_city_code

将自由输入的城市名称("特拉维夫")转换为 IATA 代码("TLV")

flexible_hotels_list_hotels_in_city

返回指定城市中的酒店列表(及 ID)

flexible_hotels_search_flexible_offers

核心工具——按日期范围内的晚数进行灵活搜索,每条结果附带预订链接

flexible_hotels_get_offer_details

特定交易的完整详情和最新价格 + 预订链接

"灵活搜索"的逻辑实现在 src/services/hotelSearch.ts 中:由于 Amadeus(以及任何其他供应商)每次调用只支持一个固定日期范围,服务器自行扫描所定义窗口内的每个可能的入住日期,相应地调用 API,并汇总结果。每条结果的链接在 src/services/affiliateLink.ts 中构建。

Related MCP server: Amadeus Hotel API MCP Server

安装

1. 获取免费 API 密钥(2 分钟,无需信用卡):

  1. 在 https://developers.amadeus.com/register 注册

  2. 验证后,在 My Self-Service Workspace 中创建 "New App"

  3. 您将获得 API Key 和 API Secret——它们将作为 AMADEUS_CLIENT_ID 和 AMADEUS_CLIENT_SECRET

这是 test(沙盒)环境——完全免费,每月配额充足,速率限制约为每秒 10 个请求。它覆盖全球数十万家酒店,数据真实(尽管有时是部分/缓存的)。未来如需切换到完整实时数据,可向 Amadeus 申请 production 访问权限(付费)并将 AMADEUS_ENV=production。

2. 本地安装:

npm install
cp .env.example .env
# ערכו את .env והכניסו את המפתחות שקיבלתם
npm run build

3. 验证一切正常(不消耗配额——不向 Amadeus 发送真实请求):

npm test

运行

npm start

默认情况下,服务器通过 stdio 运行(适合本地连接到 Claude Desktop 等客户端)。要以 HTTP 远程服务器方式运行(用于从 Claude.ai / Cowork 作为"custom connector"连接,或从任何其他远程 MCP 客户端连接):

TRANSPORT=http PORT=3000 npm start

作为"插件"连接到 AI

Claude Desktop(本地)

在配置文件 claude_desktop_config.json 中添加:

{
  "mcpServers": {
    "flexible-hotels": {
      "command": "node",
      "args": ["/נתיב-מלא/ל/flexible-hotels-mcp-server/dist/index.js"],
      "env": {
        "AMADEUS_CLIENT_ID": "המפתח-שלכם",
        "AMADEUS_CLIENT_SECRET": "הסוד-שלכם"
      }
    }
  }
}

重启 Claude Desktop 后,AI 将能够在对话中直接调用这些工具。

远程连接(Claude.ai / Cowork / 任何其他客户端)

要使产品作为"插件"可供多个用户使用,而无需每个人在本地运行代码,需要在某个服务器/主机上以 TRANSPORT=http 模式部署服务器,然后在客户端设置中将 https://您的域名/mcp 地址连接为 custom MCP connector。

Railway(如果您已有账户 + 已安装 CLI,推荐):

cd flexible-hotels-mcp-server
railway login          # רק אם עוד לא מחוברים — פותח דפדפן לאישור
railway up --new -y --detach --json   # יוצר פרויקט חדש, מעלה את הקוד ומפעיל build מה-Dockerfile
railway variable set AMADEUS_CLIENT_ID=המפתח-שלכם --skip-deploys
railway variable set AMADEUS_CLIENT_SECRET=הסוד-שלכם --skip-deploys
railway variable set AMADEUS_ENV=test
railway domain          # מפיק כתובת ציבורית כמו https://xxx.up.railway.app

服务器在设置 Amadeus 密钥之前也能启动(并通过健康检查)——搜索工具只会返回明确的错误,直到设置好真实密钥并自动重新部署。

Render: 项目根目录中的 render.yaml 文件已准备好作为 Blueprint——将 repo 连接到 Render,"New" -> "Blueprint",并在界面中填写 AMADEUS_CLIENT_ID/AMADEUS_CLIENT_SECRET(参见文件内的注释)。

请注意所有选项:AMADEUS_CLIENT_ID/AMADEUS_CLIENT_SECRET 应该是托管服务器上定义的环境变量,绝不能写在代码中。

使用示例(AI"看到"的内容)

User: תמצא לי 3 לילות בתל אביב, איפשהו בין ה-1 ל-20 בספטמבר, לזוג.

AI calls: flexible_hotels_resolve_city_code(keyword="Tel Aviv")
  -> TLV

AI calls: flexible_hotels_search_flexible_offers(
  city_code="TLV", nights=3,
  earliest_check_in="2026-09-01", latest_check_in="2026-09-20",
  adults=2
)
  -> רשימת עסקאות ממוינת מהזולה ביותר, עם תאריך צ'ק-אין/אאוט מדויק וקישור הזמנה לכל אחת

User: מעולה, תראה לי את הפרטים המלאים של האופציה הכי זולה.

AI calls: flexible_hotels_get_offer_details(offer_id="...")
  -> מחיר מעודכן + קישור לחיצה שמעביר את המשתמש להשלים את ההזמנה באתר המקורי

商业模式:联盟营销

该服务器不执行预订,也不在任何阶段接触付款信息——这是有意为之,既出于简单/安全考虑,也因为应用商店(如 ChatGPT App Directory)要求"external checkout"并禁止通过工具本身收集信用卡号。

实际运作方式:

  1. 服务器通过 Amadeus(免费)找到真实价格。

  2. 为每条结果构建一个 Booking.com 搜索页面链接,酒店名称和日期已预先填好(src/services/affiliateLink.ts)。

  3. 如果您注册 Booking.com Affiliate Partner Program(免费,自主注册,无需特别批准)并获得 aid,可以将其设置为环境变量 BOOKING_AFFILIATE_ID——这样每个链接都会包含您的合作伙伴 ID,您就可以从通过该链接完成的预订中获得佣金。

  4. 没有 BOOKING_AFFILIATE_ID 时链接仍然完全可用——只是没有佣金。

这正是您要求的"只需注册":一次性注册 Booking.com 合作伙伴计划(不是 Amadeus,不是 Railway)就是让链接开始赚钱所需的全部。

已知限制

  • 免费配额:Amadeus 的 test 环境有月度配额和请求速率限制(约 10 次/秒)。对 N 天窗口的灵活搜索最多发送 N 个请求(之间有轻微延迟)——窗口越大 = 搜索越慢。每次搜索的窗口限制为 30 天以保护配额。

  • 酒店数量:默认情况下,每个日期最多检查城市中的 15 家酒店(可通过 max_hotels 提高到 30,或缩小到特定的 hotelIds)。

  • 沙盒数据:在 test 环境中,大城市(如 NYC、LON、PAR)的覆盖最可靠。较小的城市可能返回很少或没有酒店——切换到 production 即可解决。

  • 预订链接 根据酒店名称和日期构建 Booking.com 搜索——不是直接指向特定房间的深层链接,因为 Amadeus 不提供这种链接。在大多数情况下,相关酒店会出现在搜索结果的第一位。

项目结构

src/
├── index.ts              # נקודת כניסה, רישום השרת והכלים
├── constants.ts           # קבועים (URLs, מגבלות)
├── types.ts                # טיפוסי TypeScript משותפים
├── services/
│   ├── amadeusClient.ts   # קליינט HTTP מאומת ל-Amadeus (OAuth2, endpoints)
│   ├── hotelSearch.ts     # הלוגיקה של חיפוש גמיש (הליבה)
│   ├── affiliateLink.ts   # בניית קישור ההזמנה (Booking.com + aid אופציונלי)
│   └── format.ts           # פורמט markdown לתשובות
├── schemas/
│   └── schemas.ts          # סכמות Zod לכל כלי
├── tools/                  # רישום כל כלי MCP
└── test/
    └── hotelSearch.test.ts # בדיקות יחידה ללוגיקת החיפוש (ללא צורך במפתח API)
scripts/
└── smoke-test-tools.mjs    # בדיקת עשן שמפעילה את השרת האמיתי ומוודאת שכל הכלים נחשפים כראוי

未来可能的扩展

  • 灵活的晚数范围(最小/最大晚数),而不仅仅是固定数字。

  • 搜索结果的缓存以节省配额。

  • 加入更多联盟计划(Expedia、Agoda 等),为每条结果显示多个不同来源的链接,而不仅仅是 Booking.com。

  • 当产品增长时,切换到付费供应商(如 StayAPI / RapidAPI)以获得更广泛的供应商覆盖。

  • 在 ChatGPT 应用商店(Apps SDK)中注册——一个独立项目,有自己的要求(OpenAI 开发者账户、隐私政策、用于测试的演示账户);当前的联盟模式(不收集信用卡)已经符合他们的"external checkout"要求。

Available Tools

4 tools
flexible_hotels_get_offer_detailsGet Hotel Offer DetailsA
Read-onlyIdempotent

Fetch full, up-to-date details for a single hotel offer previously returned by search_flexible_hotel_offers, including its cancellation policy.

Offer prices can shift between search and booking, so call this before telling the user a final price or sending them to book. Like the rest of this server, it never collects payment details — it only reads offer details and returns a click-through link to the real site.

Args:

  • offer_id (string): the offerId returned by search_flexible_hotel_offers

  • response_format ('markdown' | 'json'): output format (default markdown)

Returns: hotel name, confirmed price, check-in/check-out dates, room description, board type, cancellation deadline, and a booking link to the real site.

Examples:

  • Use when: the user picked one result from search_flexible_hotel_offers and wants to double-check the price before going to book

  • Don't use when: you don't have an offer_id yet — run search_flexible_hotel_offers first

Error Handling:

  • Returns "No details found for this offer id" if the offer expired (Amadeus test-environment offers are short-lived)

ParametersJSON Schema
NameRequiredDescriptionDefault
offer_idYesThe Amadeus offer id returned by search_flexible_hotel_offers, e.g. 'XYZ123'
response_formatNoOutput format: 'markdown' for human-readable or 'json' for machine-readablemarkdown

TDQS

A4.6/5.0
Behavior5/5

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

Annotations cover readOnlyHint, openWorldHint, idempotentHint, and destructiveHint. The description goes beyond these by disclosing that prices may shift, that it never collects payment details, that it returns a click-through link, and that expired offers produce a specific error message. This provides rich behavioral context that annotations alone do not offer, and it does not contradict any annotation.

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

Conciseness4/5

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

The description is well-structured with clear sections (main description, Args, Returns, Examples, Error Handling) and is front-loaded with the core purpose. It is somewhat repetitive of the input schema (the Args section duplicates schema descriptions), but each section carries useful information and there is no fluff. A slightly tighter version could omit the repetition, but overall it is appropriately concise for the tool's complexity.

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

Completeness5/5

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

With no output schema, the description compensates by listing the exact fields returned (hotel name, price, dates, room, board, cancellation deadline, booking link). It also covers error handling and usage context. For a single-offer detail tool with read-only semantics, nothing essential is missing for an agent to call it correctly.

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?

Schema coverage is 100%, so the schema already fully documents both offer_id and response_format, including their types, defaults, and descriptions. The description repeats the same info (e.g., 'offerId returned by search_flexible_hotel_offers') without adding new meaning beyond what the schema provides. The expiry note appears in error handling, not in parameter semantics, so the description adds marginal value over the schema.

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 states a specific verb ('fetch'), a precise resource ('full, up-to-date details for a single hotel offer previously returned by search_flexible_hotel_offers'), and explicitly ties it to a sibling search tool. This clearly differentiates it from the siblings (resolve_city_code, list_hotels_in_city, search_flexible_offers), so an agent knows exactly what this tool does and when to use it.

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 gives explicit 'Use when' and 'Don't use when' examples, including a concrete scenario (double-check price before booking) and a negative condition (no offer_id yet -> run search first). It also states the timing requirement ('call this before telling the user a final price or sending them to book'), leaving no ambiguity about when to invoke this tool.

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

flexible_hotels_list_hotels_in_cityList Hotels in a CityA
Read-onlyIdempotent

List hotels (with their Amadeus hotelId) located in a given IATA city code, optionally within a radius.

Use this to discover hotelIds you can pass into search_flexible_hotel_offers via hotel_ids, e.g. to restrict a search to a specific neighborhood or a shortlist of hotels the user already mentioned by name. It does NOT return prices or availability — only hotel identity/location. This tool does not modify any data.

Args:

  • city_code (string): 3-letter IATA city code, e.g. "TLV", "PAR" (use resolve_city_code to find it)

  • radius_km (number): search radius around the city center in km, 1-300 (default 20)

  • max_results (number): max hotels to return, 1-30 (default 20)

  • response_format ('markdown' | 'json'): output format (default markdown)

Returns: hotel name, hotelId, chain code, and coordinates for each matching hotel.

Examples:

  • Use when: "only search hotels near the Tel Aviv beachfront" -> narrow radius_km, then pass returned hotelIds to search_flexible_hotel_offers

  • Don't use when: you just want priced offers — use search_flexible_hotel_offers directly, it looks up hotels automatically

Error Handling:

  • Returns "No hotels found for city code ..." if the city_code is invalid or has no coverage in this environment

ParametersJSON Schema
NameRequiredDescriptionDefault
city_codeYesIATA city code, e.g. 'TLV' for Tel Aviv, 'PAR' for Paris (use resolve_city_code to find it)
radius_kmNoSearch radius around the city center, in kilometers
max_resultsNoMaximum hotels to return
response_formatNoOutput format: 'markdown' for human-readable or 'json' for machine-readablemarkdown

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnlyHint=true, idempotentHint=true, destructiveHint=false), so the bar is lower. The description adds genuine value beyond annotations: it discloses that the tool does NOT return prices/availability, states 'This tool does not modify any data' (consistent with destructiveHint=false), and documents the exact error message for invalid city codes. There is no contradiction with annotations.

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

Conciseness4/5

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

The description is organized with clear section headers (Args, Returns, Examples, Error Handling) and is front-loaded with the core purpose. Every section earns its place — the examples, error handling, and return description all add operational value. Slightly longer than minimal, but the structure makes it scannable, and no sentence is wasted.

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 tool is simple (4 params, 1 required, all schema-documented, no nested objects, no output schema) with rich annotations. The description covers what's returned (hotel name, hotelId, chain code, coordinates), error handling, the chaining pattern with search_flexible_hotel_offers, and when not to use it. An agent has everything needed to call it correctly — including awareness that no output schema exists but the return content is described.

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?

Schema description coverage is 100%, so baseline is 3. The description largely mirrors the schema (e.g., radius range 1-300, default 20, response_format enum). It adds modest value by embedding usage context in the examples (e.g., 'narrow radius_km' for a beachfront search, 'use resolve_city_code to find it' — though that hint also exists in the schema). The description doesn't introduce meaning beyond what the schema provides, so 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 states a precise verb-resource-scope combination: 'List hotels (with their Amadeus hotelId) located in a given IATA city code, optionally within a radius.' It explicitly differentiates itself from search_flexible_hotel_offers by clarifying it returns only hotel identity/location, NOT prices or availability. An agent can distinguish this from all three siblings without opening their schemas.

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 names the exact alternative (search_flexible_hotel_offers), the chaining pattern (pass returned hotelIds into it), and provides concrete 'Use when' / 'Don't use when' examples. It even shows how to narrow radius_km for neighborhood-specific searches. Nothing about when to invoke this tool is left to inference.

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

flexible_hotels_resolve_city_codeResolve City Name to IATA City CodeA
Read-onlyIdempotent

Look up the 3-letter IATA city code for a free-text city name (e.g. "Tel Aviv" -> "TLV", "Paris" -> "PAR").

Every other tool in this server (list_hotels_in_city, search_flexible_hotel_offers) requires an IATA city_code, so this is normally the first tool to call when the user names a city.

Args:

  • keyword (string): city name or the start of it, e.g. "Tel Aviv", "New York", "Par"

  • max_results (number): max cities to return, 1-20 (default 10)

  • response_format ('markdown' | 'json'): output format (default markdown)

Returns: matching cities with their name, iataCode, and country code.

Examples:

  • Use when: "find flexible hotel deals in Tel Aviv" -> call with keyword="Tel Aviv" first to get city_code="TLV"

  • Don't use when: you already have a 3-letter IATA city code

Error Handling:

  • Returns "No cities found matching ..." if the keyword doesn't match anything

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordYesFree-text city name or the start of it, e.g. 'Tel Aviv', 'Par', 'New York'
max_resultsNoMaximum cities to return
response_formatNoOutput format: 'markdown' for human-readable or 'json' for machine-readablemarkdown

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds beyond these by specifying the return format (name, iataCode, country code), the exact error message ('No cities found matching ...'), and clarifies it is a pure lookup with no side effects. No contradiction with annotations.

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

Conciseness4/5

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

The description is well-structured and front-loaded with purpose and usage context. It includes a clear one-liner, a brief explanation of why it's used first, parameter descriptions (somewhat redundant with schema), returns, examples, and error handling. It is slightly lengthy due to repeating the Args section, but every section earns its place and the structure aids comprehension.

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

Completeness5/5

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

For a simple lookup tool with three parameters, no output schema, and strong annotation coverage, this description covers all necessary aspects: purpose, when to use, examples, return format, and error handling. An agent can invoke it correctly without ambiguity, making it complete for its complexity.

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?

Schema description coverage is 100% with all three parameters fully documented. The tool description repeats parameter details (e.g., max_results range and default, response_format enum) but adds no new semantic meaning beyond what the schema already provides. It reinforces usage examples but does not introduce additional information, so the baseline of 3 applies.

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 states a specific action and resource: 'Look up the 3-letter IATA city code for a free-text city name,' with concrete examples (Tel Aviv -> TLV, Paris -> PAR). It clearly distinguishes itself from sibling tools by explaining that all other tools require an IATA city_code, making this the prerequisite lookup tool.

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 states when to use it ('normally the first tool to call when the user names a city') and when not to use it ('Don't use when: you already have a 3-letter IATA city code'). It names dependent tools (list_hotels_in_city, search_flexible_hotel_offers) and provides a concrete example of usage context.

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

flexible_hotels_search_flexible_offersSearch Hotel Offers by Number of Nights (Flexible Dates)A
Read-only

Find the cheapest hotel offers for a fixed NUMBER OF NIGHTS across a WINDOW of possible check-in dates, instead of a single fixed check-in/check-out date pair. This is the core tool of this server.

For example: "3 nights in Tel Aviv, sometime between Sep 1 and Sep 20" is expressed as nights=3, earliest_check_in="2026-09-01", latest_check_in="2026-09-20". The tool scans every possible check-in date in that window (each implying checkout = check-in + nights), fetches real priced offers for each, and returns the cheapest options found, sorted by price — so the caller can see which exact dates are the best deal.

This does NOT search by a single fixed date — for that, just set earliest_check_in = latest_check_in. This is an AFFILIATE search tool: it never collects payment details or creates a booking. Each returned offer includes a bookingUrl — a link to the real hotel/OTA listing where the guest can complete the purchase themselves, if they choose to. Data comes from the Amadeus for Developers hotel API (free test/sandbox environment by default).

Args:

  • city_code (string): 3-letter IATA city code, e.g. "TLV" (use resolve_city_code to find it from a name)

  • nights (number): exact stay length in nights, 1-28

  • earliest_check_in / latest_check_in (YYYY-MM-DD): the flexible window of possible check-in dates, at most 30 days apart

  • hotel_ids (string[], optional): restrict to specific Amadeus hotelIds (from list_hotels_in_city)

  • max_hotels (number): if hotel_ids is omitted, how many hotels in the city to auto-check (default 15)

  • adults (number): guests per room (default 2)

  • room_quantity (number): rooms to book (default 1)

  • currency (string, optional): 3-letter ISO currency code, e.g. "USD"

  • max_results (number): how many offers to return, cheapest first (default 10)

  • response_format ('markdown' | 'json'): output format (default markdown)

Returns: For JSON format: { "cityCode": string, "nights": number, "earliestCheckIn": string, "latestCheckIn": string, "datesScanned": number, "datesWithOffers": number, "datesSkipped": [{ "date": string, "reason": string }], "hotelsConsidered": number, "offers": [{ "offerId", "hotelId", "hotelName", "checkInDate", "checkOutDate", "nights", "currency", "totalPrice", "boardType", "roomDescription", "bookingUrl" }, ...], "cheapest": <same shape as one offer, or null>, "truncated": boolean }

Examples:

  • Use when: "find me the cheapest 3-night stay in Paris sometime in the next month"

  • Use when: "is it cheaper to go for a long weekend early or late September?"

  • Don't use when: the user already has exact fixed dates in mind — a single-date search is faster (set earliest_check_in = latest_check_in), though this tool still works for that case.

Error Handling:

  • Returns an error if the date window exceeds 30 days — narrow it and retry

  • Individual dates that error out (e.g. sandbox has no data) are listed in datesSkipped rather than failing the whole search

ParametersJSON Schema
NameRequiredDescriptionDefault
adultsNoNumber of adult guests per room
nightsYesExact number of consecutive nights the guest wants to stay, e.g. 3
currencyNoOptional 3-letter ISO currency code to request prices in, e.g. 'USD', 'EUR', 'ILS'
city_codeYesIATA city code to search in, e.g. 'TLV' for Tel Aviv (use resolve_city_code to find it from a name)
hotel_idsNoOptional list of specific Amadeus hotelIds to restrict the search to (from list_hotels_in_city). If omitted, the tool automatically looks up hotels in city_code and checks up to max_hotels of them.
max_hotelsNoWhen hotel_ids is not provided, how many hotels in the city to check (higher = more thorough but slower, capped at 30)
max_resultsNoMaximum number of offers to return, sorted cheapest first
room_quantityNoNumber of rooms to book
latest_check_inYesLatest allowed check-in date, YYYY-MM-DD. Must be no more than 30 days after earliest_check_in.
response_formatNoOutput format: 'markdown' for human-readable or 'json' for machine-readablemarkdown
earliest_check_inYesEarliest allowed check-in date, YYYY-MM-DD. The search scans every possible check-in date from this date through latest_check_in.

TDQS

A4.9/5.0
Behavior5/5

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

While annotations already declare readOnlyHint and destructiveHint=false, the description enriches the behavioral profile substantially. It discloses an affiliate-search model ('never collects payment details or creates a booking'), the external bookingUrl flow, the Amadeus sandbox data source, per-date error handling via datesSkipped, and the scanning of every check-in date. These details go well beyond the annotations and materially shape an agent's expectations for side effects and response quirks. No contradiction with annotations; readOnlyHint aligns with the stated no-booking behavior.

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 long but unusually well-organized. It front-loads the core concept and the key differentiator (flexible window vs fixed dates) in the opening sentences, then flows logically through an example, non-use cases, affiliate disclaimer, parameter list, return shape, usage examples, and error handling. Every section earns its place: the return JSON is especially valuable given the lack of an output schema, and the error-handling note covers edge cases an agent would otherwise discover only at runtime. No redundant filler; the length is justified by the tool's complexity.

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

Completeness5/5

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

For an 11-parameter tool with no output schema, the description is exceptionally complete. It specifies the exact JSON return structure, including datesSkipped with reason fields and the cheapest summary object, which an agent needs to parse results reliably. It covers error conditions (window >30 days, per-date failures), explains the scanning algorithm, and provides two representative use cases. Combined with full schema parameter descriptions and annotations, nothing required to correctly invoke and interpret this tool is missing.

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 coverage is 100%, so each parameter is already documented. The description adds value by explaining the conceptual relationship between earliest_check_in and latest_check_in as a 'window' with a worked example ('3 nights in Tel Aviv... is expressed as nights=3, earliest_check_in="2026-09-01", latest_check_in="2026-09-20"'), and by clarifying default behaviors (max_hotels auto-check, response_format selection). These additions go beyond simple schema repetition, though the marginal gain is modest since the schema descriptions already cover the same facts. A solid 4, not a 5 because the added insight is illustrative rather than essential.

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 opens with a precise statement: 'Find the cheapest hotel offers for a fixed NUMBER OF NIGHTS across a WINDOW of possible check-in dates.' It specifies the resource (hotel offers), the action (find cheapest), and the distinctive scope (flexible dates rather than fixed). It also explicitly positions itself as 'the core tool of this server' and contrasts with sibling tools such as list_hotels_in_city and resolve_city_code, making its role unmistakable. No ambiguity.

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?

Usage guidance is explicit and actionable. It provides concrete when-to-use examples ('find me the cheapest 3-night stay in Paris sometime in the next month') and a clear don't-use case ('Don't use when: the user already has exact fixed dates in mind'), followed by the recommended alternative (set earliest_check_in = latest_check_in). It also points to companion tools (resolve_city_code, list_hotels_in_city) for prerequisites, making routing between siblings trivial.

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. 4 tool updatesv1.0.0
    • First observedflexible_hotels_get_offer_details
    • First observedflexible_hotels_list_hotels_in_city
    • First observedflexible_hotels_resolve_city_code
    • First observedflexible_hotels_search_flexible_offers

TDQS

A4.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: resolving city names to codes, listing hotels, searching flexible offers, and getting offer details. There is no overlap or ambiguity between them; the workflow is linear and obvious.

Naming Consistency5/5

All tool names follow the same pattern: 'flexible_hotels_' prefix followed by a verb_noun combination (resolve_city_code, list_hotels_in_city, search_flexible_offers, get_offer_details). The verbs are consistent and descriptive, making the API predictable.

Tool Count4/5

Four tools is on the lower end but well-scoped for a focused flexible hotel search server. Each tool is necessary and covers a distinct step in the workflow; there is no bloat or redundancy.

Completeness5/5

The server covers the complete user journey: resolve city → optionally list hotels → search offers → get details. For an affiliate-focused read-only service, the surface is complete with no obvious missing operations or dead ends.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to search, browse, and book hotels from a database of 2 million properties worldwide. Provides comprehensive hotel search capabilities with location lookup, filtering by amenities, detailed property information, and integrated booking functionality.
    6
    9 npm
    1
    ISC
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to search, compare, and book hotels with real-time pricing and availability, supporting multiple location types, star ratings, and price filters.
    11
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to search and book hotels globally with real-time pricing and inventory from over 2 million properties.
    46
    MIT