submit_to_guest_list
Submit guest list entries for nightlife events or venues to receive door instructions and benefits confirmation.
Instructions
Submit a guest list entry for an event or venue. Provide either event_id or venue_id + service_date. Returns confirmation with door instructions and guest list benefits if available.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| event_id | No | ||
| venue_id | No | ||
| service_date | No | ||
| customer_name | Yes | ||
| party_size | No | ||
| customer_email | Yes | ||
| customer_phone | No | ||
| messaging_channel | No | ||
| messaging_handle | No | ||
| language | No | en | |
| source | No | concierge |
Implementation Reference
- src/services/guestList.ts:274-400 (handler)The actual logic for submitting a guest list entry, resolving context, checking settings, and processing the submission.
export async function submitToGuestList( supabase: SupabaseClient, input: SubmitInput, ): Promise<SubmitOutput> { // 1. Resolve event context const ctx = await resolveEventContext( supabase, input.event_id, input.venue_id, input.service_date, ); // 2. Load guest list settings const settings = await loadGuestListSettings(supabase, ctx.event_day_id, ctx.venue_id); if (!settings || settings.enabled === false) { throw new NightlifeError( "GUEST_LIST_NOT_AVAILABLE", "Guest list is not available for this event", ); } // 3. Check cutoff time if (settings.cutoff_time && ctx.service_date) { const now = new Date(); const tokyoFormatter = new Intl.DateTimeFormat("en-US", { timeZone: "Asia/Tokyo", hour: "2-digit", minute: "2-digit", hourCycle: "h23", }); const nowParts = tokyoFormatter.formatToParts(now); const nowHour = Number(nowParts.find((p) => p.type === "hour")?.value ?? 0); const nowMinute = Number(nowParts.find((p) => p.type === "minute")?.value ?? 0); const nowMinutes = nowHour * 60 + nowMinute; const [cutoffH, cutoffM] = settings.cutoff_time.split(":").map(Number); const cutoffMinutes = cutoffH * 60 + cutoffM; // Only check cutoff if the service date is today const dateFormatter = new Intl.DateTimeFormat("en-CA", { timeZone: "Asia/Tokyo" }); const todayStr = dateFormatter.format(now); if (ctx.service_date === todayStr && nowMinutes >= cutoffMinutes) { return { entry_id: "", status: "closed", event_name: ctx.event_name, event_date: ctx.event_date, message: "Guest list has closed for tonight. Consider VIP table booking instead.", guest_list_benefit: null, door_instructions: null, }; } } // 4. Check capacity if (settings.capacity) { const currentCount = await countExistingEntries( supabase, ctx.event_day_id, ctx.venue_id, ctx.service_date, ); if (currentCount >= settings.capacity) { return { entry_id: "", status: "full", event_name: ctx.event_name, event_date: ctx.event_date, message: "Guest list is full for this event. Consider VIP table booking instead.", guest_list_benefit: null, door_instructions: null, }; } } // 5. Check duplicate const isDuplicate = await checkDuplicate( supabase, input.customer_email, ctx.event_day_id, ctx.venue_id, ctx.service_date, ); if (isDuplicate) { throw new NightlifeError( "GUEST_LIST_DUPLICATE", "This email is already on the guest list for this event", ); } // 6. Insert entry const insertData: Record<string, unknown> = { email: input.customer_email.toLowerCase(), name: input.customer_name, group_size: input.party_size, language: input.language, source: input.source, }; if (input.customer_phone) insertData.messaging_handle = input.customer_phone; if (input.messaging_channel) insertData.messaging_channel = input.messaging_channel; if (input.messaging_handle) insertData.messaging_handle = input.messaging_handle; if (ctx.event_day_id) insertData.event_day_id = ctx.event_day_id; if (ctx.venue_id) insertData.venue_id = ctx.venue_id; if (ctx.service_date) insertData.service_date = ctx.service_date; const { data: entry, error } = await supabase .from("event_guest_list_entries") .insert(insertData) .select("id, created_at") .single(); if (error) { throw new NightlifeError("DB_QUERY_FAILED", `Failed to create guest list entry: ${error.message}`); } const message = settings.confirmation_message_en || "You're on the guest list! Show this confirmation at the door."; return { entry_id: entry.id, status: "confirmed", event_name: ctx.event_name, event_date: ctx.event_date, message, guest_list_benefit: settings.benefit_en ?? null, - src/tools/guestList.ts:115-129 (registration)The MCP tool registration for 'submit_to_guest_list' within the registerGuestListTools function.
export function registerGuestListTools(server: McpServer, deps: ToolDeps): void { server.registerTool( "submit_to_guest_list", { description: "Submit a guest list entry for an event or venue. Provide either event_id or venue_id + service_date. Returns confirmation with door instructions and guest list benefits if available.", inputSchema: submitToGuestListInputSchema, outputSchema: submitToGuestListOutputSchema, }, async (args) => runTool( "submit_to_guest_list", submitToGuestListOutputSchema, async () => submitToGuestList(deps.supabase, args), ), ); - src/tools/guestList.ts:15-27 (schema)Input schema definition for the submit_to_guest_list tool.
export const submitToGuestListInputSchema = { event_id: z.string().optional(), venue_id: z.string().optional(), service_date: z.string().optional(), customer_name: z.string().min(1), party_size: z.number().int().min(1).max(20).default(1), customer_email: z.string().min(1), customer_phone: z.string().optional(), messaging_channel: z.string().optional(), messaging_handle: z.string().optional(), language: z.string().default("en"), source: z.string().default("concierge"), };