Skip to content

Eventbrite MCP Server

This document provides comprehensive documentation for the Eventbrite Model Context Protocol (MCP) server integration in Inttrest, covering setup, configuration, and usage.

๐ŸŽฏ Overview

The Eventbrite MCP server provides seamless integration with Eventbrite's event platform, allowing Inttrest to discover, fetch, and synchronize event data from one of the world's largest event platforms.

graph TB
    subgraph "Eventbrite MCP Architecture"
        A[MCP Client] --> B[Eventbrite MCP Server]
        B --> C[Eventbrite API]
        B --> D[Data Processing]
        B --> E[Event Normalization]

        D --> F[Event Filtering]
        D --> G[Geocoding]
        D --> H[Category Mapping]

        E --> I[Standardized Event Data]

        C --> J[Event Search API]
        C --> K[Event Details API]
        C --> L[Organization API]
        C --> M[Venue API]
    end

    subgraph "Inttrest Integration"
        N[AI Chat Interface] --> A
        O[Event Discovery] --> A
        P[Map Component] --> A

        I --> Q[MongoDB Storage]
        I --> R[Real-time Updates]
        I --> S[User Interface]
    end

    style B fill:#e3f2fd
    style C fill:#e8f5e8
    style I fill:#fff3e0

๐Ÿ“ฆ Installation & Setup

Prerequisites

  • Node.js 18+ with npm/pnpm
  • Eventbrite API Key (OAuth token)
  • MCP SDK (@modelcontextprotocol/sdk)

Installation

# Navigate to MCP servers directory
cd mcp_servers/eventbrite-mcp

# Install dependencies
npm install
# or
pnpm install

# Build the server
npm run build

Configuration

Create a .env file in the eventbrite-mcp directory:

# Eventbrite API Configuration
EVENTBRITE_API_KEY=your_eventbrite_oauth_token_here
EVENTBRITE_API_BASE_URL=https://www.eventbriteapi.com/v3

# MCP Server Configuration
MCP_SERVER_PORT=3001
MCP_SERVER_HOST=localhost

# Cache Configuration
CACHE_TTL=300000  # 5 minutes in milliseconds
CACHE_MAX_SIZE=1000

# Rate Limiting
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW=60000  # 1 minute

# Logging
LOG_LEVEL=info
LOG_FILE=eventbrite-mcp.log

Package Dependencies

{
  "name": "eventbrite-mcp-server",
  "version": "1.0.0",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.17.4",
    "axios": "^1.6.0",
    "node-cache": "^5.1.2",
    "winston": "^3.11.0",
    "zod": "^4.1.5",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0",
    "tsx": "^4.0.0"
  }
}

๐Ÿ—๏ธ Server Implementation

Core Server Structure

// src/eventbrite-mcp-server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js'
import { EventbriteAPI } from './api/eventbrite-api.js'
import { EventProcessor } from './processors/event-processor.js'
import { Logger } from './utils/logger.js'
import { z } from 'zod'

const logger = new Logger('EventbriteMCP')

class EventbriteMCPServer {
  private server: Server
  private eventbriteAPI: EventbriteAPI
  private eventProcessor: EventProcessor

