Install
$ agentstack add mcp-him229-stays ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
🏨 stays — Google Hotels MCP Server + Python Library
[](https://github.com/him229/stays/actions/workflows/test.yml) [](https://pypi.org/project/stays/) [](https://pypi.org/project/stays/) [](LICENSE)
A single Python package that gives you Google Hotels three ways: a CLI, an MCP server for Claude / Codex / ChatGPT, and an importable library. All three talk directly to Google's internal batchexecute RPC — no HTML scraping, no headless browser, no unofficial proxies.
> 🚀 Why stays? > > Fast — direct RPC calls, not page rendering > Zero scraping — no HTML parsing, no Playwright/Puppeteer at runtime > Reliable — Chrome TLS impersonation via curl_cffi, 10 rps rate-limit bucket, tenacity retries > MCP-native — three tools, two prompts, one resource; stdio and streamable HTTP > * One install, three surfaces — pipx install stays gets you the CLI, the MCP server, and the library
Quick start
Don't want to touch the terminal? If you already use Claude Code or Codex, paste this into the chat and your assistant will handle everything — uv, the stays install, and the MCP client registration:
> Install the stays MCP server on this Mac and register it with your CLI. Use uv (install it from https://astral.sh/uv/install.sh if it's not already there) — don't use Homebrew. Once stays is installed, run stays setup (use claude if you're Claude Code, codex if you're Codex) and tell me to restart this session.
Then restart your CLI and ask for a hotel search.
Prefer the terminal?
# Install
pipx install stays
# Register the MCP server with whichever client(s) you use
stays setup claude # Claude Code CLI and/or Claude Desktop
stays setup codex # OpenAI Codex CLI
stays setup chatgpt # Instructions for remote HTTPS + Developer Mode
# Or skip MCP and use the CLI directly
stays "tokyo hotels" --check-in 2026-07-22 --check-out 2026-07-26
Restart your MCP client, then try:
> "Find me a 4-star hotel in Tokyo for July 22–26 under $120 a night." > > "Compare rooms, rates, and cancellation for the top 5 hotels near Big Ben." > > "Show me pet-friendly refundable stays in Paris for next weekend."
Prefer a different install path? See [Install](#install) below.
Pick your path
| You are… | Start here | |----------|------------| | A Claude / Codex / ChatGPT user who wants your assistant to search hotels | [MCP Clients](#mcp-clients) → [MCP Tools](#mcp-tools) | | Running hotel searches from the terminal | [CLI Usage](#cli-usage) | | Building a Python app on top of Google Hotels | [Python API](#python-api) | | An AI coding agent installing stays for a user | [For AI Agents](#for-ai-agents) | | Deploying stays as an HTTP MCP server | [Running the server directly](#running-the-server-directly) → [Docker](#docker) |
Features
- 🔍 List-view search — 16 filter slots: city / brand / stars / price range / amenities / dates / guests / cancellation / eco / special offers / sort.
- 🏨 Deep hotel detail — rooms, per-OTA rate plans (Booking, Expedia, Hotels.com, Trip.com, direct), cancellation policies, deep-link URLs.
- ⚡ Parallel enrichment — search + fan-out detail fetch for the top N hotels in a single call, with per-hotel partial failure.
- 🤖 MCP server — FastMCP over stdio (what Claude/Codex spawn) or streamable HTTP (dev / Docker).
- 🧰 Three-format CLI —
text(rich tables),json(single envelope),jsonl(stream-friendly). - 🛡️ Production hygiene — rate-limited
curl_cffisession with Chrome TLS impersonation, tenacity exponential backoff, typed pydantic v2 models, 330 offline tests. - 🐳 Ready for containers — published multi-arch image at
ghcr.io/him229/stays:latest, plusdocker-composeprofiles.
Install
# Recommended — isolated venv, `stays` on your PATH
pipx install stays
# Inside an existing environment
pip install stays
# From source (latest main)
pip install 'git+https://github.com/him229/stays.git'
# Local dev checkout
git clone https://github.com/him229/stays.git
cd stays
uv sync --extra dev
uv run stays --help
Requires Python 3.10+. There are no optional extras — the CLI, the MCP stdio/HTTP server, and the Python library are all included in the single core install.
CLI Usage
The stays console script is the only entry point you need. Subcommands:
| Command | Purpose | |---------|---------| | stays search | Fast list-view search (one RPC) | | stays details | Rooms / rates / cancellation for ONE hotel | | stays enrich | Search + parallel detail fetch for the top N hotels | | stays mcp | Stdio MCP server (what Claude / Codex spawn) | | stays mcp-http | Streamable-HTTP MCP server (dev / Docker) | | stays setup {claude\|codex\|chatgpt} | Register the MCP server with a client |
Smart default: if the first positional arg doesn't match a known subcommand, stays routes to search. stays "paris hotels" ... is equivalent to stays search "paris hotels" ....
Examples
# Rich list-view with filters
stays search "tokyo hotels" \
--check-in 2026-07-22 --check-out 2026-07-26 \
--stars 4 --stars 5 \
--amenity POOL --brand HILTON \
--price-max 300 --sort-by LOWEST_PRICE
# Smart-default form (no `search` subcommand)
stays "paris hotels" --check-in 2026-09-01 --check-out 2026-09-04
# Rooms / rates / cancellation for ONE hotel
stays details "ChkI_ENTITY_KEY_FROM_SEARCH" \
--check-in 2026-07-22 --check-out 2026-07-26
# Search + top-5 deep detail in parallel
stays enrich "new york hotels" --max-hotels 5 \
--check-in 2026-09-01 --check-out 2026-09-04
# Machine-readable output
stays search "tokyo" --format json # single pretty-printed envelope
stays search "tokyo" --format jsonl # one record per line, stream-friendly
CLI options (search / enrich)
| Flag | Type | Purpose | |------|------|---------| | --check-in / --check-out | YYYY-MM-DD | Stay window (required for rate plans) | | --adults / --children | int | Party composition (1–12 / 0–8) | | --child-age | int (repeat) | One --child-age per child | | --currency | ISO 4217 | Output currency (default USD) | | --property-type | enum | HOTELS (default) or VACATION_RENTALS | | --sort-by | enum | RELEVANCE, LOWEST_PRICE, HIGHEST_RATING, MOST_REVIEWED | | --stars | 1–5 (repeat) | Hotel-class filter (--stars 4 --stars 5) | | --min-rating | enum | THREE_FIVE_PLUS, FOUR_ZERO_PLUS, FOUR_FIVE_PLUS | | --amenity | enum (repeat) | POOL, WIFI, SPA, PET_FRIENDLY, … | | --brand | enum (repeat) | HILTON, MARRIOTT, HYATT, … | | --price-min / --price-max | int | Price band (selected currency) | | --free-cancellation | flag | Refundable-only | | --eco-certified | flag | Eco-certified only | | --special-offers | flag | Deals only | | --max-results | int | search only — cap (1–25) | | --max-hotels | int | enrich only — cap (1–15, default 5) | | --format | enum | text (rich tables, default), json, jsonl |
> --format json / --format jsonl envelope shapes are stable for v0.1.x > but may evolve in minor releases.
MCP Clients
One command per client. If auto-registration isn't possible, each backend prints the equivalent JSON/TOML you can paste yourself.
Claude Code / Desktop
stays setup claude
Auto-detects both the claude CLI (Claude Code) and claude_desktop_config.json (Claude Desktop) and registers with whichever it finds. Falls through to printing the canonical JSON when neither is present.
--print-json— always print, never register.--desktop-only— skip theclaudeCLI probe and force Desktop mode.--replace— overwrite any priorstaysentry.
Codex CLI
stays setup codex
Shells to codex mcp add stays -- /stays mcp when the codex binary is on $PATH; otherwise prints the equivalent TOML block for ~/.codex/config.toml.
--print-toml— always print, never shell out.--replace— overwrite any priorstaysentry.
ChatGPT
stays setup chatgpt
Prints setup instructions. ChatGPT requires a public HTTPS endpoint implementing OAuth 2.1 + Dynamic Client Registration, registered via Developer Mode in the ChatGPT app — no local auto-registration is possible.
--open— jump to the ChatGPT Connectors settings page in your browser.
Canonical MCP client config
If the stays setup … installer cannot detect your client, emit the snippet yourself:
stays setup claude --print-json
A minimal version that works when the stays binary is on the client's $PATH:
{
"mcpServers": {
"stays": {
"command": "/abs/path/to/stays",
"args": ["mcp"]
}
}
}
Claude Desktop config path:
| OS | Path | |---------|------| | macOS | ~/Library/Application Support/Claude/claude_desktop_config.json | | Linux | ~/.config/Claude/claude_desktop_config.json | | Windows | %APPDATA%\Claude\claude_desktop_config.json |
MCP Tools
The server exposes three tools. All of them return JSON-safe dicts.
| Tool | When to use | RPC cost | |------|-------------|----------| | search_hotels | List-view discovery: browse / filter by city, stars, amenities, price, brand. Start here. | 1 | | get_hotel_details | One hotel: rooms, per-OTA rates, cancellation. Needs an entity_key from search_hotels. | 1 | | search_hotels_with_details | Compare 3–15 hotels' rooms/rates/cancellation in a single call. | 1 + N |
search_hotels parameters
| Parameter | Type | Description | |-----------|------|-------------| | query required | string | "tokyo hotels", "Hilton Paris", etc. | | check_in / check_out | string | YYYY-MM-DD. Omit both for flexible dates. | | adults / children / child_ages | int / int / list[int] | Party composition | | currency | string | ISO 4217 (default from STAYS_MCP_DEFAULT_CURRENCY) | | property_type | enum | HOTELS (default) or VACATION_RENTALS | | sort_by | enum | RELEVANCE, LOWEST_PRICE, HIGHEST_RATING, MOST_REVIEWED | | hotel_class | list[int] | Star classes to include, e.g. [4, 5] | | min_guest_rating | enum | THREE_FIVE_PLUS, FOUR_ZERO_PLUS, FOUR_FIVE_PLUS | | amenities | list[string] | POOL, WIFI, SPA, PET_FRIENDLY, … | | brands | list[string] | HILTON, MARRIOTT, HYATT, IHG, ACCOR, … | | free_cancellation | bool | Refundable-only | | eco_certified | bool | Eco-certified only | | special_offers | bool | Deals only | | price_min / price_max | int | Price band (selected currency) | | max_results | int | Cap (1–25); overrides STAYS_MCP_MAX_RESULTS |
get_hotel_details parameters
| Parameter | Type | Description | |-----------|------|-------------| | entity_key required | string | From a prior search_hotels result | | check_in required | string | YYYY-MM-DD (rate plans are date-keyed) | | check_out required | string | YYYY-MM-DD after check_in | | currency | string | ISO 4217 (default USD) |
search_hotels_with_details parameters
Same filter set as search_hotels, plus:
| Parameter | Type | Description | |-----------|------|-------------| | max_hotels | int | Top-N hotels to enrich (1–15, default 5) |
Prompts & resources
The server also exposes two prompts — when-to-deep-search and compare-hotels-in-city — that help an LLM pick the right tool, plus one resource resource://stays-mcp/configuration describing the live env-var config.
Python API
Everything public is re-exported from the top-level stays package.
from datetime import date
from stays import (
SearchHotels, HotelSearchFilters, Location, DateRange, GuestInfo,
Amenity, Brand, Currency, SortBy, MinGuestRating,
)
s = SearchHotels()
# 1. Fast list-view search — one RPC
results = s.search(HotelSearchFilters(
location=Location(query="tokyo hotels"),
dates=DateRange(check_in=date(2026, 7, 22), check_out=date(2026, 7, 26)),
guests=GuestInfo(adults=2),
hotel_class=[4, 5],
amenities=[Amenity.POOL, Amenity.WIFI],
brands=[Brand.HILTON],
sort_by=SortBy.LOWEST_PRICE,
currency=Currency.USD,
))
for hotel in results[:3]:
print(hotel.name, hotel.display_price, hotel.overall_rating)
Deep detail for one hotel
first = results[0]
if first.entity_key:
detail = s.get_details(
entity_key=first.entity_key,
dates=DateRange(check_in=date(2026, 7, 22), check_out=date(2026, 7, 26)),
)
print(detail.address, detail.phone)
for room in detail.rooms:
for rp in room.rates:
print(rp.provider, rp.price, rp.cancellation.kind.value)
Parallel enrichment with partial-failure handling
filters = HotelSearchFilters(
location=Location(query="new york hotels"),
dates=DateRange(check_in=date(2026, 9, 1), check_out=date(2026, 9, 4)),
)
for item in s.search_with_details(filters, max_hotels=5):
if item.ok:
print(item.detail.name, len(item.detail.rooms), "rooms")
else:
# error_kind is "transient" or "fatal"; is_retryable is True only
# for transient failures. Unknown exceptions (parser bugs, etc.)
# propagate — only typed BatchExecuteError / TransientBatchExecuteError
# / MissingHotelIdError become per-item errors.
retry_hint = " (retryable)" if item.is_retryable else ""
print("skipped:", item.result.name, "—", item.error_kind, item.error, retry_hint)
stays enrich --format json and the MCP search_hotels_with_details tool mirror this shape: each per-hotel record includes ok, result, detail, error, error_kind ("transient" | "fatal" | null), and is_retryable.
Serializer-only (no HTTP)
Useful for debugging the wire shape or building your own client on top:
filters = HotelSearchFilters(
location=Location(query="new york hotels"),
dates=DateRange(check_in=date(2026, 9, 1), check_out=date(2026, 9, 4)),
guests=GuestInfo(adults=2, children=1, child_ages=[7]),
price_range=(100, 300),
)
filters.format() # Python list — inner JSON shape
filters.encode() # URL-encoded outer envelope
filters.to_request_body() # "f.req=..." — ready to POST
Public exports
- Models:
Amenity,Brand,Currency,DateRange,GuestInfo,
HotelSearchFilters, Location, MinGuestRating, PropertyType, SortBy
- Results:
HotelResult,HotelDetail,RoomType,RatePlan,
CancellationPolicy, CancellationPolicyKind, Review, RatingHistogram, CategoryRating, NearbyPlace, EnrichedResult (now carries error_kind: Literal["transient","fatal"] | None and a .is_retryable property)
- Search API:
SearchHotels,Client,BatchExecuteError,
TransientBatchExecuteError, MissingHotelIdError
- Serializers:
stays.serialize— canonicalserialize_hotel_result,
serialize_hotel_detail, plus build_success / build_error envelope helpers (shared by CLI + MCP; dict shapes guarded by golden-fixture tests)
- MCP (core install only):
mcp,search_hotels,get_hotel_details,
search_hotels_with_details, run_mcp, run_mcp_http
Running the server directly
# Stdio — what Claude Code / Desktop / Codex invoke on your behalf
stays mcp
# Streamable HTTP — dev or Docker runtime
stays mcp-http # serves http://127.0.0.1:8000/mcp/
> The streamable-HTTP endpoint requires the MCP-spec header > Accept: application/json, text/event-stream. A bare GET /mcp/ returns > 405/406 by design — this is not a bug.
Docker
A published image is available from GitHub Container Registry:
# Pull the latest release image
docker run --rm -p 8000:8000 ghcr.io/him229/stays:latest
# Or with compose (prod profile, healthcheck included)
docker compose --profile prod up
# Or build + run locally (de
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [him229](https://github.com/him229)
- **Source:** [him229/stays](https://github.com/him229/stays)
- **License:** MIT
- **Homepage:** https://pypi.org/project/stays/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.