Install
openclaw skills install @chrischall/resyManage Resy restaurant reservations via MCP — search venues, book tables, list and cancel reservations, manage favorites, and subscribe to Priority Notify. Triggers on phrases like "book a table at", "find me a reservation", "what reservations do I have", "cancel my Resy", "add to my Resy hit list", or any request involving restaurant reservations on Resy. Requires resy-mcp installed and the resy server registered (see Setup below).
openclaw skills install @chrischall/resyMCP server for Resy — natural-language restaurant reservation management. Uses Resy's private web-app API with email + password auth.
⚠️ Resy does not publish an official API. This server uses the same private endpoints the Resy web app calls, with the public web-app
api_keyand user-level auth via email + password. Use at your own discretion.
Add to .mcp.json in your project or ~/.claude/mcp.json:
{
"mcpServers": {
"resy": {
"command": "npx",
"args": ["-y", "resy-mcp"],
"env": {
"RESY_EMAIL": "you@example.com",
"RESY_PASSWORD": "yourpassword"
}
}
}
}
git clone https://github.com/chrischall/resy-mcp
cd resy-mcp
npm install && npm run build
Then add to .mcp.json:
{
"mcpServers": {
"resy": {
"command": "node",
"args": ["/path/to/resy-mcp/dist/bundle.js"],
"env": {
"RESY_EMAIL": "you@example.com",
"RESY_PASSWORD": "yourpassword"
}
}
}
}
Or place .env in the project directory with RESY_EMAIL= and RESY_PASSWORD=.
RESY_EMAIL + RESY_PASSWORD are required. The client logs in lazily on first request via POST /3/auth/password, caches the returned token for the process lifetime, and re-logs automatically on 401 / 419 / auth-like 500 responses.RESY_API_KEY is optional. If unset, the client uses the public web-app key baked into resy.com's JS bundle. Override only if Resy rotates it.| Tool | Description |
|---|---|
resy_get_profile(view?) | Current user profile — name, email, phone, booking count, member-since. Payment method IDs are stripped. |
resy_list_payment_methods(view?) | List saved payment methods (id, brand, last_four, exp_month, exp_year, is_default). The id feeds resy_book's payment_method_id. |
| Tool | Description |
|---|---|
resy_search_venues(date, party_size, query?, lat?, lng?, limit?, radius_meters?, view?) | Search venues with availability for a date + party size. Defaults to NYC geo. |
resy_find_slots(venue_id, date, party_size, lat?, lng?) | List bookable slots at a venue — each includes a short-lived config_token. |
resy_get_venue(venue_id) | Full venue details. |
| Tool | Description |
|---|---|
resy_book(venue_id, date, party_size, desired_time?, lat?, lng?, payment_method_id?) | Composite: find fresh slot → details → book. desired_time is "HH:MM" (24h); closest match wins if no exact slot. Uses default payment method unless payment_method_id is supplied. |
resy_list_reservations(scope?) | List reservations. scope: upcoming (default), past, or all. Each result includes the resy_token needed for cancellation. |
resy_cancel(resy_token) | Cancel by resy_token (rr://…). Inspects the response body to set cancelled: true/false honestly. |
| Tool | Description |
|---|---|
resy_list_favorites(view?) | List favorited venues ("hit list"). |
resy_add_favorite(venue_id) | Add a venue to favorites. |
resy_remove_favorite(venue_id) | Remove from favorites. |
| Tool | Description |
|---|---|
resy_list_notify(view?) | List Priority Notify subscriptions. |
resy_add_notify(venue_id, date, party_size, time_start?, time_end?) | Subscribe to notifications when slots open. time_start / time_end are HH:MM (24h); default window 18:00–21:00. |
resy_remove_notify(notify_id) | Cancel a Priority Notify subscription. |
view)Five read tools take view: "compact" | "full" — resy_get_profile,
resy_list_payment_methods, resy_list_favorites, resy_list_notify and
resy_search_venues — and compact is the default. The slim rung arrives
without being asked for, because an efficiency a caller has to know about and
request is one that usually is not requested.
Compact strips image and avatar URLs here, and nothing else. There is no field projection: this server holds no captured fixture or documented field list for Resy's payloads, and an invented one would return a record with holes in it that still reads like a verified answer. Media stripping is subtractive, so it cannot lose a field nobody knew about.
The surprise worth stating plainly: on four of those five tools, compact and
full are byte-identical today. resy_get_profile is the ONE response in this
whole server that carries a media URL — profile_image_url off /2/user — so
it is the one place compact removes anything at all. The other four already
hand back records this server assembled field by field, with no picture in
them. The parameter is on them so that a field arriving later with an avatar
inside it is stripped by default rather than after somebody notices.
profile_image_url is named in the drop list rather than left to the built-in
rules, and that is what makes its removal deterministic. The key pattern
anchors its noun at the START of a key (it knows profile_pic, not
profile_image_url), which leaves only the value rule — and that fires just
when a URL path ends in an image extension, which Resy's avatar pipeline need
not do.
view: "full" returns the record untouched. There is no raw rung: full
already IS the un-stripped response, so a third value could only alias it.
The other ten tools take no view:
resy_find_slots, resy_get_venue and resy_list_reservations build
their answers field by field (formatSlot, formatVenue,
formatReservation). No upstream payload survives into the response, and no
media URL with it — there is nothing for the rung to remove.resy_book, resy_cancel, resy_add_favorite, resy_remove_favorite,
resy_add_notify, resy_remove_notify are writes. A write's response is a
receipt — a resy_token, a confirmation, a status — with nothing to strip and
everything to keep.resy_healthcheck answers reachability and auth.Book a specific restaurant at a specific time:
resy_search_venues(query: "carbone", date: "2026-05-01", party_size: 2)
→ find venue_id
resy_book(venue_id, date: "2026-05-01", party_size: 2, desired_time: "19:00")
See what's available tonight near me:
resy_search_venues(date: "2026-04-20", party_size: 2, lat: 37.7749, lng: -122.4194)
→ returns venues with baked-in slot availability
Cancel a reservation:
resy_list_reservations() → find resy_token for the one to cancel
resy_cancel(resy_token)
Stalking a hard-to-get table:
resy_search_venues(query: "4 charles prime rib", ...) → venue_id
resy_add_notify(venue_id, date: "2026-05-31", party_size: 2, time_start: "19:00", time_end: "21:00")
# Resy emails you when a slot opens
config_tokens expire within minutes of being fetched. resy_book re-fetches fresh slots internally — don't try to thread a stale token from resy_find_slots into a book call manually.resy_book requires a payment method on file at resy.com/account. If none exists it throws a clear error.lat/lng for other cities.npm run smoke locally against your credentials and adjust the path.RESY_API_KEY env var overrides the baked-in public web-app key if Resy ever rotates it.