  constructor() {
    this.server = new Server(
      {
        name: 'eventbrite-mcp-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    )

    this.eventbriteAPI = new EventbriteAPI({
      apiKey: process.env.EVENTBRITE_API_KEY!,
      baseUrl: process.env.EVENTBRITE_API_BASE_URL!,
    })

    this.eventProcessor = new EventProcessor()
    this.setupToolHandlers()
  }

  private setupToolHandlers() {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'search_events',
            description: 'Search for events on Eventbrite',
            inputSchema: {
              type: 'object',
              properties: {
                q: {
                  type: 'string',
                  description: 'Search query for events'
                },
                location: {
                  type: 'string',
                  description: 'Location to search for events (city, address, etc.)'
                },
                categories: {
                  type: 'array',
                  items: { type: 'string' },
                  description: 'Event categories to filter by'
                },
                start_date: {
                  type: 'string',
                  description: 'Start date filter (ISO format)'
                },
                end_date: {
                  type: 'string',
                  description: 'End date filter (ISO format)'
                },
                price: {
                  type: 'string',
                  enum: ['free', 'paid'],
                  description: 'Price filter'
                },
                sort_by: {
                  type: 'string',
                  enum: ['date', 'relevance', 'distance'],
                  description: 'Sort order for results'
                },
                limit: {
                  type: 'number',
                  minimum: 1,
                  maximum: 100,
                  description: 'Maximum number of events to return'
                }
              },
              required: []
            }
          },
          {
            name: 'get_event_details',
            description: 'Get detailed information about a specific event',
            inputSchema: {
              type: 'object',
              properties: {
                event_id: {
                  type: 'string',
                  description: 'Eventbrite event ID'
                }
              },
              required: ['event_id']
            }
          },
          {
            name: 'get_event_categories',
            description: 'Get list of available event categories',
            inputSchema: {
              type: 'object',
              properties: {},
              required: []
            }
          },
          {
            name: 'search_venues',
            description: 'Search for venues on Eventbrite',
            inputSchema: {
              type: 'object',
              properties: {
                location: {
                  type: 'string',
                  description: 'Location to search for venues'
                },
                q: {
                  type: 'string',
                  description: 'Search query for venues'
                }
              },
              required: []
            }
          }
        ]
      }
    })

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      try {
        const { name, arguments: args } = request.params

        switch (name) {
          case 'search_events':
            return await this.handleSearchEvents(args)

          case 'get_event_details':
            return await this.handleGetEventDetails(args)

          case 'get_event_categories':
            return await this.handleGetEventCategories(args)

          case 'search_venues':
            return await this.handleSearchVenues(args)

          default:
            throw new Error(`Unknown tool: ${name}`)
        }
      } catch (error) {
        logger.error('Tool execution failed:', error)
        throw error
      }
    })
  }

  private async handleSearchEvents(args: any) {
    const searchParams = EventSearchSchema.parse(args)

    logger.info('Searching events with params:', searchParams)

    const events = await this.eventbriteAPI.searchEvents(searchParams)
    const processedEvents = await this.eventProcessor.processEvents(events)

    return {
      content: [
        {
          type: 'text',
          text: `Found ${processedEvents.length} events from Eventbrite`
        }
      ],
      _meta: {
        source: 'eventbrite',
        count: processedEvents.length,
        events: processedEvents
      }
    }
  }

  private async handleGetEventDetails(args: any) {
    const { event_id } = EventDetailsSchema.parse(args)

    logger.info('Fetching event details for:', event_id)

    const eventDetails = await this.eventbriteAPI.getEventDetails(event_id)
    const processedEvent = await this.eventProcessor.processEvent(eventDetails)

    return {
      content: [
        {
          type: 'text',
          text: `Event details retrieved for: ${processedEvent.title}`
        }
      ],
      _meta: {
        source: 'eventbrite',
        event: processedEvent
      }
    }
  }

  private async handleGetEventCategories(args: any) {
    logger.info('Fetching event categories')

    const categories = await this.eventbriteAPI.getCategories()

    return {
      content: [
        {
          type: 'text',
          text: `Retrieved ${categories.length} event categories`
        }
      ],
      _meta: {
        source: 'eventbrite',
        categories: categories
      }
    }
  }

  private async handleSearchVenues(args: any) {
    const searchParams = VenueSearchSchema.parse(args)

    logger.info('Searching venues with params:', searchParams)

    const venues = await this.eventbriteAPI.searchVenues(searchParams)

    return {
      content: [
        {
          type: 'text',
          text: `Found ${venues.length} venues`
        }
      ],
      _meta: {
        source: 'eventbrite',
        venues: venues
      }
    }
  }

  async start() {
    const transport = new StdioServerTransport()
    await this.server.connect(transport)
    logger.info('Eventbrite MCP Server started successfully')
  }
}

// Schema definitions
const EventSearchSchema = z.object({
  q: z.string().optional(),
  location: z.string().optional(),
  categories: z.array(z.string()).optional(),
  start_date: z.string().optional(),
  end_date: z.string().optional(),
  price: z.enum(['free', 'paid']).optional(),
  sort_by: z.enum(['date', 'relevance', 'distance']).optional(),
  limit: z.number().min(1).max(100).optional()
})

const EventDetailsSchema = z.object({
  event_id: z.string()
})

const VenueSearchSchema = z.object({
  location: z.string().optional(),
  q: z.string().optional()
})

// Start the server
if (import.meta.url === `file://${process.argv[1]}`) {
  const server = new EventbriteMCPServer()
  server.start().catch(console.error)
}

export { EventbriteMCPServer }

Eventbrite API Client

