get_event_details
Retrieve comprehensive event information using a specific UUID to access details like venue, performers, schedules, and ticket availability for nightlife planning.
Instructions
Get full details for a specific event occurrence ID (UUID).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| event_id | Yes |
Implementation Reference
- src/services/events.ts:905-1095 (handler)Implementation of getEventDetails service function, responsible for fetching event occurrence data, ticket tiers, guest list settings, and formatting the response.
export async function getEventDetails( supabase: SupabaseClient, config: AppConfig, eventId: string, ): Promise<EventDetail | null> { const cleanedId = eventId.trim(); if (!cleanedId) { throw new NightlifeError("INVALID_EVENT_ID", "event_id cannot be blank."); } if (cleanedId.startsWith("virtual:")) { throw new NightlifeError( "UNSUPPORTED_EVENT_ID", "virtual:* IDs are reserved for future support. Use a concrete event occurrence ID.", ); } if (!UUID_RE.test(cleanedId)) { throw new NightlifeError( "INVALID_EVENT_ID", "event_id must be a UUID in v1.", ); } const { data: occurrence, error } = await supabase .from("event_occurrences") .select( "id,city_id,venue_id,name_en,name_i18n,description_en,description_i18n,start_at,end_at,published,featured,source,source_url,entrance_costs,venue:venues(id,name,name_en,name_ja,address,address_en,address_ja,city,city_en,city_ja,website),occurrence_days:event_occurrence_days(id,service_date,start_at,end_at,published,title_en_override,title_i18n_override)", ) .eq("id", cleanedId) .eq("published", true) .maybeSingle<EventOccurrenceRow>(); if (error) { throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch event details.", { cause: error.message, }); } if (!occurrence) { return null; } const cityQuery = occurrence.city_id ? await supabase .from("cities") .select("slug,timezone,service_day_cutoff_time,country_code") .eq("id", occurrence.city_id) .maybeSingle() : { data: null }; const citySlug = (cityQuery.data?.slug as string | undefined)?.toLowerCase() || config.defaultCity; const fallbackCurrency = defaultCurrencyForCountry( (cityQuery.data?.country_code as string | undefined) || config.defaultCountryCode, ); const timezone = (cityQuery.data?.timezone as string | undefined) || "UTC"; const cutoffTime = (cityQuery.data?.service_day_cutoff_time as string | undefined) || "06:00"; const dayIds = (occurrence.occurrence_days || []) .map((day) => day.id) .filter(Boolean); const [metadata, ticketOccurrenceResult, ticketDayResult, guestSettingsResult] = await Promise.all([ fetchOccurrenceMetadata(supabase, [occurrence.id]), supabase .from("event_ticket_tiers") .select("tier_name,price,currency,status,url,provider") .eq("occurrence_id", occurrence.id), dayIds.length > 0 ? supabase .from("event_ticket_tiers") .select("tier_name,price,currency,status,url,provider,event_day_id") .in("event_day_id", dayIds) : Promise.resolve({ data: [], error: null }), dayIds.length > 0 ? supabase .from("event_guest_list_settings") .select("event_day_id,enabled,capacity,cutoff_time") .in("event_day_id", dayIds) : Promise.resolve({ data: [], error: null }), ]); if (ticketOccurrenceResult.error) { throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch ticket tiers.", { cause: ticketOccurrenceResult.error.message, }); } if (ticketDayResult.error) { throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch day ticket tiers.", { cause: ticketDayResult.error.message, }); } if (guestSettingsResult.error) { throw new NightlifeError("DB_QUERY_FAILED", "Failed to fetch guest list settings.", { cause: guestSettingsResult.error.message, }); } const tiers = [ ...(ticketOccurrenceResult.data || []), ...(ticketDayResult.data || []), ] .map((row) => ({ tier_name: String(row.tier_name || "Unknown Tier"), price: typeof row.price === "number" ? row.price : null, currency: row.currency || null, status: row.status || "on_sale", url: row.url || null, provider: row.provider || null, })) .sort((a, b) => a.tier_name.localeCompare(b.tier_name)); const lowerTier = (value: string) => value.toLowerCase(); const doorTier = tiers.find((tier) => lowerTier(tier.tier_name).includes("door")); const advanceTier = tiers.find((tier) => { const value = lowerTier(tier.tier_name); return value.includes("advance") || value.includes("presale") || value.includes("early"); }); const day = primaryDay(occurrence.occurrence_days); const settings = (guestSettingsResult.data || []).find( (row) => row.event_day_id === day?.id, ); let guestStatus: "available" | "full" | "closed" = "closed"; if (settings?.enabled === true && day?.service_date) { const cutoffPassed = isCutoffPassedForServiceDate( new Date(), day.service_date, timezone, settings.cutoff_time || cutoffTime, ); if (!cutoffPassed) { if (typeof settings.capacity === "number" && settings.capacity >= 0) { const countResult = await supabase .from("event_guest_list_entries") .select("id", { count: "exact", head: true }) .eq("event_day_id", day.id); if (countResult.error) { throw new NightlifeError( "DB_QUERY_FAILED", "Failed to fetch guest list counts.", { cause: countResult.error.message }, ); } const currentCount = countResult.count || 0; guestStatus = currentCount >= settings.capacity ? "full" : "available"; } else { guestStatus = "available"; } } } const flyer = metadata.mediaByEvent.get(occurrence.id)?.[0]?.media_url || null; const lineup = metadata.lineupByEvent.get(occurrence.id) || []; const venue = firstRelation(occurrence.venue); const address = venue?.address_en || venue?.address || venue?.address_ja || null; return { event_id: occurrence.id, name: eventName(occurrence), date: day?.start_at || occurrence.start_at || "", start_time: day?.start_at || occurrence.start_at || null, end_time: day?.end_at || occurrence.end_at || null, service_date: day?.service_date || null, venue: { id: venue?.id || occurrence.venue_id || "", name: venueName(venue), area: venueArea(venue), address, map_link: buildMapLink(address), website: venue?.website || null, }, lineup, genres: metadata.genresByEvent.get(occurrence.id) || [], price: { entrance_summary: summarizeEntranceCosts(occurrence.entrance_costs, fallbackCurrency), door: doorTier ? tierText(doorTier, fallbackCurrency) : null, advance: advanceTier ? tierText(advanceTier, fallbackCurrency) : null, tiers, }, flyer_url: flyer, guest_list_status: guestStatus, nlt_url: buildEventUrl(config.nightlifeBaseUrl, citySlug, occurrence.id), }; } - src/tools/events.ts:214-238 (registration)Registration of the get_event_details MCP tool.
server.registerTool( "get_event_details", { description: "Get full details for a specific event occurrence ID (UUID).", inputSchema: { event_id: z.string().min(1), }, outputSchema: eventDetailSchema, }, async ({ event_id }) => runTool( "get_event_details", eventDetailSchema, async () => { const detail = await getEventDetails(deps.supabase, deps.config, event_id); if (!detail) { throw new NightlifeError( "EVENT_NOT_FOUND", `Event not found: ${event_id}`, ); } return detail; }, ), ); - src/tools/events.ts:46-88 (schema)Schema definition for the event details output.
const eventDetailSchema = z.object({ event_id: z.string(), name: z.string(), date: z.string(), start_time: z.string().nullable(), end_time: z.string().nullable(), service_date: z.string().nullable(), venue: z.object({ id: z.string(), name: z.string(), area: z.string().nullable(), address: z.string().nullable(), map_link: z.string().nullable(), website: z.string().nullable(), }), lineup: z.array( z.object({ stage: z.string().nullable(), performer_name: z.string(), start_time: z.string().nullable(), end_time: z.string().nullable(), }), ), genres: z.array(z.string()), price: z.object({ entrance_summary: z.string().nullable(), door: z.string().nullable(), advance: z.string().nullable(), tiers: z.array( z.object({ tier_name: z.string(), price: z.number().nullable(), currency: z.string().nullable(), status: z.string(), url: z.string().nullable(), provider: z.string().nullable(), }), ), }), flyer_url: z.string().nullable(), guest_list_status: z.enum(["available", "full", "closed"]), nlt_url: z.string(), });