create_vip_booking_request
Submit VIP table booking requests to nightlife venues that support VIP reservations. This tool sends booking details directly to venue booking desks for processing.
Instructions
Create a VIP table booking request and send it directly to the venue booking desk. The venue must have vip_booking_supported=true. Before calling this tool, always confirm booking date and arrival time in venue local time. For arrivals from 00:00 to 05:59, use the 'night + actual day' format to avoid midnight confusion. Required format: '[Night] night, [time] ([Actual Day] [time])' — e.g., 'Friday night, 2am (Saturday 2am)'. Example confirmation: 'So you're coming Friday night, 2am (Saturday 2am), table for 4 at Zouk?' If the user gives a time like 2am without a day, ask: 'Do you mean Thursday night, 2am (Friday morning), or Friday night, 2am (Saturday morning)?' If the user changes the requested day, regenerate confirmation before calling this tool.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| venue_id | Yes | ||
| booking_date | Yes | ||
| arrival_time | Yes | ||
| party_size | Yes | ||
| customer_name | Yes | ||
| customer_email | Yes | ||
| customer_phone | Yes | ||
| preferred_table_code | No | ||
| special_requests | No |
Implementation Reference
- src/services/vipBookings.ts:679-797 (handler)The main handler function `createVipBookingRequest` which processes the VIP booking creation request, validates inputs, interacts with Supabase to insert the booking, and triggers related events and tasks.
export async function createVipBookingRequest( supabase: SupabaseClient, input: CreateVipBookingRequestInput, options?: { resendApiKey?: string }, ): Promise<VipBookingCreateResult> { const venueId = ensureUuid(input.venue_id, "venue_id"); const bookingDate = normalizeBookingDate(input.booking_date); const arrivalTime = normalizeArrivalTime(input.arrival_time); const partySize = normalizePartySize(input.party_size); const customerName = normalizeCustomerName(input.customer_name); const customerEmail = normalizeCustomerEmail(input.customer_email); const customerPhone = normalizeCustomerPhone(input.customer_phone); const preferredTableCode = normalizeOptionalTableCode(input.preferred_table_code); const specialRequests = normalizeOptionalText(input.special_requests, 2000); const window = await resolveBookingWindow(supabase, venueId); if (bookingDate < window.currentServiceDate || bookingDate > window.maxServiceDate) { throw new NightlifeError( "INVALID_BOOKING_REQUEST", `booking_date must be between ${window.currentServiceDate} and ${window.maxServiceDate}.`, ); } let minSpend: number | null = null; let minSpendCurrency: string | null = null; let tableWarning: string | null = null; if (preferredTableCode) { const pricing = await lookupTablePricing(supabase, venueId, preferredTableCode, bookingDate); if (!pricing.found) { tableWarning = `Table "${preferredTableCode}" was not found in our system for this venue. The booking request has been submitted and the venue will confirm table availability.`; } minSpend = pricing.minSpend; minSpendCurrency = pricing.currency; } const { data: created, error: createError } = await supabase .from("vip_booking_requests") .insert({ venue_id: venueId, booking_date: bookingDate, arrival_time: arrivalTime, party_size: partySize, customer_name: customerName, customer_email: customerEmail, customer_phone: customerPhone, preferred_table_code: preferredTableCode, min_spend: minSpend, min_spend_currency: minSpendCurrency, special_requests: specialRequests, status: "submitted", status_message: DEFAULT_STATUS_MESSAGE, }) .select("id,status,created_at,status_message") .single<VipBookingInsertRow>(); if (createError || !created) { throw new NightlifeError("REQUEST_WRITE_FAILED", "Failed to create VIP booking request.", { cause: createError?.message || "Unknown insert error", }); } const { error: eventError } = await supabase .from("vip_booking_status_events") .insert({ booking_request_id: created.id, from_status: null, to_status: "submitted", actor_type: "customer", note: "VIP booking request sent to venue booking desk.", }); const { error: taskError } = await supabase .from("vip_agent_tasks") .insert({ booking_request_id: created.id, task_type: "new_vip_request", status: "pending", attempt_count: 0, next_attempt_at: new Date().toISOString(), }); if (eventError || taskError) { throw new NightlifeError( "REQUEST_WRITE_FAILED", "Failed to submit VIP booking request.", { cause: { status_event_error: eventError?.message || null, task_error: taskError?.message || null, }, }, ); } // Send submitted email (fire-and-forget) if (options?.resendApiKey) { try { const { sendBookingSubmittedEmail } = await import("./email.js"); await sendBookingSubmittedEmail(supabase, options.resendApiKey, created.id); } catch (emailError) { logEvent("email.send_error", { booking_request_id: created.id, error: emailError instanceof Error ? emailError.message : "Unknown error", }); } } return { booking_request_id: created.id, status: created.status, created_at: created.created_at, message: created.status_message, preferred_table_code: preferredTableCode, min_spend: minSpend, min_spend_currency: minSpendCurrency, table_warning: tableWarning, }; } - src/tools/vipBookings.ts:141-156 (registration)Registration of the `create_vip_booking_request` tool in the MCP server using `registerVipBookingTools`.
export function registerVipBookingTools(server: McpServer, deps: ToolDeps): void { server.registerTool( "create_vip_booking_request", { description: createVipBookingToolDescription, inputSchema: createVipBookingInputSchema, outputSchema: createVipBookingOutputSchema, }, async (args) => runTool( "create_vip_booking_request", createVipBookingOutputSchema, async () => createVipBookingRequest(deps.supabase, args, { resendApiKey: deps.config.resendApiKey ?? undefined, }), ), ); - src/tools/vipBookings.ts:18-28 (schema)Input schema definition for the `create_vip_booking_request` tool using Zod.
export const createVipBookingInputSchema = { venue_id: z.string().min(1), booking_date: z.string().min(1), arrival_time: z.string().min(1), party_size: z.number().int().min(1).max(30), customer_name: z.string().min(1), customer_email: z.string().min(1), customer_phone: z.string().min(1), preferred_table_code: z.string().optional(), special_requests: z.string().optional(), }; - src/tools/vipBookings.ts:30-39 (schema)Output schema definition for the `create_vip_booking_request` tool using Zod.
export const createVipBookingOutputSchema = z.object({ booking_request_id: z.string(), status: z.enum(["submitted", "in_review", "deposit_required", "confirmed", "rejected", "cancelled"]), created_at: z.string(), message: z.string(), preferred_table_code: z.string().nullable(), min_spend: z.number().nullable(), min_spend_currency: z.string().nullable(), table_warning: z.string().nullable(), });