Booking.com

search-stays

booking.com · Cowork
Download for Coworkthe skill folder, zipped
How to use it in Coworkonce per skill
Download the skill (.zip)

One .zip per skill. Inside: SKILL.md with the intent and the frontmatter, and the code that runs in the page.

Open Skills in Cowork

Settings → Personalize → Skills. This is Cowork's own skill library, shared across every chat.

Add → pick the .zip

Click Add and choose the file you just downloaded. Cowork installs it once; no CLAUDE.md, no per-project setup.

Ask in plain language

The skill stays available in every session. Cowork runs it in your own Chrome, riding the session you are already signed into.

Side effectread
Authnone (no login needed; an open booking.com tab supplies the origin for storage)
Needsnavigate · evaluate
Summary

Search Booking.com hotels by place and dates, returning name, price and rating.

Params
{"location":"string, REQUIRED (place name, e.g. \"Barcelona\" or \"Barcelona, Spain\")","checkin":"YYYY-MM-DD (optional)","checkout":"YYYY-MM-DD (optional)","adults":"int (optional, default 2)","rooms":"int (optional, default 1)"}
Returns
{"count":"int","location":"string","results":"array of { name, url, price, rating (0-10), ratingWord, reviews (int), location (neighbourhood), distance }"}
SKILL.md65 lines

booking-search-stays

Searches hotels and stays from a place name and a date range. Resolves in a single bridge call: the extractor fetches what it needs itself, so the params actually drive the request and no page is opened.

The extractor lives as text in the site's localStorage (key booking-search-stays) and is rebuilt on each call with synchronous eval. It is stored once and then persists across navigation and browser restarts, so on every run except the first it is already there.

Default action: just call (step 1). The call itself reports NEEDS_STORE when the extractor is missing or out of date, only then do you store it (step 2) and call again.

Versioning. The stored extractor is gated by VER (must match the version in the frontmatter above). Both snippets carry VER = "1", keep them in sync with the frontmatter. When evaluate.js changes, bump version and VER in both snippets: the next call sees the mismatch, returns NEEDS_STORE, and re-stores automatically. Storage stays at two keys (booking-search-stays and booking-search-stays:ver), overwritten each store, versions never accumulate.

1. Call (do this every run, params only, single bridge call)

await (() => {
  const KEY = "booking-search-stays", VER = "1";
  const s = localStorage.getItem(KEY);
  if (!s || localStorage.getItem(KEY + ":ver") !== VER) return 'NEEDS_STORE';
  const fn = eval('(' + s + ')');
  return fn(document, { location: "Barcelona", checkin: "2026-08-10", checkout: "2026-08-12", adults: 2 });
})()

Returns the matching properties (~25 per call). If it returns the string 'NEEDS_STORE', the extractor is missing or stale in this browser → do step 2 once, then run this exact call again.

Run this snippet verbatim. Never add an await before the eval, only the outer await is allowed.

Any booking.com tab works, the extractor fetches the results page itself and never reads the open page. The tab only supplies the origin.

2. Store (only when step 1 returned NEEDS_STORE; persists until localStorage is cleared)

From a booking.com tab, evaluate the following with the bridge, replacing <FN> with the contents of evaluate.js (the ( async (root, params) => { ... } ) function). Keep VER equal to the call snippet's:

(() => {
  const KEY = "booking-search-stays", VER = "1";
  const fn = (<FN>);
  localStorage.setItem(KEY, String(fn));
  localStorage.setItem(KEY + ":ver", VER);
  return 'stored';
})()

Returns "stored". The extractor passes through the model only here, once. Then go back to step 1.

Session locale

Prices and wording follow the session, not this skill: an Argentine session answers "$ 427.853" (ARS) and ratingWord: "Muy bien"; a US one answers USD and "Very good" (verified). price and ratingWord are display strings, parse them against the currency and language you see, never assume USD/English. rating and reviews are numeric and locale-safe.

Reading the review block

Booking nests the score in five divs with hashed class names, whose order shifts, and whose first line is a localised accessibility label ("Puntuacion: 8,4"). The extractor reads the leaf divs by the shape of their text, pure number = the score, letters-only = the word, last digits+letters = the count, so it does not break when the classes or the order change.

Errors

  • NEEDS_STORE → extractor missing or version-mismatched in this browser; run step 2 once, then retry step 1.
  • missing_params: location is required → pass a place name.
  • blocked → a bot check; open booking.com in the tab and retry.
  • tool-broken: no property cards → Booking changed its markup; recapture the selectors.
  • request_failed → the fetch could not run from this tab.

Success assertion

{ "type": "json", "jsonPath": "count" }
search-stays: the Booking.com skill in Cowork · browser-memory