@striderlabs/mcp-opentable 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # @striderlabs/mcp-opentable
2
+
3
+ [![npm](https://img.shields.io/npm/v/@striderlabs/mcp-opentable)](https://www.npmjs.com/package/@striderlabs/mcp-opentable)
4
+ [![MCP Registry](https://img.shields.io/badge/MCP-Registry-blue)](https://registry.modelcontextprotocol.io/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ MCP server for OpenTable — let AI agents search restaurants and book reservations.
8
+
9
+ Built by [Strider Labs](https://striderlabs.ai).
10
+
11
+ ## Features
12
+
13
+ - Search restaurants by location, cuisine, party size, date and time
14
+ - Get detailed restaurant info — description, address, hours, and features
15
+ - Check real-time availability for any date, time, and party size
16
+ - Book reservations with a confirmation step before committing
17
+ - View all upcoming reservations in one place
18
+ - Cancel reservations safely with a confirm gate
19
+ - Persistent sessions — stay logged in across restarts
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install -g @striderlabs/mcp-opentable
25
+ ```
26
+
27
+ Or with npx:
28
+
29
+ ```bash
30
+ npx @striderlabs/mcp-opentable
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ Add to your MCP client configuration (e.g., Claude Desktop `~/Library/Application Support/Claude/claude_desktop_config.json`):
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "opentable": {
41
+ "command": "npx",
42
+ "args": ["-y", "@striderlabs/mcp-opentable"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Authentication
49
+
50
+ This connector uses browser automation via Playwright. On first use:
51
+
52
+ 1. Call `opentable_status` — it returns a login URL
53
+ 2. Open the login URL in your browser and sign in to OpenTable
54
+ 3. Session cookies are automatically saved to `~/.strider/opentable/cookies.json`
55
+ 4. Sessions persist across restarts — no need to log in again
56
+
57
+ To log out or reset your session:
58
+
59
+ ```
60
+ opentable_logout
61
+ ```
62
+
63
+ ## Available Tools
64
+
65
+ ### Session Management
66
+
67
+ | Tool | Description |
68
+ |------|-------------|
69
+ | `opentable_status` | Check login status; returns login URL if not authenticated |
70
+ | `opentable_login` | Get the OpenTable login URL and instructions |
71
+ | `opentable_logout` | Clear stored session cookies (log out) |
72
+
73
+ ### Discovery
74
+
75
+ | Tool | Description |
76
+ |------|-------------|
77
+ | `opentable_search` | Search restaurants by location, cuisine, party size, date/time |
78
+ | `opentable_get_restaurant` | Get full details for a specific restaurant |
79
+ | `opentable_check_availability` | List available time slots for a restaurant |
80
+
81
+ ### Reservations
82
+
83
+ | Tool | Description |
84
+ |------|-------------|
85
+ | `opentable_make_reservation` | Book a reservation (requires `confirm=true`) |
86
+ | `opentable_get_reservations` | List all upcoming reservations |
87
+ | `opentable_cancel_reservation` | Cancel a reservation (requires `confirm=true`) |
88
+
89
+ ## Example Usage
90
+
91
+ ### Search for restaurants
92
+
93
+ ```json
94
+ {
95
+ "tool": "opentable_search",
96
+ "arguments": {
97
+ "location": "San Francisco",
98
+ "cuisine": "italian",
99
+ "partySize": 4,
100
+ "date": "2026-03-20",
101
+ "time": "19:30"
102
+ }
103
+ }
104
+ ```
105
+
106
+ ### Check availability
107
+
108
+ ```json
109
+ {
110
+ "tool": "opentable_check_availability",
111
+ "arguments": {
112
+ "restaurantId": "restaurant-slug-or-id",
113
+ "date": "2026-03-20",
114
+ "time": "19:30",
115
+ "partySize": 4
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Book a reservation (two-step)
121
+
122
+ ```json
123
+ // Step 1: Preview
124
+ {
125
+ "tool": "opentable_make_reservation",
126
+ "arguments": {
127
+ "restaurantId": "restaurant-slug-or-id",
128
+ "date": "2026-03-20",
129
+ "time": "19:30",
130
+ "partySize": 4,
131
+ "specialRequests": "Window table if possible",
132
+ "confirm": false
133
+ }
134
+ }
135
+
136
+ // Step 2: Confirm and book
137
+ {
138
+ "tool": "opentable_make_reservation",
139
+ "arguments": {
140
+ "restaurantId": "restaurant-slug-or-id",
141
+ "date": "2026-03-20",
142
+ "time": "19:30",
143
+ "partySize": 4,
144
+ "specialRequests": "Window table if possible",
145
+ "confirm": true
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### View upcoming reservations
151
+
152
+ ```json
153
+ {
154
+ "tool": "opentable_get_reservations",
155
+ "arguments": {}
156
+ }
157
+ ```
158
+
159
+ ### Cancel a reservation
160
+
161
+ ```json
162
+ // Step 1: Preview cancellation
163
+ {
164
+ "tool": "opentable_cancel_reservation",
165
+ "arguments": {
166
+ "reservationId": "res-abc123",
167
+ "confirm": false
168
+ }
169
+ }
170
+
171
+ // Step 2: Confirm cancellation
172
+ {
173
+ "tool": "opentable_cancel_reservation",
174
+ "arguments": {
175
+ "reservationId": "res-abc123",
176
+ "confirm": true
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## Requirements
182
+
183
+ - Node.js 18+
184
+ - Playwright browsers (auto-installed on first run via `playwright install chromium`)
185
+
186
+ ## How It Works
187
+
188
+ This connector uses Playwright for browser automation:
189
+
190
+ 1. **Headless Chrome** — runs a real browser in the background
191
+ 2. **Cookie persistence** — maintains logged-in state across sessions
192
+ 3. **Stealth mode** — uses realistic browser fingerprints to avoid detection
193
+ 4. **Structured responses** — all data returned as JSON
194
+
195
+ ## Security
196
+
197
+ - Session cookies are stored locally in `~/.strider/opentable/cookies.json`
198
+ - No credentials are stored — authentication uses browser-based login
199
+ - Cookies are only readable by the current OS user
200
+
201
+ ## Limitations
202
+
203
+ - OpenTable must be available in your region
204
+ - Some reservation flows may require additional profile information on your OpenTable account
205
+ - Bot-detection countermeasures on OpenTable's site may occasionally interrupt automation
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ git clone https://github.com/markswendsen-code/mcp-opentable.git
211
+ cd mcp-opentable
212
+ npm install
213
+ npx playwright install chromium
214
+ npm run build
215
+ npm start
216
+ ```
217
+
218
+ ## License
219
+
220
+ MIT © [Strider Labs](https://striderlabs.ai)
221
+
222
+ ## Related
223
+
224
+ - [@striderlabs/mcp-doordash](https://www.npmjs.com/package/@striderlabs/mcp-doordash) - DoorDash MCP connector
225
+ - [@striderlabs/mcp-gmail](https://www.npmjs.com/package/@striderlabs/mcp-gmail) - Gmail MCP connector
226
+ - [Model Context Protocol](https://modelcontextprotocol.io) - Learn more about MCP
package/dist/auth.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * OpenTable Authentication & Session Management
3
+ *
4
+ * Handles cookie persistence and login state detection.
5
+ */
6
+ import type { BrowserContext } from "playwright";
7
+ export interface AuthState {
8
+ isLoggedIn: boolean;
9
+ email?: string;
10
+ firstName?: string;
11
+ lastName?: string;
12
+ }
13
+ /**
14
+ * Save cookies from browser context to disk
15
+ */
16
+ export declare function saveCookies(context: BrowserContext): Promise<void>;
17
+ /**
18
+ * Load cookies from disk and add to browser context
19
+ */
20
+ export declare function loadCookies(context: BrowserContext): Promise<boolean>;
21
+ /**
22
+ * Clear stored cookies
23
+ */
24
+ export declare function clearCookies(): void;
25
+ /**
26
+ * Check if we have stored cookies
27
+ */
28
+ export declare function hasStoredCookies(): boolean;
29
+ /**
30
+ * Extract auth state from OpenTable page cookies
31
+ */
32
+ export declare function getAuthState(context: BrowserContext): Promise<AuthState>;
33
+ /**
34
+ * Get the path where cookies are stored
35
+ */
36
+ export declare function getCookiesPath(): string;
package/dist/auth.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * OpenTable Authentication & Session Management
3
+ *
4
+ * Handles cookie persistence and login state detection.
5
+ */
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
7
+ import { homedir } from "os";
8
+ import { join } from "path";
9
+ // Cookie storage location: ~/.strider/opentable/
10
+ const CONFIG_DIR = join(homedir(), ".strider", "opentable");
11
+ const COOKIES_FILE = join(CONFIG_DIR, "cookies.json");
12
+ /**
13
+ * Ensure config directory exists
14
+ */
15
+ function ensureConfigDir() {
16
+ if (!existsSync(CONFIG_DIR)) {
17
+ mkdirSync(CONFIG_DIR, { recursive: true });
18
+ }
19
+ }
20
+ /**
21
+ * Save cookies from browser context to disk
22
+ */
23
+ export async function saveCookies(context) {
24
+ ensureConfigDir();
25
+ const cookies = await context.cookies();
26
+ writeFileSync(COOKIES_FILE, JSON.stringify(cookies, null, 2));
27
+ }
28
+ /**
29
+ * Load cookies from disk and add to browser context
30
+ */
31
+ export async function loadCookies(context) {
32
+ if (!existsSync(COOKIES_FILE)) {
33
+ return false;
34
+ }
35
+ try {
36
+ const cookiesData = readFileSync(COOKIES_FILE, "utf-8");
37
+ const cookies = JSON.parse(cookiesData);
38
+ if (cookies.length > 0) {
39
+ await context.addCookies(cookies);
40
+ return true;
41
+ }
42
+ }
43
+ catch (error) {
44
+ console.error("Failed to load cookies:", error);
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * Clear stored cookies
50
+ */
51
+ export function clearCookies() {
52
+ if (existsSync(COOKIES_FILE)) {
53
+ writeFileSync(COOKIES_FILE, "[]");
54
+ }
55
+ }
56
+ /**
57
+ * Check if we have stored cookies
58
+ */
59
+ export function hasStoredCookies() {
60
+ if (!existsSync(COOKIES_FILE)) {
61
+ return false;
62
+ }
63
+ try {
64
+ const cookiesData = readFileSync(COOKIES_FILE, "utf-8");
65
+ const cookies = JSON.parse(cookiesData);
66
+ return Array.isArray(cookies) && cookies.length > 0;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
72
+ /**
73
+ * Extract auth state from OpenTable page cookies
74
+ */
75
+ export async function getAuthState(context) {
76
+ const cookies = await context.cookies("https://www.opentable.com");
77
+ // OpenTable uses various session/auth cookies
78
+ const sessionCookie = cookies.find((c) => c.name === "OT_SESSION" ||
79
+ c.name === "ot_session" ||
80
+ c.name === "otd" ||
81
+ c.name === "OTUserInfo" ||
82
+ c.name === "ot_userid");
83
+ if (sessionCookie) {
84
+ return {
85
+ isLoggedIn: true,
86
+ };
87
+ }
88
+ return {
89
+ isLoggedIn: false,
90
+ };
91
+ }
92
+ /**
93
+ * Get the path where cookies are stored
94
+ */
95
+ export function getCookiesPath() {
96
+ return COOKIES_FILE;
97
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * OpenTable Browser Automation
3
+ *
4
+ * Playwright-based automation for OpenTable reservation operations.
5
+ */
6
+ import { AuthState } from "./auth.js";
7
+ export interface Restaurant {
8
+ id: string;
9
+ name: string;
10
+ cuisine: string;
11
+ location: string;
12
+ neighborhood?: string;
13
+ rating?: number;
14
+ reviewCount?: number;
15
+ priceRange?: string;
16
+ imageUrl?: string;
17
+ profileUrl?: string;
18
+ }
19
+ export interface RestaurantDetails extends Restaurant {
20
+ description?: string;
21
+ address?: string;
22
+ phone?: string;
23
+ hours?: string;
24
+ website?: string;
25
+ features?: string[];
26
+ }
27
+ export interface AvailabilitySlot {
28
+ time: string;
29
+ partySize: number;
30
+ date: string;
31
+ reservationToken?: string;
32
+ }
33
+ export interface Reservation {
34
+ id: string;
35
+ restaurantName: string;
36
+ date: string;
37
+ time: string;
38
+ partySize: number;
39
+ status: string;
40
+ confirmationNumber?: string;
41
+ specialRequests?: string;
42
+ }
43
+ /**
44
+ * Check if user is logged in to OpenTable
45
+ */
46
+ export declare function checkAuth(): Promise<AuthState>;
47
+ /**
48
+ * Return login URL and instructions for the user to authenticate
49
+ */
50
+ export declare function getLoginUrl(): Promise<{
51
+ url: string;
52
+ instructions: string;
53
+ }>;
54
+ /**
55
+ * Search restaurants on OpenTable
56
+ */
57
+ export declare function searchRestaurants(params: {
58
+ location: string;
59
+ cuisine?: string;
60
+ partySize?: number;
61
+ date?: string;
62
+ time?: string;
63
+ }): Promise<{
64
+ success: boolean;
65
+ restaurants?: Restaurant[];
66
+ error?: string;
67
+ }>;
68
+ /**
69
+ * Get detailed information about a specific restaurant
70
+ */
71
+ export declare function getRestaurantDetails(restaurantId: string): Promise<{
72
+ success: boolean;
73
+ restaurant?: RestaurantDetails;
74
+ error?: string;
75
+ }>;
76
+ /**
77
+ * Check available reservation times for a restaurant
78
+ */
79
+ export declare function checkAvailability(params: {
80
+ restaurantId: string;
81
+ date: string;
82
+ time: string;
83
+ partySize: number;
84
+ }): Promise<{
85
+ success: boolean;
86
+ slots?: AvailabilitySlot[];
87
+ restaurantName?: string;
88
+ error?: string;
89
+ }>;
90
+ /**
91
+ * Make a reservation at a restaurant
92
+ */
93
+ export declare function makeReservation(params: {
94
+ restaurantId: string;
95
+ date: string;
96
+ time: string;
97
+ partySize: number;
98
+ firstName?: string;
99
+ lastName?: string;
100
+ email?: string;
101
+ phone?: string;
102
+ specialRequests?: string;
103
+ confirm: boolean;
104
+ }): Promise<{
105
+ success: boolean;
106
+ reservation?: Partial<Reservation>;
107
+ requiresConfirmation?: boolean;
108
+ preview?: {
109
+ restaurantName: string;
110
+ date: string;
111
+ time: string;
112
+ partySize: number;
113
+ specialRequests?: string;
114
+ };
115
+ error?: string;
116
+ }>;
117
+ /**
118
+ * Get list of upcoming reservations
119
+ */
120
+ export declare function getReservations(): Promise<{
121
+ success: boolean;
122
+ reservations?: Reservation[];
123
+ error?: string;
124
+ }>;
125
+ /**
126
+ * Cancel a reservation
127
+ */
128
+ export declare function cancelReservation(params: {
129
+ reservationId: string;
130
+ confirm: boolean;
131
+ }): Promise<{
132
+ success: boolean;
133
+ requiresConfirmation?: boolean;
134
+ message?: string;
135
+ error?: string;
136
+ }>;
137
+ /**
138
+ * Cleanup browser resources
139
+ */
140
+ export declare function cleanup(): Promise<void>;