mcp-tenki
by acxelerator
- src
import axios, { AxiosError } from 'axios';
import {
SearchType,
TicketmasterApiError,
TicketmasterAttraction,
TicketmasterError,
TicketmasterEvent,
TicketmasterResponse,
TicketmasterVenue
} from './types.js';
/**
* Client for interacting with the Ticketmaster Discovery API
*/
export class TicketmasterClient {
private readonly apiKey: string;
private readonly baseUrl = 'https://app.ticketmaster.com/discovery/v2';
constructor(apiKey: string) {
if (!apiKey) {
throw new Error('API key is required');
}
this.apiKey = apiKey;
}
/**
* Formats a date range for the Ticketmaster API
* @param startDate Start of the date range
* @param endDate End of the date range
* @returns Formatted date range string
*/
formatDateRange(startDate: Date, endDate: Date): string {
// Set start time to beginning of day in UTC
const start = new Date(startDate);
start.setUTCHours(0, 0, 0, 0);
// Set end time to end of day in UTC
const end = new Date(endDate);
end.setUTCHours(23, 59, 59, 999);
return `${start.toISOString().split('.')[0]}Z,${end.toISOString().split('.')[0]}Z`;
}
/**
* Search for events, venues, or attractions
* @param type Type of search (event, venue, attraction)
* @param query Search query parameters
* @returns Array of matching items
*/
async search<T extends TicketmasterEvent | TicketmasterVenue | TicketmasterAttraction>(
type: SearchType,
query: {
keyword?: string;
startDateTime?: Date;
endDateTime?: Date;
city?: string;
stateCode?: string;
countryCode?: string;
venueId?: string;
attractionId?: string;
classificationName?: string;
size?: number;
} = {}
): Promise<T[]> {
try {
const endpoint = `${this.baseUrl}/${type}s`;
const params: Record<string, string | number> = {
apikey: this.apiKey,
size: query.size || 20
};
if (query.keyword) {
params.keyword = query.keyword;
}
if (query.startDateTime && query.endDateTime) {
const dateRange = this.formatDateRange(query.startDateTime, query.endDateTime);
params.startDateTime = dateRange.split(',')[0];
params.endDateTime = dateRange.split(',')[1];
}
if (query.city) {
params.city = query.city;
}
if (query.stateCode) {
params.stateCode = query.stateCode;
}
if (query.countryCode) {
params.countryCode = query.countryCode;
}
if (query.venueId) {
params.venueId = query.venueId;
}
if (query.attractionId) {
params.attractionId = query.attractionId;
}
if (query.classificationName) {
params.classificationName = query.classificationName;
}
const response = await axios.get<TicketmasterResponse>(endpoint, { params });
const items = response.data._embedded?.[`${type}s` as keyof typeof response.data._embedded] as T[] || [];
return items;
} catch (error) {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError<TicketmasterError>;
const apiError = axiosError.response?.data?.fault;
throw new TicketmasterApiError(
apiError?.faultstring || 'Failed to fetch results',
apiError?.detail?.errorcode,
axiosError.response?.status
);
}
throw error;
}
}
/**
* Search for events
*/
async searchEvents(query: Parameters<typeof this.search>[1] = {}): Promise<TicketmasterEvent[]> {
return this.search<TicketmasterEvent>('event', query);
}
/**
* Search for venues
*/
async searchVenues(query: Parameters<typeof this.search>[1] = {}): Promise<TicketmasterVenue[]> {
return this.search<TicketmasterVenue>('venue', query);
}
/**
* Search for attractions
*/
async searchAttractions(query: Parameters<typeof this.search>[1] = {}): Promise<TicketmasterAttraction[]> {
return this.search<TicketmasterAttraction>('attraction', query);
}
}