// src/api/eventbrite-api.ts
import axios, { AxiosInstance } from 'axios'
import NodeCache from 'node-cache'
import { Logger } from '../utils/logger.js'

interface EventbriteConfig {
  apiKey: string
  baseUrl: string
  timeout?: number
}

interface EventSearchParams {
  q?: string
  location?: string
  categories?: string[]
  start_date?: string
  end_date?: string
  price?: 'free' | 'paid'
  sort_by?: 'date' | 'relevance' | 'distance'
  limit?: number
}

interface VenueSearchParams {
  location?: string
  q?: string
}

export class EventbriteAPI {
  private client: AxiosInstance
  private cache: NodeCache
  private logger: Logger

  constructor(config: EventbriteConfig) {
    this.logger = new Logger('EventbriteAPI')

    this.client = axios.create({
      baseURL: config.baseUrl,
      timeout: config.timeout || 30000,
      headers: {
        'Authorization': `Bearer ${config.apiKey}`,
        'Content-Type': 'application/json'
      }
    })

    this.cache = new NodeCache({
      stdTTL: parseInt(process.env.CACHE_TTL || '300'), // 5 minutes
      maxKeys: parseInt(process.env.CACHE_MAX_SIZE || '1000')
    })

    this.setupInterceptors()
  }

  private setupInterceptors() {
    // Request interceptor for logging
    this.client.interceptors.request.use(
      (config) => {
        this.logger.debug(`API Request: ${config.method?.toUpperCase()} ${config.url}`)
        return config
      },
      (error) => {
        this.logger.error('API Request Error:', error)
        return Promise.reject(error)
      }
    )

    // Response interceptor for logging and error handling
    this.client.interceptors.response.use(
      (response) => {
        this.logger.debug(`API Response: ${response.status} ${response.config.url}`)
        return response
      },
      (error) => {
        this.logger.error('API Response Error:', {
          status: error.response?.status,
          statusText: error.response?.statusText,
          url: error.config?.url
        })
        return Promise.reject(error)
      }
    )
  }

  async searchEvents(params: EventSearchParams) {
    const cacheKey = `events:${JSON.stringify(params)}`
    const cached = this.cache.get(cacheKey)

    if (cached) {
      this.logger.debug('Returning cached events')
      return cached
    }

    try {
      const queryParams: any = {}

      if (params.q) queryParams.q = params.q
      if (params.location) queryParams['location.address'] = params.location
      if (params.categories?.length) queryParams.categories = params.categories.join(',')
      if (params.start_date) queryParams['start_date.range_start'] = params.start_date
      if (params.end_date) queryParams['start_date.range_end'] = params.end_date
      if (params.price === 'free') queryParams.price = 'free'
      if (params.price === 'paid') queryParams.price = 'paid'
      if (params.sort_by) queryParams.sort_by = params.sort_by

      queryParams.expand = 'venue,category,subcategory,organizer'
      queryParams.page_size = Math.min(params.limit || 50, 100)

      const response = await this.client.get('/events/search/', {
        params: queryParams
      })

      const events = response.data.events || []
      this.cache.set(cacheKey, events)

      this.logger.info(`Found ${events.length} events`)
      return events

    } catch (error) {
      this.logger.error('Event search failed:', error)
      throw new Error(`Failed to search events: ${error.message}`)
    }
  }

  async getEventDetails(eventId: string) {
    const cacheKey = `event:${eventId}`
    const cached = this.cache.get(cacheKey)

    if (cached) {
      this.logger.debug('Returning cached event details')
      return cached
    }

    try {
      const response = await this.client.get(`/events/${eventId}/`, {
        params: {
          expand: 'venue,category,subcategory,organizer,ticket_availability,logo'
        }
      })

      const event = response.data
      this.cache.set(cacheKey, event, 600) // Cache for 10 minutes

      this.logger.info(`Retrieved details for event: ${event.name?.text}`)
      return event

    } catch (error) {
      this.logger.error('Event details fetch failed:', error)
      throw new Error(`Failed to get event details: ${error.message}`)
    }
  }

  async getCategories() {
    const cacheKey = 'categories'
    const cached = this.cache.get(cacheKey)

    if (cached) {
      this.logger.debug('Returning cached categories')
      return cached
    }

    try {
      const response = await this.client.get('/categories/')
      const categories = response.data.categories || []

      // Cache for 1 hour since categories don't change often
      this.cache.set(cacheKey, categories, 3600)

      this.logger.info(`Retrieved ${categories.length} categories`)
      return categories

    } catch (error) {
      this.logger.error('Categories fetch failed:', error)
      throw new Error(`Failed to get categories: ${error.message}`)
    }
  }

