{"slug": "build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite", "title": "Build a hotel rate-parity watcher with Claude Desktop and SQLite", "summary": "A developer published an open-source Google Hotels MCP server (huynhtanan/google-hotels-mcp) that connects Claude Desktop to hotel pricing data, paired with a roughly 20-line SQLite logger to track rate parity across OTAs. The setup calls search_hotels once per check-in date, storing hotel, date, OTA and price tuples in a snapshot table, then uses a window-function SQL query to surface the cheapest versus reference price and flag drift beyond a threshold. The author notes the server relies on three paid Apify actors, with Apify's free tier covering about 60 full-city searches per month.", "body_md": "Rate parity is the agreement every hotel signs with each OTA: *no booking site can list the room cheaper than our website*. In practice it breaks constantly — one site runs a flash discount, another forgets to resync a package rate. If you own a hotel, catching that drift in 24 hours instead of 10 days is money.\n\nThis post wires the Google Hotels MCP server to a 20-line SQLite logger. Claude Desktop asks the question, the script writes the diff. You end up with a daily table like this:\n\n```\nhotel                          date         booking   expedia   agoda    delta\nRitz Paris                     2026-11-15   €1,480    €1,520    €1,480   -2.6%\nHotel Lutetia                  2026-11-15   €820      €820      €795     -3.1%\nMandarin Oriental Paris        2026-11-15   €1,650    €1,650    €1,650    0.0%\n```\n\nDisclosure: the MCP server (`huynhtanan/google-hotels-mcp`, open source) calls three paid Apify actors I wrote. Apify's free tier ships $5 of credit per month — about 60 full-city searches — before you need to upgrade. No card required to sign up.\n\nSame install as [the previous post](https://dev.to/huynhtanan/add-google-hotels-to-claude-desktop-as-an-mcp-server-2-minute-setup-1m00). Add this block to `claude_desktop_config.json`:\n\n```\n{\n  \"mcpServers\": {\n    \"google-hotels\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"git+https://github.com/huynhtanan/google-hotels-mcp.git@v0.1.2\", \"google-hotels-mcp\"],\n      \"env\": { \"APIFY_TOKEN\": \"apify_api_...\" }\n    }\n  }\n}\n```\n\nRestart Claude Desktop. Three tools appear: `search_hotels`, `hotel_price_calendar`, `search_vacation_rentals`.\n\n``` python\nimport sqlite3\n\ndb = sqlite3.connect(\"rate_parity.db\")\ndb.executescript(\"\"\"\nCREATE TABLE IF NOT EXISTS snapshot (\n  captured_at TEXT NOT NULL,\n  hotel       TEXT NOT NULL,\n  stay_date   TEXT NOT NULL,\n  ota         TEXT NOT NULL,\n  price       REAL NOT NULL,\n  currency    TEXT NOT NULL,\n  PRIMARY KEY (captured_at, hotel, stay_date, ota)\n);\nCREATE INDEX IF NOT EXISTS snapshot_recent\n  ON snapshot (hotel, stay_date, captured_at DESC);\n\"\"\")\ndb.commit()\n```\n\nKeeping `currency` as a column (instead of baking EUR into the field name) means the same table can hold USD, GBP, or BRL snapshots when you expand beyond one region.\n\n*\"Snapshot the top 20 Paris hotels across the next 30 check-in dates: for each date, call `search_hotels` with `query=\"hotels in Paris\"`, `include_provider_prices=true`, `max_results=20`, two adults, EUR. Insert each (hotel, stay_date, ota, price) tuple into the snapshot table in `rate_parity.db`.\"*\n\nClaude issues one `search_hotels` call per date (30 calls total), each returning ~20 hotels and their `providers` array. That's 30 × 20 = 600 (hotel, date) rows per snapshot. The whole thing runs in your Claude Desktop session — no external cron.\n\nHere is what the MCP tool actually returns for one hotel on one date (lightly trimmed):\n\n```\n{\n  \"name\": \"Hotel Lutetia\",\n  \"checkIn\": \"2026-11-15\",\n  \"checkOut\": \"2026-11-16\",\n  \"providers\": [\n    {\"name\": \"Booking.com\", \"price\": 820, \"currency\": \"EUR\"},\n    {\"name\": \"Expedia\",     \"price\": 820, \"currency\": \"EUR\"},\n    {\"name\": \"Agoda\",       \"price\": 795, \"currency\": \"EUR\"},\n    {\"name\": \"Hotels.com\",  \"price\": 820, \"currency\": \"EUR\"}\n  ],\n  \"rating\": 4.6,\n  \"reviewsCount\": 2341\n}\n```\n\nOnce snapshot rows exist, a single SQL query surfaces drift:\n\n```\nWITH latest AS (\n  SELECT hotel, stay_date, ota, price, currency,\n         ROW_NUMBER() OVER (PARTITION BY hotel, stay_date, ota ORDER BY captured_at DESC) AS rn\n  FROM snapshot\n),\ntoday AS (SELECT * FROM latest WHERE rn = 1),\nagg AS (\n  SELECT hotel, stay_date, currency,\n         MIN(price) AS cheapest, MAX(price) AS reference,\n         COUNT(DISTINCT ota) AS ota_count\n  FROM today GROUP BY hotel, stay_date, currency\n  HAVING ota_count >= 2\n)\nSELECT hotel, stay_date, currency, cheapest, reference,\n       ROUND((cheapest - reference) * 100.0 / reference, 2) AS delta_pct\nFROM agg\nWHERE (cheapest - reference) * 100.0 / reference <= -3.0\nORDER BY delta_pct ASC\nLIMIT 50;\n```\n\nThe `ota_count >= 2` guard is important — a hotel with only one OTA listed is not a parity signal; it is just a single price point.\n\nAny row that comes back is a rate-parity violation at least 3% deep. Pipe it into Slack, email, or just a daily markdown digest.\n\n*\"Of today's drift rows, which 3 would cost the hotel the most if a corporate traveler booked 10 nights on the cheapest OTA instead of the reference OTA?\"*\n\nClaude runs the arithmetic directly against the drift rows and writes a one-paragraph summary. The whole feedback loop — scrape, diff, prioritize, explain — runs inside one chat window.\n\n`search_hotels` pricing: `$0.002/hotel + $0.002/provider-prices` row → 600 × $0.004 ≈ `examples/rate_parity/` run (20 hotels × 7 nights = $0.56/day) stays in the free tier for ~9 days a month\nThe actor is pay-per-result — you pay only for rows that actually came back, not for failed runs or empty days.\n\nThe MCP server, the SQLite schema, and the drift query are in [huynhtanan/google-hotels-mcp](https://github.com/huynhtanan/google-hotels-mcp) under `examples/rate_parity/`. One-file CLI, no framework — the same script can also run from a cron, which is handy if you want nightly snapshots without keeping Claude Desktop open.\n\nIf you have a single hotel and want to try this against your own property — ping me (comments are open). I'm interested in which fields the parity teams actually care about so I can bake them into the next cut of the server.", "url": "https://wpnews.pro/news/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite", "canonical_source": "https://dev.to/huynhtanan/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite-287c", "published_at": "2026-10-21 02:10:30+00:00", "updated_at": "2026-10-21 02:19:43.868463+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "agent-protocols", "developer-tools"], "entities": ["Claude Desktop", "Anthropic", "Google Hotels", "Apify", "Booking.com", "Expedia", "Agoda", "SQLite"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite", "markdown": "https://wpnews.pro/news/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite.md", "text": "https://wpnews.pro/news/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite.txt", "jsonld": "https://wpnews.pro/news/build-a-hotel-rate-parity-watcher-with-claude-desktop-and-sqlite.jsonld"}}