ghotels

ghotels

An MCP server that gives AI agents live Google Hotels data (search, rooms, rates, cancellation) via internal RPC — no API key or scraping required.

Category
Visit Server

README

<p align="center"> <img src="./assets/banner.svg" alt="ghotels — Google Hotels for AI agents and the terminal" width="100%"> </p>

<h1 align="center">ghotels</h1>

<p align="center"><strong>Give your AI agent live hotel data — no API key, no scraping.</strong></p>

<p align="center"> An MCP server, CLI, and typed Python library that talk directly to Google Hotels' internal <code>batchexecute</code> RPC — the same call the Google Hotels UI makes. </p>

<p align="center"> <a href="https://pypi.org/project/ghotels/"><img src="https://img.shields.io/pypi/v/ghotels.svg" alt="PyPI version"></a> <a href="https://github.com/alexechoi/google-hotels-mcp/actions/workflows/test.yml"><img src="https://github.com/alexechoi/google-hotels-mcp/actions/workflows/test.yml/badge.svg" alt="CI"></a> <a href="https://pypi.org/project/ghotels/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg" alt="Python 3.10–3.13"></a> <a href="https://github.com/alexechoi/google-hotels-mcp/pkgs/container/google-hotels-mcp"><img src="https://img.shields.io/badge/ghcr.io-multi--arch-2496ED.svg?logo=docker&logoColor=white" alt="Docker image"></a> <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT License"></a> </p>

<p align="center"> <a href="#quick-start">Quick start</a> · <a href="#mcp-tools">MCP tools</a> · <a href="#cli">CLI</a> · <a href="#python-api">Python API</a> · <a href="./docs/PROTOCOL.md">Protocol docs</a> · <a href="./CONTRIBUTING.md">Contributing</a> </p>

ghotels is a ground-up, actively maintained rebuild of him229/stays, which pioneered this approach but is no longer maintained — see Acknowledgements.

Why ghotels?

⚡ Fast One RPC per search — no page rendering, no headless browser, no third-party proxy
🔑 No API key Talks to the same internal endpoint the Google Hotels UI uses
🤖 MCP-native Three read-only tools, two prompts, one config resource; stdio and streamable HTTP
🧰 Three surfaces pipx install ghotels gets you the MCP server, the CLI, and the Python library
🛡️ Engineered, not scripted mypy --strict, ruff on ALL rules, async token-bucket rate limiting, typed error taxonomy, live-verified test suite
📖 Documented protocol The reverse-engineered wire format lives in docs/PROTOCOL.md, not in tribal knowledge

Quick start

# 1. Install (pipx keeps it isolated and on your PATH)
pipx install ghotels

# 2. Register the MCP server with your client
ghotels setup claude        # Claude Code / Claude Desktop
ghotels setup codex         # OpenAI Codex CLI
ghotels setup chatgpt       # prints remote-connector instructions

# 3. Restart your client, then ask:

"Find me a 4-star hotel in Tokyo for Sep 22–26 under $150 a night."

"Compare rooms, rates, and cancellation for the top 5 hotels near the Louvre."

"Show me pet-friendly refundable stays in Austin next weekend."

Prefer the terminal? The CLI speaks the same engine:

ghotels "tokyo hotels" --check-in 2026-09-22 --check-out 2026-09-26 --stars 4 --price-max 150

<p align="center"> <img src="./assets/demo-terminal.svg" alt="ghotels CLI searching hotels and fetching per-provider rates" width="90%"> </p>

How it works

<p align="center"> <img src="./assets/architecture.svg" alt="Architecture: MCP clients, terminal, and Python apps flow through one typed core to Google's batchexecute RPC" width="100%"> </p>

Every surface funnels through one typed core: validated filters are encoded by a documented slot map into the f.req envelope, posted through a Chrome-impersonated, token-bucket-paced transport, and the response frames are walked back into frozen pydantic models. Cancellation deadlines are anchored to your check-in date — parsing never reads the wall clock, so results are reproducible.

MCP tools

Tool Use it for RPC cost
search_hotels Discovery: browse and filter by city, stars, price, brand, amenities. Start here. 1
get_hotel_details One hotel's rooms, per-provider rates, and cancellation policies. Needs an entity_key from a search. 1
search_hotels_with_details Compare rooms/rates/cancellation across the top N hotels in one call. 1 + N

All three return one canonical envelope (ok, kind, query, count, results), and bad arguments come back as corrective error messages that list the valid options — the calling model can fix itself without a retry loop. The server also ships two prompts (choosing-a-tool, compare-hotels-in-city) and a live config resource at resource://google-hotels-mcp/configuration.

<details> <summary><strong><code>search_hotels</code> parameters</strong></summary>

Parameter Type Notes
query (required) string "tokyo hotels", "Hilton Paris"
check_in / check_out YYYY-MM-DD Omit both for flexible dates
adults / children / child_ages int / int / list[int] One age (0–17) per child
currency ISO 4217 USD (default), EUR, GBP, JPY, …
sort_by enum RELEVANCE, LOWEST_PRICE, HIGHEST_RATING, MOST_REVIEWED
hotel_class list[int] e.g. [4, 5]
amenities list POOL, WIFI, SPA, PET_FRIENDLY, KID_FRIENDLY, …
brands list HILTON, MARRIOTT, HYATT, IHG, ACCOR, …
min_guest_rating float 3.5, 4.0, or 4.5
free_cancellation / eco_certified / special_offers bool Refundable / eco / deals only
price_min / price_max int Per-night band in the selected currency
property_type enum HOTELS (default) or VACATION_RENTALS
max_results int Cap (1–25)

