list_events
Find events happening this week in your specified location using Eventfinda data.
Instructions
List all events within this week for a given location.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| location | Yes | The location to search for events in. |
Implementation Reference
- src/index.ts:93-199 (handler)The execute function implementing the list_events tool logic: searches for location ID, fetches events in the next week, retrieves detailed event info, extracts and enhances descriptions from event pages if available, processes ticket information, and returns a formatted JSON array of events.execute: async (args, { log }) => { const today = new Date(); const nextWeek = new Date( today.getFullYear(), today.getMonth(), today.getDate() + 7 ); // Step 1: Find location ID from location name const locations = await fetchFromEventfinda('locations', { q: args.location, }); if (!locations.locations.length) { throw new UserError(`Could not find location: ${args.location}`); } const locationId = locations.locations[0].id; // Step 2: Get events by location and date range const eventsResponse = await fetchFromEventfinda('events', { location: locationId, start_date: formatDateTime(today), end_date: formatDateTime(nextWeek), rows: '10', // Limit to 10 events to avoid rate limiting }); log.debug(`Found ${eventsResponse.events.length} events`); // Step 3: Get detailed information for each event const eventDetails = await Promise.all( eventsResponse.events.map(async (event: any) => { try { // Get detailed event information using event ID return await fetchFromEventfinda(`events/${event.id}.json`, {}); } catch (error) { log.error(`Error fetching details for event ${event.id}:`, error); // Return basic event info if detailed fetch fails return event; } }) ); // Step 4: Process each event to extract required information const processedEvents = await Promise.all( eventDetails.map(async (eventDetail: any) => { // Extract event from the response structure const event = eventDetail.event || eventDetail; // Extract and process description from API let description = 'No description available'; if (event.description) { if (typeof event.description === 'string') { description = event.description; } else if (event.description.html) { description = event.description.html.replace(/<[^>]*>?/gm, ''); } } // If we have a URL, try to fetch completed description from HTML if (event.url) { log.debug( `Fetching description from event page for event ${event.id}` ); const htmlDescription = await fetchEventDescription(event.url, log); if (htmlDescription && htmlDescription.length > 0) { description = htmlDescription; log.debug( `Successfully extracted description from HTML for event ${event.id}` ); } } // Check if event is free const isFree = Boolean(event.is_free); let ticketInfo = 'No ticket information available'; // Process ticket information if ( event.ticket_types && Array.isArray(event.ticket_types.ticket_types) && event.ticket_types.ticket_types.length > 0 ) { ticketInfo = event.ticket_types.ticket_types.map((ticket: any) => ({ name: ticket.name || 'Unnamed ticket', price: ticket.price, description: ticket.description, onsale_at: ticket.onsale_at, })); } return { id: event.id, title: event.name, description: description, url: event.url, datetime_start: event.datetime_start || event.datetime_summary, datetime_end: event.datetime_end || event.datetime_summary, address: event.address, is_free: isFree, ticket_info: isFree ? 'Free event' : ticketInfo, categories: event.category.name, }; }) ); return JSON.stringify(processedEvents, null, 2); },
- src/index.ts:90-92 (schema)Zod schema defining the input parameter 'location' for the list_events tool.parameters: z.object({ location: z.string().describe('The location to search for events in.'), }),
- src/index.ts:87-89 (registration)Registration of the list_events tool with the FastMCP server, specifying name and description.server.addTool({ name: 'list_events', description: 'List all events within this week for a given location.',
- src/index.ts:25-45 (helper)Helper function to fetch data from Eventfinda API with authentication and error handling, used by the list_events handler.async function fetchFromEventfinda( endpoint: string, params: Record<string, string> ) { const url = new URL(`https://api.eventfinda.co.nz/v2/${endpoint}.json`); Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); const response = await fetch(url.toString(), { headers: { Authorization: auth, }, }); if (!response.ok) { throw new UserError( `Failed to fetch from Eventfinda API: ${response.statusText}` ); } return await response.json(); }
- src/index.ts:48-81 (helper)Helper function to scrape event description from the event webpage HTML, used to enhance event details in list_events.async function fetchEventDescription( eventUrl: string, log: any ): Promise<string | null> { try { const response = await fetch(eventUrl); if (!response.ok) { log.error(`Failed to fetch event page: ${response.statusText}`); return null; } const html = await response.text(); // Simple HTML parsing to find element with id 'eventDescription' const descriptionMatch = html.match( /<div[^>]*id=['"]eventDescription['"][^>]*>([\s\S]*?)<\/div>/i ); if (descriptionMatch && descriptionMatch[1]) { // Clean up HTML tags and decode HTML entities return descriptionMatch[1] .replace(/<[^>]*>?/gm, '') // Remove HTML tags .replace(/ /g, ' ') // Replace non-breaking spaces .replace(/&/g, '&') // Replace ampersands .replace(/</g, '<') // Replace less than .replace(/>/g, '>') // Replace greater than .replace(/"/g, '"') // Replace quotes .trim(); } return null; } catch (error) { log.error(`Error fetching event description from URL: ${error}`); return null; } }