Skip to main content
Glama
alcylu

Nightlife Search

get_vip_pricing

Retrieve VIP pricing information for a venue, including minimum spend ranges, zone summaries, and table chart image URL, to help users understand bottle service costs and table reservation options.

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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
venue_idYes
venue_nameYes
venue_openYes
venue_closed_messageYes
pricing_configuredYes
pricing_not_configured_messageYes
weekday_min_spendYes
weekend_min_spendYes
currencyYes
zonesYes
layout_image_urlYes
booking_supportedYes
booking_noteYes
generated_atYes
service_dateYes
event_pricing_noteYes
event_nameYes
busy_nightYes
pricing_approximateYes

Implementation Reference

  • Main service function that computes VIP pricing for a venue. Validates venue_id, resolves venue/city context, determines service date, checks open/closed status, fetches VIP tables and day defaults, aggregates pricing by weekday/weekend and zone, and returns the full VipPricingResult.
    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,
      };
    }
  • Input type definition for the getVipPricing function: venue_id (UUID) and optional date (tonight or YYYY-MM-DD).
    export type GetVipPricingInput = {
      venue_id: string;
      date?: string; // "tonight" | YYYY-MM-DD | undefined
    };
  • Return type (VipPricingResult) defining the shape of the pricing response including venue info, min spends, zones with summaries, booking support, and event/pricing metadata.
    export interface VipPricingResult {
      venue_id: string;
      venue_name: string | null;
      venue_open: boolean;
      venue_closed_message: string | null;
      pricing_configured: boolean;
      pricing_not_configured_message: string | null;
      weekday_min_spend: number | null;
      weekend_min_spend: number | null;
      currency: string;
      zones: VipZonePricingSummary[];
      layout_image_url: string | null;
      booking_supported: boolean;
      booking_note: string | null;
      generated_at: string;
      service_date: string | null;
      event_pricing_note: string | null;
      event_name: string | null;       // name of event on requested date
      busy_night: boolean;             // true when event exists on requested date
      pricing_approximate: boolean;    // true when pricing from venue-level default (no day-defaults)
  • VipZonePricingSummary interface defining per-zone pricing data including capacity range and min spends.
    export interface VipZonePricingSummary {
      zone: string;
      capacity_min: number | null;
      capacity_max: number | null;
      weekday_min_spend: number | null;
      weekend_min_spend: number | null;
      currency: string;
  • Registers the 'get_vip_pricing' tool on the McpServer with Zod input/output schemas, description, and a handler that calls getVipPricing from services.
    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),
        ),
      );
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It covers important behaviors: handling venue_open=false (returns open nights' pricing), pricing_approximate flag (use hedging language), and busy_night (mention event). Missing details on authentication, rate limits, or error cases, but adequate for a read-only tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and includes clear 'WHEN TO CALL' and 'WHAT TO DO AFTER' sections. It is somewhat verbose but well-structured. Every section serves a purpose, though some post-call guidance could be considered agent behavior rather than tool definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the existence of an output schema (not shown), the description does not need to explain return values but still summarizes them. It covers edge cases (venue closed, approximate pricing, busy night). Complete enough for a pricing retrieval tool, though missing pagination or request limits.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description does not explain the parameters (venue_id, date) beyond the context. It does not specify date format, requiredness, or how venue_id is obtained. The description focuses on output and post-call actions rather than input semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Get', the resource 'VIP pricing for a venue', and lists specific returns (minimum spend ranges, zone summaries, table chart URL, booking affordance). This distinguishes it from sibling tools like create_vip_booking_request or get_vip_booking_status, which handle booking operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'WHEN TO CALL' section explicitly lists scenarios (VIP tables, pricing, bottle service, etc.), providing clear context. However, it does not explicitly state when not to use this tool or suggest alternatives, though the sibling list implies distinctions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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