  async searchVenues(params: VenueSearchParams) {
    const cacheKey = `venues:${JSON.stringify(params)}`
    const cached = this.cache.get(cacheKey)

    if (cached) {
      this.logger.debug('Returning cached venues')
      return cached
    }

    try {
      const queryParams: any = {}

      if (params.location) queryParams['location.address'] = params.location
      if (params.q) queryParams.q = params.q

      const response = await this.client.get('/venues/search/', {
        params: queryParams
      })

      const venues = response.data.venues || []
      this.cache.set(cacheKey, venues)

      this.logger.info(`Found ${venues.length} venues`)
      return venues

    } catch (error) {
      this.logger.error('Venue search failed:', error)
      throw new Error(`Failed to search venues: ${error.message}`)
    }
  }

  async getVenueDetails(venueId: string) {
    const cacheKey = `venue:${venueId}`
    const cached = this.cache.get(cacheKey)

    if (cached) {
      return cached
    }

    try {
      const response = await this.client.get(`/venues/${venueId}/`)
      const venue = response.data

      this.cache.set(cacheKey, venue, 1800) // Cache for 30 minutes
      return venue

    } catch (error) {
      this.logger.error('Venue details fetch failed:', error)
      throw new Error(`Failed to get venue details: ${error.message}`)
    }
  }
}

Event Data Processing

// src/processors/event-processor.ts
import { Logger } from '../utils/logger.js'

interface StandardEvent {
  id: string
  title: string
  description: string
  url: string
  start_date: string
  end_date?: string
  timezone: string
  is_online: boolean
  location?: {
    name: string
    address: string
    city: string
    region: string
    country: string
    latitude?: number
    longitude?: number
  }
  organizer: {
    name: string
    description?: string
    url?: string
  }
  category: {
    id: string
    name: string
    short_name: string
  }
  subcategory?: {
    id: string
    name: string
  }
  image_url?: string
  ticket_info: {
    is_free: boolean
    currency?: string
    minimum_price?: number
    maximum_price?: number
    sales_status: string
  }
  capacity?: number
  attendance_count?: number
  source: string
  source_id: string
  created_at: string
  updated_at: string
}

export class EventProcessor {
  private logger: Logger

  constructor() {
    this.logger = new Logger('EventProcessor')
  }

  async processEvents(rawEvents: any[]): Promise<StandardEvent[]> {
    this.logger.info(`Processing ${rawEvents.length} events`)

    const processedEvents = await Promise.all(
      rawEvents.map(event => this.processEvent(event))
    )

    return processedEvents.filter(event => event !== null) as StandardEvent[]
  }

  async processEvent(rawEvent: any): Promise<StandardEvent | null> {
    try {
      // Handle different event data structures from Eventbrite
      const event = this.normalizeEventStructure(rawEvent)

      const standardEvent: StandardEvent = {
        id: `eventbrite_${event.id}`,
        title: this.extractText(event.name),
        description: this.extractText(event.description) || '',
        url: event.url || '',
        start_date: event.start?.utc || event.start_date,
        end_date: event.end?.utc || event.end_date,
        timezone: event.start?.timezone || 'UTC',
        is_online: event.is_online_event || false,
        location: this.processLocation(event.venue),
        organizer: this.processOrganizer(event.organizer),
        category: this.processCategory(event.category),
        subcategory: this.processSubcategory(event.subcategory),
        image_url: this.extractImageUrl(event.logo),
        ticket_info: this.processTicketInfo(event),
        capacity: event.capacity_integer,
        attendance_count: event.attendance_count,
        source: 'eventbrite',
        source_id: event.id,
        created_at: event.created,
        updated_at: event.changed
      }

      this.logger.debug(`Processed event: ${standardEvent.title}`)
      return standardEvent

    } catch (error) {
      this.logger.error('Failed to process event:', error)
      return null
    }
  }

  private normalizeEventStructure(event: any): any {
    // Handle different API response formats
    if (event.event) {
      return event.event
    }
    return event
  }

  private extractText(textObject: any): string {
    if (typeof textObject === 'string') {
      return textObject
    }
    if (textObject?.text) {
      return textObject.text
    }
    if (textObject?.html) {
      // Strip HTML tags
      return textObject.html.replace(/<[^>]*>/g, '')
    }
    return ''
  }

