opentable-watcher-mcp
by jtur671
README.md
# opentable-watcher-mcp
A watch-and-alert tool for OpenTable availability. You tell it a restaurant, a
party size, and a window of dates/times you'd take a table; it polls quietly
in the background and pings you the moment something opens up.
**This tool does not and will not book anything for you.** It watches, it
alerts, and it hands you a deep link to the restaurant's booking page so a
human clicks the final button. There is no booking automation anywhere in
this codebase, on purpose.
It ships two ways to use it: an MCP server so you can manage watches by
talking to Claude ("watch Restaurant X for 4 on the 12th between 6 and
8pm"), and a background daemon that keeps scanning even when Claude isn't
running.
## 5-minute quickstart
**1. Install it.**
This tool drives a real Chromium browser under the hood (see [How it
works](#how-it-works)), so Playwright needs a browser binary on disk too.
`playwright install chromium` isn't automatically on `PATH` after a `uv tool
install`/`pipx install` (they isolate each tool in its own venv), so grab it
explicitly in that same environment:
```bash
uv tool install opentable-watcher-mcp --with-executables-from playwright
playwright install chromium
```
`--with-executables-from playwright` puts playwright's own CLI on `PATH`
alongside `opentable-watcher` so the second command works as shown. (A
one-liner that does the same without a permanent install:
`uvx --from opentable-watcher-mcp playwright install chromium`.)
Or with pipx:
```bash
pipx install opentable-watcher-mcp
~/.local/share/pipx/venvs/opentable-watcher-mcp/bin/playwright install chromium
```
(`~/.local/share/pipx/venvs/opentable-watcher-mcp` is pipx's default venv
location for this package on Linux/macOS. If you've customized `PIPX_HOME`,
or you're not sure, run `pipx environment` to see where pipx is actually
looking.)
(PyPI publication is pending — until then, install straight from GitHub:
`uv tool install git+https://github.com/jtur671/opentable-watcher-mcp --with-executables-from playwright`.)
**2. Seed a trusted session.**
```bash
opentable-watcher seed https://www.opentable.com/r/example-restaurant
```
This opens a real, visible Chrome window on that restaurant's page. Browse
around until the available times render normally — that's it, no OpenTable
login required (this is watch-only, so there's nothing to sign in for
unless you want to). Once times are showing, switch back to the terminal
and press Enter. The session cookies are saved locally and reused by
everything else below.
**3. Point Claude at it.**
Add this to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"opentable-watcher": {
"command": "opentable-watcher",
"args": ["mcp"]
}
}
}
```
Or, for Claude Code:
```bash
claude mcp add opentable-watcher -- opentable-watcher mcp
```
See [docs/mcp-config.md](docs/mcp-config.md) for both in full.
**4. Ask Claude.**
> "Watch example-restaurant for 2 on 2027-03-01 between 5 and 8pm."
Claude will call `add_watch`, and from then on you can ask it to check
status, look at recent matches, pause/resume, or remove the watch — all in
plain English.
For scanning to keep happening while Claude isn't open, run the background
daemon — see [docs/daemon-setup.md](docs/daemon-setup.md).
## How it works
OpenTable's availability API sits behind Akamai bot detection, which
fingerprints the TLS/HTTP2 stack, client hints, and behavioral signals of
whatever's talking to it. A plain HTTP client — `requests`, `httpx`, curl
with a spoofed `User-Agent` — doesn't pass. The signature doesn't match a
browser, no matter what headers you fake.
So this tool doesn't try to imitate a browser. It *is* one. `seed` opens a
real, persistent Chrome profile and has a human browse it, which lets
Akamai's own sensor JS validate the session the normal way. From then on,
every availability check runs as an in-page `fetch()` call executed inside
that same trusted tab — same cookies, same CSRF token, same TLS
fingerprint the site already approved. It's the restaurant page's own
GraphQL query, asked from the restaurant page itself, just automated on a
timer instead of a mouse.
The tradeoff is that this only works with a real, visible browser (see
[Troubleshooting](#troubleshooting) — headless is a giveaway). It's slower
and heavier than a scraper, but it's the only approach that survives
Akamai without spoofing anything.
## Politeness (built in, not configurable down)
- **30-second floor** between requests to the same restaurant. Watches
can't be created with a tighter interval.
- **5 watches max**, active at once, across the whole tool.
- **One request in flight** at a time — scans go one restaurant, one date,
one fetch at a time; nothing runs concurrently.
These aren't suggestions you can turn off — they're enforced by the store
and the scanner. The goal is a tool that looks, to OpenTable, exactly like
one household checking dinner plans a little more often than a person
would refresh a tab. Not a scraper.
**How results are ranked:** when a scan finds several open slots for a
watch, they're sorted by closeness to 7:00pm (the tool's current fixed
"ideal" dinner time), so the slot nearest prime time is reported first.
## A note on terms of service
Automating interaction with OpenTable is contrary to its Terms of Service.
This project exists anyway as a personal-use convenience — the same kind
of "did the 7pm slot open up yet" checking a person would otherwise do by
refreshing a browser tab every few minutes, just handed off to software so
you don't have to. It deliberately contains **no booking automation** —
every match ends in a link, not a reservation — and it enforces the rate
limits above so it can't be turned into a scraper by accident.
Use it at your own risk, for your own household's dinner plans — a handful
of personal watches, not a scraping operation. If OpenTable's terms matter
to you, read them before running this.
## What Claude can do once it's connected
| Tool | What it does |
|---|---|
| `add_watch` | Start watching a restaurant for a party size across a set of dates/times |
| `list_watches` | List every configured watch and its status |
| `pause_watch` / `resume_watch` | Temporarily stop/resume scanning without deleting the watch |
| `remove_watch` | Delete a watch and its match history |
| `check_now` | Check availability immediately instead of waiting for the next scan |
| `recent_matches` | See previously found matching time slots |
| `status` | Whether the background daemon is running, plus a summary of all watches |
| `setup_session` | Instructions for (re-)establishing the trusted browser session |
## Configuration
Watches and state live in `~/.config/opentable-watcher/` by default
(`$XDG_CONFIG_HOME/opentable-watcher` if set). Override the location with
`--config-dir` or the `OTWATCHER_CONFIG_DIR` environment variable — useful
if you want the MCP server and the daemon to share state explicitly, or to
run more than one independent watcher.
## Troubleshooting
**"availability fetch returned non-JSON (likely Akamai challenge)"**
The saved session has gone stale or was never fully trusted. Re-run:
```bash
opentable-watcher seed <restaurant-url>
```
Browse until times render before pressing Enter — a quick glance often
isn't enough for the sensor JS to finish validating.
**Nothing works when run headless / on a server**
Akamai's behavioral checks are much more likely to flag a headless
browser. `seed` always opens a visible window by design, and the daemon
needs a real display too. Running on a headless server? See the Xvfb
section in [docs/daemon-setup.md](docs/daemon-setup.md).
**A watch stopped reporting matches / says expired**
Watches expire once every date in their window is in the past. Check with
`status` or `list_watches`, and create a new watch with updated dates.
## More docs
- [docs/mcp-config.md](docs/mcp-config.md) — full MCP client configuration
- [docs/daemon-setup.md](docs/daemon-setup.md) — running the 24/7 background daemon (systemd, launchd, Xvfb)
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues