Skip to main content
Glama
flying3615

Eventfinda MCP

by flying3615

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
NameRequiredDescriptionDefault
locationYesThe location to search for events in.

Implementation Reference

  • 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);
    },
  • 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.',
  • 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();
    }
  • 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;
      }
    }
Behavior2/5

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

No annotations are provided, so the description carries full burden. It states 'List all events,' implying a read-only operation, but doesn't disclose behavioral traits like pagination, rate limits, authentication needs, error handling, or what 'all events' entails (e.g., completeness guarantees). It adds minimal context beyond the basic action.

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 a single, efficient sentence that front-loads the purpose ('List all events') and specifies constraints ('within this week for a given location'). There is zero waste, and every word earns its place, making it highly concise and well-structured.

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 no annotations, no output schema, and a simple single-parameter tool, the description is adequate but has gaps. It covers the basic what and scope but lacks details on behavior, output format, or error cases. For a read operation with low complexity, it's minimally viable but not fully complete.

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 100%, with the parameter 'location' fully documented in the schema. The description adds no additional meaning about the parameter beyond implying it's used to filter events. Since the schema does the heavy lifting, the baseline score of 3 is appropriate.

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 action ('List all events') and resource ('events'), specifying scope ('within this week for a given location'). It's specific about time frame and location filtering. However, with no sibling tools mentioned, it cannot demonstrate differentiation from alternatives, preventing a perfect score.

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

Usage Guidelines3/5

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

The description implies usage context by stating 'within this week for a given location,' suggesting it's for current-week events at specific locations. However, it lacks explicit guidance on when not to use this tool or alternatives, and no prerequisites are mentioned. With no sibling tools, it cannot provide comparative guidance.

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/flying3615/eventfinda-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server