  private processLocation(venue: any): StandardEvent['location'] | undefined {
    if (!venue) return undefined

    return {
      name: venue.name || '',
      address: venue.address?.localized_address_display || 
               venue.address?.address_1 || '',
      city: venue.address?.city || '',
      region: venue.address?.region || '',
      country: venue.address?.country || '',
      latitude: venue.latitude ? parseFloat(venue.latitude) : undefined,
      longitude: venue.longitude ? parseFloat(venue.longitude) : undefined
    }
  }

  private processOrganizer(organizer: any): StandardEvent['organizer'] {
    return {
      name: organizer?.name || 'Unknown Organizer',
      description: this.extractText(organizer?.description),
      url: organizer?.url
    }
  }

  private processCategory(category: any): StandardEvent['category'] {
    return {
      id: category?.id || 'other',
      name: category?.name || 'Other',
      short_name: category?.short_name || category?.name || 'Other'
    }
  }

  private processSubcategory(subcategory: any): StandardEvent['subcategory'] | undefined {
    if (!subcategory) return undefined

    return {
      id: subcategory.id,
      name: subcategory.name
    }
  }

  private extractImageUrl(logo: any): string | undefined {
    if (!logo) return undefined

    // Eventbrite logos have different sizes
    if (logo.crop_mask?.width && logo.crop_mask?.height) {
      return logo.url
    }

    if (logo.original?.url) {
      return logo.original.url
    }

    return logo.url
  }

  private processTicketInfo(event: any): StandardEvent['ticket_info'] {
    const ticketInfo: StandardEvent['ticket_info'] = {
      is_free: event.is_free || false,
      sales_status: 'unknown'
    }

    // Process ticket availability
    if (event.ticket_availability) {
      ticketInfo.sales_status = event.ticket_availability.is_sold_out ? 'sold_out' : 'available'

      if (!ticketInfo.is_free && event.ticket_availability.minimum_price) {
        ticketInfo.currency = event.ticket_availability.minimum_price.currency
        ticketInfo.minimum_price = parseFloat(event.ticket_availability.minimum_price.major_value)
      }

      if (event.ticket_availability.maximum_price) {
        ticketInfo.maximum_price = parseFloat(event.ticket_availability.maximum_price.major_value)
      }
    }

    return ticketInfo
  }
}

๐Ÿ”ง Configuration & Usage

Environment Configuration

// src/config/eventbrite-config.ts
export interface EventbriteConfig {
  apiKey: string
  baseUrl: string
  timeout: number
  cache: {
    ttl: number
    maxSize: number
  }
  rateLimit: {
    requests: number
    window: number
  }
  logging: {
    level: string
    file?: string
  }
}

export function loadConfig(): EventbriteConfig {
  const requiredEnvVars = ['EVENTBRITE_API_KEY']

  for (const envVar of requiredEnvVars) {
    if (!process.env[envVar]) {
      throw new Error(`Missing required environment variable: ${envVar}`)
    }
  }

  return {
    apiKey: process.env.EVENTBRITE_API_KEY!,
    baseUrl: process.env.EVENTBRITE_API_BASE_URL || 'https://www.eventbriteapi.com/v3',
    timeout: parseInt(process.env.API_TIMEOUT || '30000'),
    cache: {
      ttl: parseInt(process.env.CACHE_TTL || '300'),
      maxSize: parseInt(process.env.CACHE_MAX_SIZE || '1000')
    },
    rateLimit: {
      requests: parseInt(process.env.RATE_LIMIT_REQUESTS || '100'),
      window: parseInt(process.env.RATE_LIMIT_WINDOW || '60000')
    },
    logging: {
      level: process.env.LOG_LEVEL || 'info',
      file: process.env.LOG_FILE
    }
  }
}

Running the Server

# Start the MCP server
npm start

# Run in development mode with auto-reload
npm run dev

# Run with debug logging
LOG_LEVEL=debug npm start

# Run with custom port
MCP_SERVER_PORT=3001 npm start

Integration with Inttrest

// Example usage in the main application
import { MCPClient } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'

async function setupEventbriteMCP() {
  const transport = new StdioClientTransport({
    command: 'node',
    args: ['./mcp_servers/eventbrite-mcp/dist/eventbrite-mcp-server.js']
  })

  const client = new MCPClient(
    {
      name: 'inttrest-client',
      version: '1.0.0',
    },
    {
      capabilities: {}
    }
  )

  await client.connect(transport)
  return client
}

