Skip to main content
Glama
alcylu

Nightlife Search

by alcylu

get_venue_info

Retrieve detailed venue information including upcoming events and VIP booking options for nightlife planning.

Instructions

Get full details for a specific venue ID, including upcoming events and VIP booking support.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
venue_idYes

Implementation Reference

  • The main implementation of the get_venue_info logic, which fetches venue details from the database, resolves city context, retrieves upcoming events, and builds the VenueDetail object.
    export async function getVenueInfo(
      supabase: SupabaseClient,
      config: AppConfig,
      venueId: string,
    ): Promise<VenueDetail | null> {
      const cleanedId = venueId.trim();
      if (!cleanedId) {
        throw new NightlifeError("INVALID_VENUE_ID", "venue_id cannot be blank.");
      }
      if (!UUID_RE.test(cleanedId)) {
        throw new NightlifeError("INVALID_VENUE_ID", "venue_id must be a UUID.");
      }
    
      const { data: venue, error: venueError } = await supabase
        .from("venues")
        .select(VENUE_SELECT)
        .eq("id", cleanedId)
        .maybeSingle<VenueRow>();
    
      if (venueError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch venue details.", {
          cause: venueError.message,
        });
      }
      if (!venue) {
        return null;
      }
    
      const fallbackCity = await getCityContext(
        supabase,
        config.defaultCity,
        config.defaultCountryCode,
      );
    
      const cityContext = venue.city_id
        ? await supabase
            .from("cities")
            .select("id,slug,timezone,service_day_cutoff_time,country_code")
            .eq("id", venue.city_id)
            .maybeSingle<{
              id: string;
              slug: string;
              timezone: string;
              service_day_cutoff_time: string;
              country_code: string;
            }>()
        : { data: null, error: null };
    
      if (cityContext.error) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to resolve venue city context.", {
          cause: cityContext.error.message,
        });
      }
    
      const citySlug =
        cityContext.data?.slug?.toLowerCase() || fallbackCity?.slug || config.defaultCity;
      const timezone = cityContext.data?.timezone || fallbackCity?.timezone || "UTC";
      const cutoffTime =
        cityContext.data?.service_day_cutoff_time ||
        fallbackCity?.serviceDayCutoffTime ||
        "06:00";
      const fallbackCurrency = defaultCurrencyForCountry(
        cityContext.data?.country_code || fallbackCity?.countryCode || config.defaultCountryCode,
      );
    
      const now = new Date();
      const currentServiceDate = getCurrentServiceDate(now, timezone, cutoffTime);
      const window = serviceDateWindowToUtc(
        currentServiceDate,
        addDaysToIsoDate(currentServiceDate, 31),
        timezone,
        cutoffTime,
      );
    
      const { data: occurrenceRows, error: occurrenceError } = await supabase
        .from("event_occurrences")
        .select(OCCURRENCE_SELECT)
        .eq("published", true)
        .eq("venue_id", venue.id)
        .gte("start_at", window.startIso)
        .lt("start_at", window.endIso)
        .order("start_at", { ascending: true })
        .range(0, 49);
    
      if (occurrenceError) {
        throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch venue events.", {
          cause: occurrenceError.message,
        });
      }
    
      const occurrences = (occurrenceRows || []) as unknown as EventOccurrenceRow[];
      const vipHourOccurrences = buildVipHoursSyntheticOccurrences(
        venue,
        currentServiceDate,
        addDaysToIsoDate(currentServiceDate, 31),
        timezone,
      );
      const effectiveOccurrences = [...occurrences, ...vipHourOccurrences].sort((a, b) =>
        String(a.start_at || "").localeCompare(String(b.start_at || "")),
      );
      const metadata = await fetchOccurrenceMetadata(
        supabase,
        occurrences.map((row) => row.id),
      );
    
      const upcomingEvents = effectiveOccurrences
        .map((row) =>
          toEventSummary(row, citySlug, config.nightlifeBaseUrl, fallbackCurrency, metadata),
        )
        .slice(0, 5);
    
      return {
        venue_id: venue.id,
        name: venueName(venue),
        area: venueArea(venue),
        address: venueAddress(venue),
        website: venue.website || null,
        image_url: venue.image_url || null,
        vip_booking_supported: venue.vip_booking_enabled === true,
        sns_instagram: venue.sns_instagram || null,
        sns_tiktok: venue.sns_tiktok || null,
        sns_x: venue.sns_x || null,
        sns_youtube: venue.sns_youtube || null,
        guest_list_enabled: venue.guest_list_enabled,
        upcoming_event_count: effectiveOccurrences.length,
        upcoming_events: upcomingEvents,
        nlt_url: buildVenueUrl(config.nightlifeBaseUrl, citySlug, venue.id),
      };
    }
  • Registration of the get_venue_info tool in the MCP server, including its input schema and the handler wrapper.
    server.registerTool(
      "get_venue_info",
      {
        description:
          "Get full details for a specific venue ID, including upcoming events and VIP booking support.",
        inputSchema: {
          venue_id: z.string().min(1),
        },
        outputSchema: venueDetailSchema,
      },
      async ({ venue_id }) => runTool(
        "get_venue_info",
        venueDetailSchema,
        async () => {
          const detail = await getVenueInfo(deps.supabase, deps.config, venue_id);
          if (!detail) {
            throw new NightlifeError("VENUE_NOT_FOUND", `Venue not found: ${venue_id}`);
          }
          return detail;
        },
      ),
    );
  • Schema definition for the output of the get_venue_info tool using Zod.
    const venueDetailSchema = z.object({
      venue_id: z.string(),
      name: z.string(),
      area: z.string().nullable(),
      address: z.string().nullable(),
      website: z.string().nullable(),
      image_url: z.string().nullable(),
      vip_booking_supported: z.boolean(),
      sns_instagram: z.string().nullable(),
      sns_tiktok: z.string().nullable(),
      sns_x: z.string().nullable(),
      sns_youtube: z.string().nullable(),
      guest_list_enabled: z.boolean().nullable(),
      upcoming_event_count: z.number().int().min(0),
      upcoming_events: z.array(eventSummarySchema),
      nlt_url: z.string(),
    });

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