Skip to main content
Glama
alcylu

Nightlife Search

by alcylu

get_vip_pricing

Retrieve VIP table pricing and minimum spend requirements for nightlife venues, including weekday/weekend ranges and booking availability.

Instructions

Get VIP pricing information for a venue. Returns honest weekday and weekend minimum spend ranges, zone summaries, table chart image URL, and booking affordance.

WHEN TO CALL: When a user asks about VIP tables, VIP pricing, bottle service costs, minimum spend, or table reservations at a specific venue.

WHAT TO DO AFTER:

  • Present pricing conversationally ("Weekday minimums start around ¥100K, weekends from ¥200K")

  • Show table chart URL as a layout reference only — do not infer availability from the image

  • If booking_supported is true and user is interested, offer to submit an inquiry via create_vip_booking_request

  • Do NOT suggest specific table codes unless the user asks

  • If busy_night is true, mention the event name and that demand may be higher on that night

  • When pricing_approximate is true, use hedging language ("around", "approximately") rather than stating exact figures

When venue_open is false, the venue is closed on that specific date but general pricing ranges for open nights are still included. Present the open nights and pricing to the user.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
venue_idYes
dateNo

Implementation Reference

  • The main implementation of the `get_vip_pricing` service function which fetches venue details, pricing information, and availability.
    export async function getVipPricing(
      supabase: SupabaseClient,
      input: GetVipPricingInput,
    ): Promise<VipPricingResult> {
      // 1. Validate UUID
      const venueId = ensureUuid(input.venue_id, "venue_id");
    
      // 2. Resolve venue
      const venue = await resolveVenueForPricing(supabase, venueId);
      const venueName = venue.name;
    
      // 3. Resolve city context
      const city = await fetchPricingCityContext(supabase, venue.city_id);
    
      // 4. Determine service date (null when no date requested)
      let serviceDate: string | null = null;
      const dateInput = input.date?.trim().toLowerCase();
      if (dateInput === "tonight") {
        serviceDate = getCurrentServiceDate(new Date(), city.timezone, city.cutoff);
      } else if (dateInput && /^\d{4}-\d{2}-\d{2}$/.test(dateInput)) {
        serviceDate = dateInput;
      }
      // When no date provided: serviceDate stays null — return general pricing only
    
      // 5. Run open-day check (only when a specific date was requested)
      let venueOpen: boolean | null = null;
      let venueClosedMessage: string | null = null;
      let eventName: string | null = null;
      if (serviceDate) {
        const { closedDates, eventByDate } = await resolvePricingClosedDates(supabase, venueId, [serviceDate], city);
        venueOpen = !closedDates.has(serviceDate);
        venueClosedMessage = venueOpen
          ? null
          : `${venueName || "This venue"} appears to be closed on ${serviceDate}.`;
        eventName = eventByDate.get(serviceDate) ?? null;
      }
    
      // 6. Fetch active vip_venue_tables
      const { data: tableData, error: tableError } = await supabase
        .from("vip_venue_tables")
        .select("id,table_code,table_name,metadata,zone,capacity_min,capacity_max")
        .eq("venue_id", venueId)
        .eq("is_active", true);
    
      if (tableError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to load VIP venue tables.", {
          cause: tableError.message,
        });
      }
    
      const tables = (tableData || []) as VipVenueTableRow[];
    
      // 7. Fetch ALL vip_table_day_defaults for venue
      const { data: dayDefaultData, error: dayDefaultError } = await supabase
        .from("vip_table_day_defaults")
        .select("vip_venue_table_id,day_of_week,min_spend,currency,note")
        .eq("venue_id", venueId);
    
      if (dayDefaultError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to load VIP table day defaults.", {
          cause: dayDefaultError.message,
        });
      }
    
      const dayDefaults = (dayDefaultData || []) as VipTableDayDefaultRow[];
    
      // 8. Check per-date overrides from vip_table_availability
      let eventPricingNote: string | null = null;
      const { data: dateOverrideData } = await supabase
        .from("vip_table_availability")
        .select("min_spend")
        .eq("venue_id", venueId)
        .eq("booking_date", serviceDate);
    
      const overrideRows = (dateOverrideData || []) as Array<{ min_spend: number | string | null }>;
      const hasOverridesWithPricing = overrideRows.some((r) => coerceMinSpend(r.min_spend) !== null);
      if (hasOverridesWithPricing) {
        eventPricingNote = `Special event pricing may apply on ${serviceDate} — actual minimums may differ from the general ranges shown.`;
      }
    
      // 9. Aggregate pricing
      const aggregated = aggregatePricing(tables, dayDefaults);
    
      // 10. Determine pricing_configured and pricing_approximate
      const venueDefaultMinSpend = coerceMinSpend(venue.vip_default_min_spend);
      const pricingConfigured = dayDefaults.length > 0 || venueDefaultMinSpend !== null;
      const pricingNotConfiguredMessage = pricingConfigured
        ? null
        : "VIP pricing information is not yet available for this venue.";
      const pricingApproximate = dayDefaults.length === 0 && venueDefaultMinSpend !== null;
    
      // 11. Extract layout_image_url
      const layoutImageUrl =
        tables.map((t) => extractLayoutImageUrl(t.metadata)).find(Boolean) || null;
    
      // 12. Determine booking fields
      const bookingSupported = venue.vip_booking_enabled === true;
    
      // 13. Return result
      const busyNight = eventName !== null;
      return {
        venue_id: venueId,
        venue_name: venueName,
        venue_open: venueOpen === null ? true : venueOpen,
        venue_closed_message: venueClosedMessage,
        pricing_configured: pricingConfigured,
        pricing_not_configured_message: pricingNotConfiguredMessage,
        weekday_min_spend: aggregated.weekday_min_spend,
        weekend_min_spend: aggregated.weekend_min_spend,
        currency: aggregated.currency,
        zones: aggregated.zones,
        layout_image_url: layoutImageUrl,
        booking_supported: bookingSupported,
        booking_note: null,
        generated_at: new Date().toISOString(),
        service_date: serviceDate,
        event_pricing_note: eventPricingNote,
        event_name: eventName,
        busy_night: busyNight,
        pricing_approximate: pricingApproximate,
      };
    }
  • Registration of the `get_vip_pricing` tool within the McpServer, including description and schema definitions.
    export function registerVipPricingTool(server: McpServer, deps: ToolDeps): void {
      server.registerTool(
        "get_vip_pricing",
        {
          description: VIP_PRICING_DESCRIPTION,
          inputSchema: vipPricingInputSchema,
          outputSchema: vipPricingOutputSchema,
        },
        async (args) => runTool(
          "get_vip_pricing",
          vipPricingOutputSchema,
          async () => getVipPricing(deps.supabase, args),
        ),
      );
    }
  • Zod schema definition for the `get_vip_pricing` tool's output.
    export const vipPricingOutputSchema = z.object({
      venue_id: z.string(),
      venue_name: z.string().nullable(),
      venue_open: z.boolean(),
      venue_closed_message: z.string().nullable(),
      pricing_configured: z.boolean(),
      pricing_not_configured_message: z.string().nullable(),
      weekday_min_spend: z.number().nullable(),
      weekend_min_spend: z.number().nullable(),
      currency: z.string(),
      zones: z.array(vipZonePricingSummarySchema),
      layout_image_url: z.string().nullable(),
      booking_supported: z.boolean(),
      booking_note: z.string().nullable(),
      generated_at: z.string(),
      service_date: z.string().nullable(),
      event_pricing_note: z.string().nullable(),
      event_name: z.string().nullable(),
      busy_night: z.boolean(),
      pricing_approximate: z.boolean(),
    });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alcylu/nightlife-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server