async function searchEventbriteEvents(query: string, location?: string) {
  const client = await setupEventbriteMCP()

  const result = await client.callTool({
    name: 'search_events',
    arguments: {
      q: query,
      location: location,
      limit: 50
    }
  })

  return result._meta?.events || []
}

๐Ÿ“Š API Endpoints & Tools

Available MCP Tools

Tool Name Description Parameters
search_events Search for events q, location, categories, dates, price, etc.
get_event_details Get detailed event info event_id
get_event_categories List available categories None
search_venues Search for venues location, q

Example Tool Calls

// Search for tech events in San Francisco
const techEvents = await client.callTool({
  name: 'search_events',
  arguments: {
    q: 'technology',
    location: 'San Francisco, CA',
    categories: ['103'], // Technology category ID
    start_date: '2024-02-01T00:00:00Z',
    end_date: '2024-02-29T23:59:59Z',
    limit: 20
  }
})

// Get specific event details
const eventDetails = await client.callTool({
  name: 'get_event_details',
  arguments: {
    event_id: '123456789'
  }
})

// Get all categories
const categories = await client.callTool({
  name: 'get_event_categories',
  arguments: {}
})

๐Ÿงช Testing

Unit Tests

// tests/eventbrite-api.test.ts
import { EventbriteAPI } from '../src/api/eventbrite-api.js'
import { describe, it, expect, beforeEach } from 'vitest'

describe('EventbriteAPI', () => {
  let api: EventbriteAPI

  beforeEach(() => {
    api = new EventbriteAPI({
      apiKey: 'test-key',
      baseUrl: 'https://api.test.com'
    })
  })

  it('should search events successfully', async () => {
    const events = await api.searchEvents({
      q: 'test',
      location: 'San Francisco'
    })

    expect(Array.isArray(events)).toBe(true)
  })

  it('should handle API errors gracefully', async () => {
    await expect(
      api.getEventDetails('invalid-id')
    ).rejects.toThrow()
  })
})

Integration Tests

// tests/mcp-integration.test.ts
import { EventbriteMCPServer } from '../src/eventbrite-mcp-server.js'

describe('Eventbrite MCP Integration', () => {
  let server: EventbriteMCPServer

  beforeEach(() => {
    server = new EventbriteMCPServer()
  })

  it('should handle search_events tool call', async () => {
    const result = await server.handleSearchEvents({
      q: 'test event',
      location: 'New York'
    })

    expect(result.content).toBeDefined()
    expect(result._meta?.events).toBeDefined()
  })
})

๐Ÿ“ˆ Performance & Monitoring

Metrics Collection

// src/utils/metrics.ts
export class EventbriteMetrics {
  private static instance: EventbriteMetrics
  private metrics: Map<string, number> = new Map()

  static getInstance(): EventbriteMetrics {
    if (!EventbriteMetrics.instance) {
      EventbriteMetrics.instance = new EventbriteMetrics()
    }
    return EventbriteMetrics.instance
  }

  incrementCounter(metric: string): void {
    const current = this.metrics.get(metric) || 0
    this.metrics.set(metric, current + 1)
  }

  getMetrics(): Record<string, number> {
    return Object.fromEntries(this.metrics)
  }

  reset(): void {
    this.metrics.clear()
  }
}

// Usage in API calls
const metrics = EventbriteMetrics.getInstance()
metrics.incrementCounter('api.search_events.calls')

Health Check

// src/health/health-check.ts
export async function checkEventbriteHealth(): Promise<{
  status: 'healthy' | 'unhealthy'
  details: Record<string, any>
}> {
  try {
    const api = new EventbriteAPI({
      apiKey: process.env.EVENTBRITE_API_KEY!,
      baseUrl: process.env.EVENTBRITE_API_BASE_URL!
    })

    // Simple test call
    await api.getCategories()

    return {
      status: 'healthy',
      details: {
        api_connection: 'ok',
        timestamp: new Date().toISOString()
      }
    }
  } catch (error) {
    return {
      status: 'unhealthy',
      details: {
        api_connection: 'failed',
        error: error.message,
        timestamp: new Date().toISOString()
      }
    }
  }
}

This comprehensive documentation covers the Eventbrite MCP server implementation, providing everything needed to integrate Eventbrite event data into the Inttrest platform! ๐ŸŽซ