inboxassure-mcp-server
Version:
Comprehensive MCP server for InboxAssure email marketing platform with full API coverage including campaigns, replies, and email account management.
208 lines (163 loc) • 8.82 kB
JavaScript
export class BisonService {
constructor(apiKey) {
this.id = Math.random().toString(36).substring(2, 8);
console.error(`[DEBUG:BISON:${this.id}] Creating BisonService instance`);
if (!apiKey) {
console.error(`[DEBUG:BISON:${this.id}] Error: No API key provided to constructor`);
throw new Error("API key is required for BisonService.");
}
// Log detailed info about the API key
console.error(`[DEBUG:BISON:${this.id}] API key type: ${typeof apiKey}`);
console.error(`[DEBUG:BISON:${this.id}] API key length: ${apiKey.length}`);
// If the API key contains a comma, it might be a duplicated header value
// Take only the first value in that case
if (apiKey.includes(',')) {
console.error(`[DEBUG:BISON:${this.id}] API key contains commas, likely a duplicated header value. Taking first value.`);
apiKey = apiKey.split(',')[0].trim();
console.error(`[DEBUG:BISON:${this.id}] Extracted first API key: ${apiKey}`);
}
// Trim any whitespace from the API key
this.apiKey = apiKey.trim();
console.error(`[DEBUG:BISON:${this.id}] API key format validation: ${this.apiKey.startsWith('iak_') ? 'valid prefix' : 'invalid prefix'}, length: ${this.apiKey.length}`);
// Validate the API key has the expected format
if (!this.apiKey.startsWith('iak_')) {
console.error(`[DEBUG:BISON:${this.id}] Warning: API key does not start with expected prefix 'iak_'`);
}
if (this.apiKey.length < 10) {
console.error(`[DEBUG:BISON:${this.id}] Warning: API key seems too short. Expected length > 20`);
}
this.baseUrl = "https://bison.imnodev.com/api";
console.error(`[DEBUG:BISON:${this.id}] Instance created with API key: ${this.apiKey.substring(0, 8)}... and baseUrl: ${this.baseUrl}`);
}
async makeRequest(endpoint, method = 'GET', body = null) {
const callId = Math.random().toString(36).substring(2, 8);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Starting ${method} request to ${endpoint}`);
try {
const url = `${this.baseUrl}${endpoint}`;
console.error(`[DEBUG:BISON:${this.id}:${callId}] Fetching ${url}`);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Using API key: ${this.apiKey.substring(0, 8)}...`);
const options = {
method,
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
}
};
if (body && (method === 'POST' || method === 'PATCH')) {
options.body = JSON.stringify(body);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Request body: ${JSON.stringify(body)}`);
}
const fetchStart = Date.now();
const response = await fetch(url, options);
const fetchDuration = Date.now() - fetchStart;
console.error(`[DEBUG:BISON:${this.id}:${callId}] Response received in ${fetchDuration}ms with status: ${response.status}`);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Response headers: ${JSON.stringify(Object.fromEntries([...response.headers]))}`);
if (!response.ok) {
const errorBody = await response.text();
console.error(`[DEBUG:BISON:${this.id}:${callId}] Error response body: ${errorBody}`);
// Try to parse errorBody as JSON if possible for more structured error, otherwise use text
let details = errorBody;
try {
details = JSON.stringify(JSON.parse(errorBody));
console.error(`[DEBUG:BISON:${this.id}:${callId}] Parsed error body as JSON`);
} catch (e) {
// not JSON, use as is
console.error(`[DEBUG:BISON:${this.id}:${callId}] Error body is not valid JSON`);
}
const error = new Error(`API error: ${response.status} - ${details}`);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Throwing error: ${error.message}`);
throw error;
}
const parseStart = Date.now();
const data = await response.json();
const parseDuration = Date.now() - parseStart;
console.error(`[DEBUG:BISON:${this.id}:${callId}] Response parsed in ${parseDuration}ms`);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Response data: ${JSON.stringify(data)}`);
return data;
} catch (error) {
console.error(`[DEBUG:BISON:${this.id}:${callId}] Error in ${method} ${endpoint}: ${error.message}`);
console.error(`[DEBUG:BISON:${this.id}:${callId}] Error stack: ${error.stack}`);
throw error;
}
}
async getHello() {
return this.makeRequest('/hello');
}
// ==================== CAMPAIGNS API ====================
async listCampaigns(workspaceId, params = {}) {
const queryString = new URLSearchParams(params).toString();
const endpoint = `/campaigns/${workspaceId}${queryString ? '?' + queryString : ''}`;
return this.makeRequest(endpoint);
}
async createCampaign(workspaceId, campaignData) {
return this.makeRequest(`/campaigns/${workspaceId}`, 'POST', campaignData);
}
async getCampaignDetails(workspaceId, campaignId) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}`);
}
async pauseCampaign(workspaceId, campaignId) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/pause`, 'PATCH');
}
async resumeCampaign(workspaceId, campaignId) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/resume`, 'PATCH');
}
async createCampaignSchedule(workspaceId, campaignId, scheduleData) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/schedule`, 'POST', scheduleData);
}
async getScheduleTemplates(workspaceId) {
return this.makeRequest(`/campaigns/${workspaceId}/schedule/templates`);
}
async createSequenceSteps(workspaceId, campaignId, sequenceData) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/sequence-steps`, 'POST', sequenceData);
}
async attachLeadList(workspaceId, campaignId, leadListData) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/leads/attach-lead-list`, 'POST', leadListData);
}
async getCampaignLeads(workspaceId, campaignId, params = {}) {
const queryString = new URLSearchParams(params).toString();
const endpoint = `/campaigns/${workspaceId}/${campaignId}/leads${queryString ? '?' + queryString : ''}`;
return this.makeRequest(endpoint);
}
async getCampaignStats(workspaceId, campaignId, dateRange) {
return this.makeRequest(`/campaigns/${workspaceId}/${campaignId}/stats`, 'POST', dateRange);
}
// ==================== REPLIES API ====================
async listReplies(workspaceId, params = {}) {
const queryString = new URLSearchParams(params).toString();
const endpoint = `/replies/${workspaceId}${queryString ? '?' + queryString : ''}`;
return this.makeRequest(endpoint);
}
async getReplyDetails(workspaceId, replyId) {
return this.makeRequest(`/replies/${workspaceId}/${replyId}`);
}
async composeNewEmail(workspaceId, emailData) {
return this.makeRequest(`/replies/${workspaceId}/new`, 'POST', emailData);
}
async replyToEmail(workspaceId, replyId, replyData) {
return this.makeRequest(`/replies/${workspaceId}/${replyId}/reply`, 'POST', replyData);
}
async getConversationThread(workspaceId, replyId) {
return this.makeRequest(`/replies/${workspaceId}/${replyId}/conversation-thread`);
}
// ==================== EMAIL ACCOUNTS API ====================
async listEmailAccounts(workspaceId, params = {}) {
const queryString = new URLSearchParams(params).toString();
const endpoint = `/email-accounts/${workspaceId}/sender-emails${queryString ? '?' + queryString : ''}`;
return this.makeRequest(endpoint);
}
async getEmailAccountDetails(workspaceId, emailId) {
return this.makeRequest(`/email-accounts/${workspaceId}/sender-emails/${emailId}`);
}
async updateEmailAccount(workspaceId, emailId, updateData) {
return this.makeRequest(`/email-accounts/${workspaceId}/sender-emails/${emailId}`, 'PATCH', updateData);
}
async getAccountCampaigns(workspaceId, emailId) {
return this.makeRequest(`/email-accounts/${workspaceId}/sender-emails/${emailId}/campaigns`);
}
async getAccountReplies(workspaceId, emailId) {
return this.makeRequest(`/email-accounts/${workspaceId}/sender-emails/${emailId}/replies`);
}
async bulkUpdateEmailSignatures(workspaceId, bulkData) {
return this.makeRequest(`/email-accounts/${workspaceId}/sender-emails/signatures/bulk`, 'PATCH', bulkData);
}
}