Content
# Hotel Price MCP
Automatic hotel price inquiry for Ctrip, IHG, and Marriott
## Current Implementation
This is a Node.js/TypeScript price inquiry service framework that uses `cloakbrowser` to launch a browser and separates Ctrip, IHG, and Marriott into three independent provider modules.
Directory:
```text
src/
app.ts HTTP service entry
browser/ CloakBrowser startup, browser pool, and page auxiliary functions
modules/
base/ Unified input/output types, price parsing, and hotel name matching
ctrip/ Ctrip price inquiry module
ihg/ IHG price inquiry module
marriott/ Marriott price inquiry module
services/PriceQueryService.ts Aggregate three providers
api/routes.ts REST API
storage/priceRepository.ts Query record memory repository
```
## Installation
```bash
npm install
cp .env.example .env
```
## Startup
```bash
npm run dev
```
Default listening on `http://localhost:3100`.
## Query Interface
```bash
curl -X POST http://localhost:3100/price/query \
-H 'Content-Type: application/json' \
-d '{
"hotelName": "Xi'an Crown Plaza",
"checkIn": "2026-06-12",
"checkOut": "2026-06-13",
"rooms": 1,
"adults": 2
}'
```
Return structure:
```json
{
"queryId": "q_xxx",
"results": [
{
"provider": "ctrip",
"hotelName": "Shanghai Jing'an Reggie Hotel",
"checkIn": "2026-07-01",
"checkOut": "2026-07-02",
"available": true,
"lowestPrice": 1680,
"currency": "CNY",
"status": "success",
"queriedAt": "2026-06-05T00:00:00.000Z"
}
]
}
```
## IHG MCP Tool
IHG price inquiry also provides a stdio MCP server, suitable for Claude, Codex, OpenClaw, Hermes, and other MCP-supported agent clients.
First, build:
```bash
npm run build
```
MCP startup command:
```bash
node /Users/apple/PycharmProjects/hotel-price-mcp/dist/mcp/ihgMcpServer.js
```
Tool name:
```text
ihg_query_price
```
Tool input:
```json
{
"hotelName": "Xi'an Intercontinental",
"checkIn": "2026-07-01",
"checkOut": "2026-07-02",
"rooms": 1,
"adults": 2,
"children": 0
}
```
MCP configuration example:
[config/mcp.ihg.example.json](config/mcp.ihg.example.json)
Also provides a portable skill description:
[skills/ihg-price-query/SKILL.md](skills/ihg-price-query/SKILL.md)
## Configuration
`.env` supports:
```text
PORT=3100
LOG_LEVEL=info
CLOAK_HEADLESS=false
CLOAK_HUMANIZE=true
CLOAK_GEOIP=true
CLOAK_REQUIRE_PROXY=true
CLOAK_PROXY_URL=http://user:pass@host:port
CLOAK_FINGERPRINT_ROTATE=true
CLOAK_FINGERPRINT_MIN=10000
CLOAK_FINGERPRINT_MAX=999999999
QUERY_TIMEOUT_MS=90000
ARTIFACT_DIR=.artifacts
```
Strong risk control pages recommend keeping `CLOAK_HEADLESS=false`, `CLOAK_HUMANIZE=true`, and `CLOAK_GEOIP=true`.
By default, proxy configuration is required:
```text
CLOAK_REQUIRE_PROXY=true
CLOAK_PROXY_URL=http://user:pass@host:port
```
Supports HTTP and SOCKS5:
```text
http://user:pass@host:port
socks5://user:pass@host:port
```
SOCKS5 proxies with usernames and passwords will automatically forward through local HTTP proxies, because Chromium does not directly support `socks5://user:pass@host:port` form authentication. This mode will disable automatic GeoIP and avoid the first download of the GeoIP database slowing down the query.
Each price inquiry will start a new CloakBrowser instance and randomly generate a new fingerprint seed:
```text
CLOAK_FINGERPRINT_ROTATE=true
CLOAK_FINGERPRINT_MIN=10000
CLOAK_FINGERPRINT_MAX=999999999
```
If you need to fix the fingerprint to troubleshoot problems, you can temporarily set `CLOAK_FINGERPRINT_ROTATE=false`.
## Follow-up Key Points
The three providers have been separated by site, and real online debugging is required for each site using actual hotel samples:
- Ctrip: `src/modules/ctrip/ctrip.selectors.ts`
- IHG: `src/modules/ihg/ihg.selectors.ts`
- Marriott: `src/modules/marriott/MarriottApiClient.ts`
Failed queries will save screenshots to `.artifacts/<provider>/` for locating verification codes, risk control, hotel non-matching, or page structure changes.
### IHG Interface Mode
The IHG module has been modified to use interface data, no longer relying on page DOM extraction:
1. Open `www.ihg.com.cn` with CloakBrowser to establish a real browser session.
2. Request `locations/v1/destinations` in the page context to convert hotel/city keywords into latitude and longitude.
3. Request `availability/v3/hotels/offers` to obtain candidate hotel prices.
4. Request `hotels/v3/profiles/{hotelCode}/details` to supplement hotel names.
5. Match according to the input hotel name, return the lowest price, currency, tax rate, and room rate code.
Core files:
```text
src/modules/ihg/IhgApiClient.ts
src/modules/ihg/IhgProvider.ts
```
### Marriott Interface Mode
The Marriott module also implements the "browser session first, then interface data" approach:
1. Open `www.marriott.com/search/findHotels.mi` search page with hotel name, check-in date, check-out date, and guest count using CloakBrowser.
2. Let the page complete Akamai/Cookie/session initialization.
3. Capture `/mi/query`, GraphQL, availability, and search JSON responses sent by the page.
4. Extract `SearchLowestAvailableRates` hotel price node from the response or page Apollo/initial state.
5. Match according to the input hotel name, return the lowest price, currency, tax rate, and room rate code.
If the current proxy is rejected by Marriott Akamai, it will return `status: "blocked"` and save a screenshot to `.artifacts/marriott/`. In this case, change to a cleaner residential proxy or an exit IP that can normally open the Marriott website, and then correct the interface fields.
Core files:
```text
src/modules/marriott/MarriottApiClient.ts
src/modules/marriott/MarriottProvider.ts
```
## Debugging by Station
Debug one station at a time to avoid mixing logs from three browser processes.
```bash
npm run debug:provider -- \
--provider ihg \
--hotel "Shanghai Jing'an Temple Intelligent Holiday Hotel" \
--checkIn 2026-07-01 \
--checkOut 2026-07-02
```
Optional providers:
```text
ctrip | ihg | marriott | all
```
Debugging order:
1. Run `marriott` first, check the command line output `status`, `sourceUrl`, and `errorMessage`.
2. If failed, open the screenshot in `.artifacts/<provider>/`, and see which step the page stops at.
3. For Ctrip-like DOM modules, modify the corresponding `*.selectors.ts` according to the screenshot and DevTools.
4. For IHG and Marriott-like interface modules, first confirm if the page is blocked by risk control; if not blocked, correct the corresponding API client according to DevTools Network.
5. Run the same command again until it returns `status: "success"` or accurately returns `no_availability`.
6. After one station is stable, debug other stations in the same way.
Prioritize checking these selectors:
```text
destinationInput / hotelSearchInput Search box
searchButton Search button
hotelCards Hotel list cards
hotelName Hotel name
priceText Price text
```