Skip to main content
Glama
alcylu

Nightlife Search

get_vip_booking_status

Retrieve VIP booking status for a nightlife venue. Input your booking request ID and matching customer email or phone to get the current status.

Instructions

Fetch VIP booking status from the venue booking workflow. Requires booking request ID and matching customer email or phone.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
booking_request_idYes
customer_emailNo
customer_phoneNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
booking_request_idYes
statusYes
last_updated_atYes
status_messageYes
latest_noteYes
historyYes
deposit_statusYes
deposit_amount_jpyYes
deposit_payment_urlYes

Implementation Reference

  • The actual handler function `getVipBookingStatus` that executes the tool logic. It validates the booking request ID and customer email/phone, fetches the booking from the DB, verifies ownership, loads status event history, loads deposit info, and returns the full status result.
    export async function getVipBookingStatus(
      supabase: SupabaseClient,
      input: GetVipBookingStatusInput,
    ): Promise<VipBookingStatusResult> {
      const { bookingRequestId, customerEmail, customerPhone } = normalizeStatusLookupContacts(input);
    
      const { data: booking, error: bookingError } = await supabase
        .from("vip_booking_requests")
        .select("id,status,updated_at,status_message,customer_email,customer_phone")
        .eq("id", bookingRequestId)
        .maybeSingle<VipBookingLookupRow>();
    
      if (bookingError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch VIP booking request.", {
          cause: bookingError.message,
        });
      }
    
      if (!booking) {
        throw new NightlifeError("BOOKING_REQUEST_NOT_FOUND", "VIP booking request not found.");
      }
    
      const emailMatches = !!customerEmail && booking.customer_email === customerEmail;
      const phoneMatches = !!customerPhone && booking.customer_phone === customerPhone;
      if (!emailMatches && !phoneMatches) {
        throw new NightlifeError("BOOKING_REQUEST_NOT_FOUND", "VIP booking request not found.");
      }
    
      const { data: events, error: eventsError } = await supabase
        .from("vip_booking_status_events")
        .select("to_status,note,created_at")
        .eq("booking_request_id", booking.id)
        .order("created_at", { ascending: true })
        .limit(20);
    
      if (eventsError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch VIP booking history.", {
          cause: eventsError.message,
        });
      }
    
      const history = ((events || []) as VipStatusEventRow[])
        .filter((row) => isVipStatus(row.to_status))
        .map((row) => ({
          status: row.to_status,
          at: row.created_at,
          note: row.note,
        }));
    
      const latestHistory = history[history.length - 1];
    
      // Load deposit info
      let depositStatus: string | null = null;
      let depositAmountJpy: number | null = null;
      let depositPaymentUrl: string | null = null;
    
      const { data: depositRow } = await supabase
        .from("vip_booking_deposits")
        .select("status,amount_jpy,stripe_checkout_url")
        .eq("booking_request_id", booking.id)
        .maybeSingle();
    
      if (depositRow) {
        depositStatus = depositRow.status as string;
        depositAmountJpy = depositRow.amount_jpy as number;
        depositPaymentUrl =
          depositRow.status === "pending" ? (depositRow.stripe_checkout_url as string | null) : null;
      } else {
        // Check booking's denormalized deposit_status (e.g., not_required)
        const { data: bookingDeposit } = await supabase
          .from("vip_booking_requests")
          .select("deposit_status")
          .eq("id", booking.id)
          .single();
        if (bookingDeposit?.deposit_status) {
          depositStatus = bookingDeposit.deposit_status as string;
        }
      }
    
      return {
        booking_request_id: booking.id,
        status: booking.status,
        last_updated_at: booking.updated_at,
        status_message: booking.status_message,
        latest_note: latestHistory?.note || null,
        history,
        deposit_status: depositStatus,
        deposit_amount_jpy: depositAmountJpy,
        deposit_payment_url: depositPaymentUrl,
      };
    }
  • Input schema (`vipBookingStatusInputSchema`) and output schema (`vipBookingStatusOutputSchema`) for the tool, defining validated fields including booking_request_id, customer_email, customer_phone, and the returned status/history/deposit fields.
    export const vipBookingStatusInputSchema = {
      booking_request_id: z.string().min(1),
      customer_email: z.string().optional(),
      customer_phone: z.string().optional(),
    };
    
    export const vipBookingStatusOutputSchema = z.object({
      booking_request_id: z.string(),
      status: z.enum(["submitted", "in_review", "deposit_required", "confirmed", "rejected", "cancelled"]),
      last_updated_at: z.string(),
      status_message: z.string(),
      latest_note: z.string().nullable(),
      history: z.array(vipBookingHistorySchema),
      deposit_status: z.string().nullable(),
      deposit_amount_jpy: z.number().nullable(),
      deposit_payment_url: z.string().nullable(),
    });
  • Tool registration on the MCP server via `server.registerTool('get_vip_booking_status', ...)`, with input/output schemas and a callback that invokes `getVipBookingStatus(deps.supabase, args)`.
    server.registerTool(
      "get_vip_booking_status",
      {
        description:
          "Fetch VIP booking status from the venue booking workflow. Requires booking request ID and matching customer email or phone.",
        inputSchema: vipBookingStatusInputSchema,
        outputSchema: vipBookingStatusOutputSchema,
      },
      async (args) => runTool(
        "get_vip_booking_status",
        vipBookingStatusOutputSchema,
        async () => getVipBookingStatus(deps.supabase, args),
      ),
    );
  • TypeScript interface `VipBookingStatusResult` defining the return type of the handler, including booking_request_id, status, last_updated_at, status_message, latest_note, history, and deposit fields.
    export interface VipBookingStatusResult {
      booking_request_id: string;
      status: VipBookingStatus;
      last_updated_at: string;
      status_message: string;
      latest_note: string | null;
      history: VipBookingStatusHistoryEntry[];
      deposit_status: string | null;
      deposit_amount_jpy: number | null;
      deposit_payment_url: string | null;
    }
  • Helper function `normalizeStatusLookupContacts` that validates and normalizes the booking request input: ensures UUID format for booking_request_id and requires at least one of customer_email or customer_phone.
    function normalizeStatusLookupContacts(input: GetVipBookingStatusInput): {
      bookingRequestId: string;
      customerEmail: string | null;
      customerPhone: string | null;
    } {
      const bookingRequestId = ensureUuid(input.booking_request_id, "booking_request_id");
    
      const emailRaw = String(input.customer_email || "").trim();
      const phoneRaw = String(input.customer_phone || "").trim();
      if (!emailRaw && !phoneRaw) {
        throw new NightlifeError(
          "INVALID_BOOKING_REQUEST",
          "Either customer_email or customer_phone is required.",
        );
      }
    
      return {
        bookingRequestId,
        customerEmail: emailRaw ? normalizeCustomerEmail(emailRaw) : null,
        customerPhone: phoneRaw ? normalizeCustomerPhone(phoneRaw) : null,
      };
    }
Behavior2/5

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

With no annotations, the description must fully convey behavior. It indicates a read operation (fetch) but lacks details on error handling, authorization beyond field matching, rate limits, or side effects. The minimal information leaves ambiguity about what happens on mismatches or missing data.

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

Conciseness5/5

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

The description is two concise sentences, front-loading the primary purpose and following with key requirement. No unnecessary words or repetition.

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

Completeness3/5

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

Given the presence of an output schema, the description does not need to explain return values. It covers basic purpose and one requirement, but omits guidance on common error cases or how to interpret responses. For a simple fetch, it is minimally adequate.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It explains that booking_request_id is required and customer_email/phone are needed for matching, adding context beyond the schema. However, it does not clarify which optional field must be provided or format requirements.

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

Purpose4/5

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

The description clearly states the tool fetches VIP booking status from the venue booking workflow, specifying the resource and action. It distinguishes from sibling tools like create or cancel, but does not explicitly differentiate from other get tools like get_guest_list_entry_status.

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

Usage Guidelines2/5

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

The description mentions required fields but provides no guidance on when to use this tool versus alternatives. It does not state scenarios where this tool is appropriate or not, nor does it reference sibling tools.

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