Skip to main content
Glama
bugbugClient.ts8.16 kB
import { captureException, addBreadcrumb } from '../utils/sentry.js'; import type { BugBugTest, BugBugSuite, BugBugProfile, BugBugTestRun, BugBugSuiteRun, BugBugRunStatusResponse, BugBugScreenshotResponse, PaginatedResponse, CreateTestRunRequest, CreateSuiteRunRequest } from '../types/bugbug.types.js'; interface BugBugConfig { apiToken: string; baseUrl?: string; } interface ApiResponse<T = unknown> { data: T; status: number; statusText: string; } export class BugBugApiClient { private apiToken: string; private baseUrl: string; constructor(config: BugBugConfig) { this.apiToken = config.apiToken; this.baseUrl = config.baseUrl || 'https://app.bugbug.io/api/v1'; } async verifyConnection(): Promise<void> { try { addBreadcrumb('Verifying BugBug API connection', 'api'); const response = await this.getIpAddresses(); if (response.status !== 200) { const error = new Error(`API verification failed: ${response.status} ${response.statusText}`); captureException(error, { component: 'bugbug-client', operation: 'verifyConnection', status: response.status }); throw error; } addBreadcrumb('BugBug API connection verified successfully', 'api'); } catch (error) { const apiError = new Error(`Failed to verify BugBug API connection: ${error instanceof Error ? error.message : 'Unknown error'}`); captureException(apiError, { component: 'bugbug-client', operation: 'verifyConnection', originalError: error instanceof Error ? error.message : String(error) }); throw apiError; } } private async makeRequest<T = unknown>( endpoint: string, method: 'GET' | 'POST' | 'PUT' | 'PATCH' = 'GET', body?: unknown ): Promise<ApiResponse<T>> { const url = `${this.baseUrl}${endpoint}`; addBreadcrumb(`Making ${method} request to ${endpoint}`, 'api', { endpoint, method, hasBody: !!body }); const headers: Record<string, string> = { 'Authorization': `Token ${this.apiToken}`, 'Content-Type': 'application/json', }; const config: { method: string; headers: Record<string, string>; body?: string; } = { method, headers, }; if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) { config.body = JSON.stringify(body); } try { const response = await fetch(url, config); let data: T; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text() as T; } // Log non-2xx responses as potential issues if (response.status >= 400) { const error = new Error(`API request failed: ${response.status} ${response.statusText}`); captureException(error, { component: 'bugbug-client', operation: 'makeRequest', endpoint, method, status: response.status, statusText: response.statusText, responseData: typeof data === 'string' ? data : JSON.stringify(data) }); } return { data, status: response.status, statusText: response.statusText, }; } catch (error) { const apiError = new Error(`API request failed: ${error instanceof Error ? error.message : 'Unknown error'}`); captureException(apiError, { component: 'bugbug-client', operation: 'makeRequest', endpoint, method, originalError: error instanceof Error ? error.message : String(error) }); throw apiError; } } // Config endpoints async getIpAddresses(): Promise<ApiResponse<string[]>> { return this.makeRequest<string[]>('/config/ips/'); } // Profile endpoints async getProfiles(page?: number, pageSize?: number): Promise<ApiResponse<PaginatedResponse<BugBugProfile>>> { const params = new URLSearchParams(); if (page) params.append('page', page.toString()); if (pageSize) params.append('page_size', pageSize.toString()); const query = params.toString() ? `?${params.toString()}` : ''; return this.makeRequest(`/profiles/${query}`); } async getProfile(id: string): Promise<ApiResponse<BugBugProfile>> { return this.makeRequest(`/profiles/${id}/`); } // Suite endpoints async getSuites(page?: number, pageSize?: number, query?: string, ordering?: string): Promise<ApiResponse<PaginatedResponse<BugBugSuite>>> { const params = new URLSearchParams(); if (page) params.append('page', page.toString()); if (pageSize) params.append('page_size', pageSize.toString()); if (query) params.append('query', query); if (ordering) params.append('ordering', ordering); const queryString = params.toString() ? `?${params.toString()}` : ''; return this.makeRequest(`/suites/${queryString}`); } async getSuite(id: string): Promise<ApiResponse<BugBugSuite>> { return this.makeRequest(`/suites/${id}/`); } // Suite run endpoints async createSuiteRun(data: CreateSuiteRunRequest): Promise<ApiResponse<BugBugSuiteRun>> { return this.makeRequest('/suiteruns/', 'POST', data); } async getSuiteRun(id: string): Promise<ApiResponse<BugBugSuiteRun>> { return this.makeRequest(`/suiteruns/${id}/`); } async getSuiteRunStatus(id: string): Promise<ApiResponse<BugBugRunStatusResponse>> { return this.makeRequest(`/suiteruns/${id}/status/`); } async getSuiteRunScreenshots(id: string): Promise<ApiResponse<BugBugScreenshotResponse>> { return this.makeRequest(`/suiteruns/${id}/screenshots/`); } async stopSuiteRun(id: string): Promise<ApiResponse<BugBugSuiteRun>> { return this.makeRequest(`/suiteruns/${id}/stop/`, 'POST'); } // Test endpoints async getTests(page?: number, pageSize?: number, query?: string, ordering?: string): Promise<ApiResponse<PaginatedResponse<BugBugTest>>> { const params = new URLSearchParams(); if (page) params.append('page', page.toString()); if (pageSize) params.append('page_size', pageSize.toString()); if (query) params.append('query', query); if (ordering) params.append('ordering', ordering); const queryString = params.toString() ? `?${params.toString()}` : ''; return this.makeRequest(`/tests/${queryString}`); } async getTest(id: string): Promise<ApiResponse<BugBugTest>> { return this.makeRequest(`/tests/${id}/`); } // Test run endpoints async getTestRuns( page?: number, pageSize?: number, ordering?: string, startedAfter?: string, startedBefore?: string ): Promise<ApiResponse<PaginatedResponse<BugBugTestRun>>> { const params = new URLSearchParams(); if (page) params.append('page', page.toString()); if (pageSize) params.append('page_size', pageSize.toString()); if (ordering) params.append('ordering', ordering); if (startedAfter) params.append('started_after', startedAfter); if (startedBefore) params.append('started_before', startedBefore); const queryString = params.toString() ? `?${params.toString()}` : ''; return this.makeRequest(`/testruns/${queryString}`); } async createTestRun(data: CreateTestRunRequest): Promise<ApiResponse<BugBugTestRun>> { return this.makeRequest('/testruns/', 'POST', data); } async getTestRun(id: string): Promise<ApiResponse<BugBugTestRun>> { return this.makeRequest(`/testruns/${id}/`); } async getTestRunStatus(id: string): Promise<ApiResponse<BugBugRunStatusResponse>> { return this.makeRequest(`/testruns/${id}/status/`); } async getTestRunScreenshots(id: string): Promise<ApiResponse<BugBugScreenshotResponse>> { return this.makeRequest(`/testruns/${id}/screenshots/`); } async stopTestRun(id: string): Promise<ApiResponse<BugBugTestRun>> { return this.makeRequest(`/testruns/${id}/stop/`, 'POST'); } } export const bugbugClient = new BugBugApiClient({ apiToken: process.env.API_KEY! });

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/simplypixi/bugbug-mcp-server'

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