</details>

<details> <summary><strong><code>get_hotel_details</code> parameters</strong></summary>

Parameter Type Notes
entity_key (required) string From a prior search_hotels result
check_in / check_out (required) YYYY-MM-DD Rate plans are date-keyed
adults / children / child_ages int / int / list[int] Occupancy affects pricing
currency ISO 4217 Labels the returned rates

</details>

<details> <summary><strong><code>search_hotels_with_details</code> parameters</strong></summary>

Everything search_hotels takes (dates required), plus:

Parameter Type Notes
max_hotels int Top-N to enrich (default 5, hard cap 15). Per-hotel failures come back inline instead of aborting the batch.

</details>

CLI

ghotels routes a bare query straight to search — ghotels "paris hotels" just works. Subcommands:

Command Purpose
ghotels search <query> List-view search (one RPC)
ghotels details <entity_key> Rooms / rates / cancellation for one hotel
ghotels enrich <query> Search + parallel detail fetch for the top N
ghotels mcp Stdio MCP server (what clients spawn)
ghotels mcp-http Streamable-HTTP MCP server (dev / Docker)
ghotels setup {claude|codex|chatgpt} Register with an MCP client
# Filters compose; enums are case-insensitive
ghotels search "london hotels" \
    --check-in 2026-09-22 --check-out 2026-09-26 \
    --stars 4 --stars 5 --amenity POOL --brand HILTON \
    --price-max 300 --sort-by LOWEST_PRICE

# Machine-readable output
ghotels "rome hotels" --format json     # one envelope
ghotels "rome hotels" --format jsonl    # one record per line

Python API

Async-first, with a synchronous facade that reuses one HTTP session:

import asyncio
from datetime import date

from ghotels import AsyncGoogleHotels, Location, SearchFilters, SortBy, StayDates


async def main() -> None:
    async with AsyncGoogleHotels() as api:
        hotels = await api.search(
            SearchFilters(
                location=Location(query="tokyo hotels"),
                dates=StayDates(check_in=date(2026, 9, 22), check_out=date(2026, 9, 26)),
                star_classes=[4, 5],
                sort_by=SortBy.LOWEST_PRICE,
            )
        )
        best = hotels[0]
        print(best.name, best.price and best.price.amount)

        detail = await api.details(
            best.entity_key,
            dates=StayDates(
                check_in=date(2026, 9, 22),
                check_out=date(2026, 9, 26),
            ),
        )
        for room in detail.rooms:
            for rate in room.rates:
                print(rate.provider, rate.price, rate.cancellation.kind.value)


asyncio.run(main())
# Synchronous — same API, no event loop to manage
from ghotels import GoogleHotels, Location, SearchFilters

with GoogleHotels() as api:
    for hotel in api.search(SearchFilters(location=Location(query="berlin hotels")))[:5]:
        print(hotel.name, hotel.rating and hotel.rating.score)

search_with_details fans out detail lookups concurrently and reports per-hotel failures with a retryable flag instead of aborting the batch. Everything public is exported from the top-level ghotels package and fully typed (py.typed).

Configuration

All knobs are environment variables:

Variable Default Purpose
GHOTELS_RPS 10 Rate limit toward Google (requests/second, token bucket)
GHOTELS_TIMEOUT 30 Per-request timeout (seconds)
GHOTELS_MCP_DEFAULT_ADULTS 2 Default party size
GHOTELS_MCP_DEFAULT_CURRENCY USD Fallback currency
GHOTELS_MCP_DEFAULT_SORT_BY RELEVANCE Default sort
GHOTELS_MCP_MAX_RESULTS unset Cap list-view results
GHOTELS_MCP_DEFAULT_MAX_HOTELS 5 Default enrich N (hard cap 15)

Docker

# Published multi-arch image (amd64 + arm64)
docker run --rm -p 8000:8000 ghcr.io/alexechoi/google-hotels-mcp:latest

# Or with compose
docker compose --profile prod up      # published image + healthcheck
docker compose --profile dev up --build

The container serves streamable-HTTP MCP on :8000. A bare GET /mcp returns 405/406 by design — the transport requires Accept: application/json, text/event-stream.

Development

git clone https://github.com/alexechoi/google-hotels-mcp.git
cd google-hotels-mcp
make install-dev    # uv sync --extra dev
make check          # ruff format --check + ruff check + mypy --strict + pytest
make test-live      # end-to-end against the real endpoint (network)

The offline suite (180+ tests) runs against live-captured fixtures and includes byte-parity goldens for the request encoder. Wire-format details live in docs/PROTOCOL.md. See CONTRIBUTING.md for the dev loop and PR conventions.

Acknowledgements

ghotels exists because of two projects:

  • stays by Himank Yadav did the original reverse engineering of the Google Hotels batchexecute RPC and proved this approach end to end. It is, at the time of writing, no longer maintained — the last activity was April 2026 and community fix PRs have gone unreviewed since — which is the direct reason this project exists. ghotels is a fresh, independently written implementation (not a fork) that re-derives the protocol from stays' documented findings, folds in the fixes that upstream never merged, and stays under active maintenance.
  • fli by Punit Arani pioneered the direct-batchexecute technique for Google Flights that inspired stays in the first place.

Disclaimer

This project is not affiliated with, endorsed by, or sponsored by Google. It uses an unofficial, undocumented interface that may change or break at any time; use it responsibly and respect Google's terms of service and rate limits (the built-in limiter defaults to 10 requests/second).

License

MIT © Alex Choi

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured