Instantly MCP Server
by bcharleson
Verified
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";
// Response interfaces
interface InstantlyResponse {
items?: any[];
next_starting_after?: string;
}
interface LeadResponse {
id: string;
email: string;
first_name?: string;
last_name?: string;
company_name?: string;
personalization?: string;
website?: string;
phone?: string;
campaign?: string;
list_id?: string;
organization?: string;
status?: number;
email_open_count?: number;
email_reply_count?: number;
email_click_count?: number;
company_domain?: string;
payload?: any;
timestamp_created?: string;
timestamp_updated?: string;
}
interface CampaignResponse {
id: string;
name: string;
status: number;
description?: string;
organization_id?: string;
step_template_id?: string;
timestamp_created?: string;
timestamp_updated?: string;
}
interface WarmupAnalyticsResponse {
email_date_data: {
[email: string]: {
[date: string]: {
sent?: number;
landed_inbox?: number;
landed_spam?: number;
received?: number;
}
}
};
aggregate_data: {
[email: string]: {
sent?: number;
landed_inbox?: number;
landed_spam?: number;
received?: number;
health_score?: number;
health_score_label?: string;
}
};
}
interface AccountVitalsResponse {
status?: string;
success_list?: Array<{
email: string;
status?: string;
provider_code?: number;
provider_name?: string;
last_used?: string;
warmup_status?: number;
error?: string;
details?: string;
}>;
failure_list?: Array<{
email: string;
error?: string;
details?: string;
status?: string;
provider_code?: number;
provider_name?: string;
}>;
}
interface CampaignAnalyticsResponse extends Array<{
campaign_name?: string;
campaign_id: string;
leads_count?: number;
contacted_count?: number;
open_count?: number;
reply_count?: number;
bounced_count?: number;
unsubscribed_count?: number;
completed_count?: number;
emails_sent_count?: number;
new_leads_contacted_count?: number;
total_opportunities?: number;
total_opportunity_value?: number;
}> {}
interface AccountResponse {
email: string;
timestamp_created: string;
timestamp_updated: string;
first_name: string;
last_name: string;
organization: string;
warmup_status: number;
provider_code: number;
setup_pending: boolean;
is_managed_account: boolean;
tracking_domain_name?: string;
daily_limit?: number;
}
function getApiKey(): string {
return process.env.INSTANTLY_API_KEY as string;
}
const INSTANTLY_API_KEY = getApiKey();
const BASE_URL = "https://api.instantly.ai/api/v2";
// Tool definitions
const CREATE_LEAD_TOOL: Tool = {
name: "instantly_create_lead",
description: "Create a new lead in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the lead"
},
first_name: {
type: "string",
description: "First name of the lead"
},
last_name: {
type: "string",
description: "Last name of the lead"
},
company_name: {
type: "string",
description: "Company name of the lead"
},
campaign: {
type: "string",
description: "Campaign ID associated with the lead (UUID)"
},
list_id: {
type: "string",
description: "List ID associated with the lead (UUID)"
},
personalization: {
type: "string",
description: "Personalization of the lead"
},
website: {
type: "string",
description: "Website of the lead"
},
phone: {
type: "string",
description: "Phone number of the lead"
},
custom_variables: {
type: "object",
description: "Custom variables for the lead"
}
},
required: ["email"]
}
};
const GET_LEAD_TOOL: Tool = {
name: "instantly_get_lead",
description: "Get details of a lead by ID",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Lead ID (UUID)"
}
},
required: ["id"]
}
};
const LIST_LEADS_TOOL: Tool = {
name: "instantly_list_leads",
description: "List leads with optional filters",
inputSchema: {
type: "object",
properties: {
campaign: {
type: "string",
description: "Filter leads by campaign ID (UUID)"
},
list_id: {
type: "string",
description: "Filter leads by list ID (UUID)"
},
limit: {
type: "number",
description: "Maximum number of leads to return (default 10, max 100)"
},
starting_after: {
type: "string",
description: "ID of the last lead from the previous page for pagination"
}
}
}
};
const UPDATE_LEAD_TOOL: Tool = {
name: "instantly_update_lead",
description: "Update a lead's information",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Lead ID (UUID)"
},
first_name: {
type: "string",
description: "First name of the lead"
},
last_name: {
type: "string",
description: "Last name of the lead"
},
company_name: {
type: "string",
description: "Company name of the lead"
},
personalization: {
type: "string",
description: "Personalization of the lead"
},
website: {
type: "string",
description: "Website of the lead"
},
phone: {
type: "string",
description: "Phone number of the lead"
},
custom_variables: {
type: "object",
description: "Custom variables for the lead"
}
},
required: ["id"]
}
};
const DELETE_LEAD_TOOL: Tool = {
name: "instantly_delete_lead",
description: "Delete a lead",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Lead ID (UUID)"
}
},
required: ["id"]
}
};
const LIST_CAMPAIGNS_TOOL: Tool = {
name: "instantly_list_campaigns",
description: "List campaigns with pagination support",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Maximum number of campaigns to return (default 5, max 100)"
},
starting_after: {
type: "string",
description: "ID of the last campaign from the previous page for pagination. Use the next_starting_after value from previous response to get the next page."
},
status: {
type: "number",
description: "Filter campaigns by status (0: Draft, 1: Active, 2: Paused, 3: Completed, 4: Running Subsequences)"
}
}
}
};
const GET_CAMPAIGN_TOOL: Tool = {
name: "instantly_get_campaign",
description: "Get details of a campaign",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Campaign ID (UUID)"
}
},
required: ["id"]
}
};
const GET_WARMUP_ANALYTICS_TOOL: Tool = {
name: "instantly_get_warmup_analytics",
description: "Get warmup analytics for specified email accounts",
inputSchema: {
type: "object",
properties: {
emails: {
type: "array",
items: { type: "string" },
description: "List of emails to get warmup analytics for (up to 100). The emails should be attached to accounts in your workspace."
}
},
required: ["emails"]
}
};
const TEST_ACCOUNT_VITALS_TOOL: Tool = {
name: "instantly_test_account_vitals",
description: "Test the health and connectivity of email accounts in your Instantly workspace",
inputSchema: {
type: "object",
properties: {
accounts: {
type: "array",
items: { type: "string" },
description: "List of email accounts to test. You can provide multiple email addresses to test them all at once (up to 10 recommended)."
}
},
required: ["accounts"]
}
};
const GET_CAMPAIGN_ANALYTICS_TOOL: Tool = {
name: "instantly_get_campaign_analytics",
description: "Get analytics for a specific campaign or all campaigns for a date range",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Campaign ID (UUID). If not provided, analytics for all campaigns will be returned."
},
start_date: {
type: "string",
description: "Start date for analytics (YYYY-MM-DD format)"
},
end_date: {
type: "string",
description: "End date for analytics (YYYY-MM-DD format)"
}
},
required: ["start_date", "end_date"]
}
};
// Account tool definitions
const CREATE_ACCOUNT_TOOL: Tool = {
name: "instantly_create_account",
description: "Create a new email account in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account"
},
first_name: {
type: "string",
description: "First name associated with the account"
},
last_name: {
type: "string",
description: "Last name associated with the account"
},
provider_code: {
type: "number",
description: "Provider code (1: Custom IMAP/SMTP, 2: Google, 3: Microsoft, 4: AWS)"
},
imap_username: {
type: "string",
description: "IMAP username"
},
imap_password: {
type: "string",
description: "IMAP password"
},
imap_host: {
type: "string",
description: "IMAP host (e.g. imap.gmail.com)"
},
imap_port: {
type: "number",
description: "IMAP port (e.g. 993)"
},
smtp_username: {
type: "string",
description: "SMTP username"
},
smtp_password: {
type: "string",
description: "SMTP password"
},
smtp_host: {
type: "string",
description: "SMTP host (e.g. smtp.gmail.com)"
},
smtp_port: {
type: "number",
description: "SMTP port (e.g. 587)"
},
daily_limit: {
type: "number",
description: "Daily email sending limit"
},
tracking_domain_name: {
type: "string",
description: "Tracking domain name"
}
},
required: ["email", "first_name", "last_name", "provider_code", "imap_username", "imap_password", "imap_host", "imap_port", "smtp_username", "smtp_password", "smtp_host", "smtp_port"]
}
};
const LIST_ACCOUNTS_TOOL: Tool = {
name: "instantly_list_accounts",
description: "List email accounts in Instantly",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "The number of accounts to return (max 100)"
},
starting_after: {
type: "string",
description: "The ID of the last item in the previous page - used for pagination"
},
search: {
type: "string",
description: "Search term to filter accounts"
},
status: {
type: "number",
description: "Status filter (1: Active, 2: Paused, -1: Connection Error, -2: Soft Bounce Error, -3: Sending Error)"
},
provider_code: {
type: "number",
description: "Provider code filter (1: Custom IMAP/SMTP, 2: Google, 3: Microsoft, 4: AWS)"
},
fetch_all: {
type: "boolean",
description: "Whether to automatically fetch all pages and provide a comprehensive summary. Use this when you need information about all accounts."
}
}
}
};
const GET_ACCOUNT_TOOL: Tool = {
name: "instantly_get_account",
description: "Get details of a specific email account in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account to retrieve"
}
},
required: ["email"]
}
};
const UPDATE_ACCOUNT_TOOL: Tool = {
name: "instantly_update_account",
description: "Update an existing email account in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account to update"
},
first_name: {
type: "string",
description: "First name associated with the account"
},
last_name: {
type: "string",
description: "Last name associated with the account"
},
daily_limit: {
type: "number",
description: "Daily email sending limit"
},
tracking_domain_name: {
type: "string",
description: "Tracking domain name"
},
skip_cname_check: {
type: "boolean",
description: "Whether to skip CNAME check for tracking domain"
},
remove_tracking_domain: {
type: "boolean",
description: "Whether to remove tracking domain from the account"
}
},
required: ["email"]
}
};
const DELETE_ACCOUNT_TOOL: Tool = {
name: "instantly_delete_account",
description: "Delete an email account from Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account to delete"
}
},
required: ["email"]
}
};
const PAUSE_ACCOUNT_TOOL: Tool = {
name: "instantly_pause_account",
description: "Pause an email account in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account to pause"
}
},
required: ["email"]
}
};
const RESUME_ACCOUNT_TOOL: Tool = {
name: "instantly_resume_account",
description: "Resume a paused email account in Instantly",
inputSchema: {
type: "object",
properties: {
email: {
type: "string",
description: "Email address of the account to resume"
}
},
required: ["email"]
}
};
const INSTANTLY_TOOLS = [
CREATE_LEAD_TOOL,
GET_LEAD_TOOL,
LIST_LEADS_TOOL,
UPDATE_LEAD_TOOL,
DELETE_LEAD_TOOL,
LIST_CAMPAIGNS_TOOL,
GET_CAMPAIGN_TOOL,
GET_WARMUP_ANALYTICS_TOOL,
TEST_ACCOUNT_VITALS_TOOL,
GET_CAMPAIGN_ANALYTICS_TOOL,
CREATE_ACCOUNT_TOOL,
LIST_ACCOUNTS_TOOL,
GET_ACCOUNT_TOOL,
UPDATE_ACCOUNT_TOOL,
DELETE_ACCOUNT_TOOL,
PAUSE_ACCOUNT_TOOL,
RESUME_ACCOUNT_TOOL
] as const;
// API handlers
async function handleCreateLead({
email,
first_name,
last_name,
company_name,
campaign,
list_id,
personalization,
website,
phone,
custom_variables
}: {
email: string;
first_name?: string;
last_name?: string;
company_name?: string;
campaign?: string;
list_id?: string;
personalization?: string;
website?: string;
phone?: string;
custom_variables?: any;
}) {
const url = `${BASE_URL}/leads`;
const body: any = { email };
if (first_name) body.first_name = first_name;
if (last_name) body.last_name = last_name;
if (company_name) body.company_name = company_name;
if (campaign) body.campaign = campaign;
if (list_id) body.list_id = list_id;
if (personalization) body.personalization = personalization;
if (website) body.website = website;
if (phone) body.phone = phone;
if (custom_variables) body.custom_variables = custom_variables;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to create lead: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as LeadResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating lead: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleGetLead(id: string) {
const url = `${BASE_URL}/leads/${id}`;
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`
}
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to get lead: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as LeadResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting lead: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleListLeads({
campaign,
list_id,
limit = 10,
starting_after
}: {
campaign?: string;
list_id?: string;
limit?: number;
starting_after?: string;
}) {
const url = `${BASE_URL}/leads/list`;
const body: any = {};
if (campaign) body.campaign = campaign;
if (list_id) body.list_id = list_id;
if (limit) body.limit = limit;
if (starting_after) body.starting_after = starting_after;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to list leads: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as InstantlyResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error listing leads: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleUpdateLead({
id,
first_name,
last_name,
company_name,
personalization,
website,
phone,
custom_variables
}: {
id: string;
first_name?: string;
last_name?: string;
company_name?: string;
personalization?: string;
website?: string;
phone?: string;
custom_variables?: any;
}) {
const url = `${BASE_URL}/leads/${id}`;
const body: any = {};
if (first_name) body.first_name = first_name;
if (last_name) body.last_name = last_name;
if (company_name) body.company_name = company_name;
if (personalization) body.personalization = personalization;
if (website) body.website = website;
if (phone) body.phone = phone;
if (custom_variables) body.custom_variables = custom_variables;
try {
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to update lead: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as LeadResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error updating lead: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleDeleteLead(id: string) {
const url = `${BASE_URL}/leads/${id}`;
try {
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`
}
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to delete lead: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as LeadResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error deleting lead: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleListCampaigns({
limit = 5,
starting_after,
status
}: {
limit?: number;
starting_after?: string;
status?: number;
}) {
const url = new URL(`${BASE_URL}/campaigns`);
if (limit) url.searchParams.append('limit', limit.toString());
if (starting_after) url.searchParams.append('starting_after', starting_after);
if (status !== undefined) url.searchParams.append('status', status.toString());
try {
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`
}
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to list campaigns: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as InstantlyResponse;
// Format the campaigns in a more human-readable format
let formattedText = "## Campaigns\n\n";
if (!data.items || data.items.length === 0) {
formattedText += "No campaigns found.\n";
} else {
// Add a summary count
formattedText += `Found ${data.items.length} campaigns with the specified criteria.\n\n`;
// Create a table-like structure with the most important fields
formattedText += "| ID | Name | Status | Creation Date |\n";
formattedText += "|---|---|---|---|\n";
// Map status codes to readable strings
const statusMap: {[key: number]: string} = {
0: "Draft",
1: "Active",
2: "Paused",
3: "Completed",
4: "Running Subsequences",
"-99": "Account Suspended",
"-1": "Accounts Unhealthy",
"-2": "Bounce Protect"
};
data.items.forEach(campaign => {
const statusText = statusMap[campaign.status] || `Unknown (${campaign.status})`;
const creationDate = campaign.timestamp_created ? new Date(campaign.timestamp_created).toLocaleDateString() : "N/A";
formattedText += `| ${campaign.id} | ${campaign.name} | ${statusText} | ${creationDate} |\n`;
});
}
// Add pagination information
if (data.next_starting_after) {
formattedText += `\n## Pagination\n\nThis is page ${starting_after ? 'n' : '1'} of results. To view the next page, call instantly_list_campaigns with:\n\`\`\`json\n{"starting_after": "${data.next_starting_after}"${status !== undefined ? `, "status": ${status}` : ""}}\n\`\`\`\n`;
} else {
formattedText += "\n## Pagination\n\nThis is the final page of results. No more pages available.\n";
}
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error listing campaigns: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleGetCampaign(id: string) {
const url = `${BASE_URL}/campaigns/${id}`;
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`
}
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{
type: "text",
text: `Failed to get campaign: ${response.status} ${response.statusText} - ${errorText}`
}],
isError: true
};
}
const data = await response.json() as CampaignResponse;
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting campaign: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleGetWarmupAnalytics(emails: string[]) {
const url = `${BASE_URL}/accounts/warmup-analytics`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ emails })
}).catch(err => {
throw new Error(`Network error: ${err.message}`);
});
if (!response.ok) {
const errorText = await response.text().catch(e => 'Could not read error response');
// More specific error message based on status code
let errorMessage = `Failed to get warmup analytics: ${response.status} ${response.statusText}`;
if (response.status === 401) {
errorMessage = "Authentication failed: Please check your API key.";
} else if (response.status === 403) {
errorMessage = "Not authorized: Your API key doesn't have permission to access warmup analytics.";
} else if (response.status === 400) {
errorMessage = `Bad request: ${errorText}`;
} else if (response.status === 404) {
errorMessage = "Endpoint not found: The warmup analytics API may have changed.";
} else if (response.status === 429) {
errorMessage = "Rate limit exceeded: Please try again later.";
}
return {
content: [{
type: "text",
text: `${errorMessage}\n\nTechnical details: ${errorText}`
}],
isError: true
};
}
const data = await response.json() as WarmupAnalyticsResponse;
// Format the response in a readable way
let formattedText = "## Warmup Analytics\n\n";
const emailList = new Set([
...Object.keys(data.email_date_data || {}),
...Object.keys(data.aggregate_data || {})
]);
if (emailList.size === 0) {
formattedText += "No data found for the specified emails. This could be because:\n\n";
formattedText += "- The emails don't exist in your Instantly account\n";
formattedText += "- The emails haven't been used for warmup yet\n";
formattedText += "- The analytics data is not yet available\n\n";
formattedText += "Please check that you've entered valid email addresses and that they are properly configured in your Instantly account.";
} else {
for (const email of emailList) {
formattedText += `### ${email}\n\n`;
// Add aggregate metrics and health score
const aggregateData = data.aggregate_data?.[email];
if (aggregateData) {
formattedText += "#### Aggregate Metrics\n\n";
// Add health score if available
if (aggregateData.health_score !== undefined) {
formattedText += `**Health Score**: ${aggregateData.health_score}${aggregateData.health_score_label ? ` (${aggregateData.health_score_label})` : ''}\n\n`;
}
formattedText += "| Metric | Value |\n|---|---|\n";
if (aggregateData.sent !== undefined) formattedText += `| Total Sent | ${aggregateData.sent} |\n`;
if (aggregateData.landed_inbox !== undefined) formattedText += `| Landed in Inbox | ${aggregateData.landed_inbox} |\n`;
if (aggregateData.landed_spam !== undefined) formattedText += `| Landed in Spam | ${aggregateData.landed_spam} |\n`;
if (aggregateData.received !== undefined) formattedText += `| Total Received | ${aggregateData.received} |\n`;
formattedText += "\n";
}
// Add daily metrics
const dailyData = data.email_date_data?.[email];
if (dailyData && Object.keys(dailyData).length > 0) {
formattedText += "#### Daily Metrics\n\n";
formattedText += "| Date | Sent | Inbox | Spam | Received |\n|---|---|---|---|---|\n";
// Sort dates in descending order (newest first)
const sortedDates = Object.keys(dailyData).sort((a, b) => new Date(b).getTime() - new Date(a).getTime());
for (const date of sortedDates) {
const dayData = dailyData[date];
formattedText += `| ${date} | ${dayData.sent || 0} | ${dayData.landed_inbox || 0} | ${dayData.landed_spam || 0} | ${dayData.received || 0} |\n`;
}
formattedText += "\n";
} else if (!aggregateData) {
formattedText += "No data available for this email.\n\n";
}
}
}
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting warmup analytics: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleTestAccountVitals(accounts: string[]) {
const url = `${BASE_URL}/accounts/test/vitals`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${INSTANTLY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ accounts })
}).catch(err => {
throw new Error(`Network error: ${err.message}`);
});
if (!response.ok) {
const errorText = await response.text().catch(e => 'Could not read error response');
// More specific error message based on status code
let errorMessage = `Failed to test account vitals: ${response.status} ${response.statusText}`;
if (response.status === 401) {
errorMessage = "Authentication failed: Please check your API key.";
} else if (response.status === 403) {
errorMessage = "Not authorized: Your API key doesn't have permission to access account vitals.";
} else if (response.status === 400) {
errorMessage = `Bad request: ${errorText}`;
} else if (response.status === 404) {
errorMessage = "Endpoint not found: The account vitals API may have changed.";
} else if (response.status === 429) {
errorMessage = "Rate limit exceeded: Please try again later.";
}
return {
content: [{
type: "text",
text: `${errorMessage}\n\nTechnical details: ${errorText}`
}],
isError: true
};
}
const data = await response.json() as AccountVitalsResponse;
// Format the response in a readable way
let formattedText = "## Account Vitals Test Results\n\n";
// Add overall status if available
if (data.status) {
formattedText += `**Overall Status**: ${data.status}\n\n`;
}
// Summary section
const successCount = data.success_list?.length || 0;
const failureCount = data.failure_list?.length || 0;
formattedText += `**Summary**: Tested ${accounts.length} account(s). ${successCount} successful, ${failureCount} failed.\n\n`;
// Handle successful accounts
if (data.success_list && data.success_list.length > 0) {
formattedText += "### ✅ Successful Accounts\n\n";
formattedText += "| Email | Status | Provider | Last Used |\n|---|---|---|---|\n";
for (const account of data.success_list) {
const provider = account.provider_name || `Provider #${account.provider_code || 'unknown'}`;
const lastUsed = account.last_used ? new Date(account.last_used).toLocaleString() : 'N/A';
formattedText += `| ${account.email} | ${account.status || 'Working'} | ${provider} | ${lastUsed} |\n`;
}
formattedText += "\n";
}
// Handle failed accounts
if (data.failure_list && data.failure_list.length > 0) {
formattedText += "### ❌ Failed Accounts\n\n";
formattedText += "| Email | Error | Details | Provider |\n|---|---|---|---|\n";
for (const account of data.failure_list) {
const provider = account.provider_name || `Provider #${account.provider_code || 'unknown'}`;
formattedText += `| ${account.email} | ${account.error || 'Unknown error'} | ${account.details || 'No details provided'} | ${provider} |\n`;
}
formattedText += "\n";
}
if ((!data.success_list || data.success_list.length === 0) && (!data.failure_list || data.failure_list.length === 0)) {
formattedText += "No test results found for the specified accounts. Please verify that these email accounts exist in your Instantly workspace.\n";
}
// Add recommendations if there are failures
if (data.failure_list && data.failure_list.length > 0) {
formattedText += "### 📋 Recommendations\n\n";
formattedText += "For failed accounts, consider the following steps:\n\n";
formattedText += "1. Verify the email credentials are correct in Instantly\n";
formattedText += "2. Check that the email provider allows API access\n";
formattedText += "3. If using Gmail or Google Workspace, verify that less secure app access is enabled or use OAuth\n";
formattedText += "4. For Office 365 accounts, check that the appropriate permissions are granted\n";
formattedText += "5. Contact Instantly support for persistent issues\n";
}
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error testing account vitals: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleGetCampaignAnalytics({
id,
start_date,
end_date
}: {
id?: string;
start_date: string;
end_date: string;
}) {
try {
const url = id
? `https://api.instantly.ai/api/v2/campaigns/${id}/analytics?start_date=${start_date}&end_date=${end_date}`
: `https://api.instantly.ai/api/v2/campaigns/analytics?start_date=${start_date}&end_date=${end_date}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
const data = await response.json() as CampaignAnalyticsResponse;
let formattedText = '# Campaign Analytics\n\n';
if (data.length === 0) {
formattedText += 'No analytics data available for the specified period.\n';
} else {
formattedText += `**Period**: ${start_date} to ${end_date}\n\n`;
for (const campaign of data) {
formattedText += `### Campaign: ${campaign.campaign_name || 'Unnamed'}\n`;
formattedText += `**ID**: ${campaign.campaign_id}\n\n`;
// Create a table for key metrics
formattedText += "#### Key Metrics\n\n";
formattedText += "| Metric | Value |\n|---|---|\n";
formattedText += `| Total Leads | ${campaign.leads_count || 0} |\n`;
formattedText += `| Contacted | ${campaign.contacted_count || 0} |\n`;
formattedText += `| Opens | ${campaign.open_count || 0} |\n`;
formattedText += `| Replies | ${campaign.reply_count || 0} |\n`;
formattedText += `| Bounced | ${campaign.bounced_count || 0} |\n`;
formattedText += `| Unsubscribed | ${campaign.unsubscribed_count || 0} |\n`;
formattedText += `| Completed | ${campaign.completed_count || 0} |\n`;
formattedText += `| Emails Sent | ${campaign.emails_sent_count || 0} |\n`;
formattedText += `| New Leads Contacted | ${campaign.new_leads_contacted_count || 0} |\n`;
// Add opportunity data if available
if (campaign.total_opportunities !== undefined || campaign.total_opportunity_value !== undefined) {
formattedText += "\n#### Opportunity Metrics\n\n";
formattedText += "| Metric | Value |\n|---|---|\n";
if (campaign.total_opportunities !== undefined) {
formattedText += `| Total Opportunities | ${campaign.total_opportunities} |\n`;
}
if (campaign.total_opportunity_value !== undefined) {
formattedText += `| Total Opportunity Value | ${campaign.total_opportunity_value} |\n`;
}
}
formattedText += "\n---\n\n";
}
}
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting campaign analytics: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
// Account handlers
async function handleCreateAccount({
email,
first_name,
last_name,
provider_code,
imap_username,
imap_password,
imap_host,
imap_port,
smtp_username,
smtp_password,
smtp_host,
smtp_port,
daily_limit,
tracking_domain_name
}: {
email: string;
first_name: string;
last_name: string;
provider_code: number;
imap_username: string;
imap_password: string;
imap_host: string;
imap_port: number;
smtp_username: string;
smtp_password: string;
smtp_host: string;
smtp_port: number;
daily_limit?: number;
tracking_domain_name?: string;
}) {
try {
const payload: any = {
email,
first_name,
last_name,
provider_code,
imap_username,
imap_password,
imap_host,
imap_port,
smtp_username,
smtp_password,
smtp_host,
smtp_port
};
// Add optional fields if provided
if (daily_limit !== undefined || tracking_domain_name !== undefined) {
payload.warmup = {};
if (daily_limit !== undefined) {
payload.warmup.daily_limit = daily_limit;
}
if (tracking_domain_name !== undefined) {
payload.warmup.tracking_domain_name = tracking_domain_name;
}
}
const response = await fetch('https://api.instantly.ai/api/v2/accounts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
const account = await response.json() as AccountResponse;
return {
content: [{
type: "text",
text: `Account created successfully:\n\nEmail: ${account.email}\nName: ${account.first_name} ${account.last_name}\nProvider: ${getProviderNameFromCode(account.provider_code)}\nWarmup Status: ${getWarmupStatusFromCode(account.warmup_status)}\nCreated: ${account.timestamp_created}`
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleListAccounts({
limit = 10,
starting_after,
search,
status,
provider_code,
fetch_all = false
}: {
limit?: number;
starting_after?: string;
search?: string;
status?: number;
provider_code?: number;
fetch_all?: boolean;
}) {
try {
// This will store all accounts when fetching all pages
const allAccounts: AccountResponse[] = [];
let nextStartingAfter = starting_after;
let hasMorePages = true;
let pageCount = 0;
// If fetch_all is true, retrieve all pages
while ((fetch_all && hasMorePages) || pageCount === 0) {
let url = `https://api.instantly.ai/api/v2/accounts?limit=${limit}`;
if (nextStartingAfter) {
url += `&starting_after=${nextStartingAfter}`;
}
if (search) {
url += `&search=${encodeURIComponent(search)}`;
}
if (status !== undefined) {
url += `&status=${status}`;
}
if (provider_code !== undefined) {
url += `&provider_code=${provider_code}`;
}
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
const data = await response.json() as InstantlyResponse;
const accounts = data.items as AccountResponse[];
// Add this page's accounts to our collection
allAccounts.push(...accounts);
// Prepare for next page if needed
nextStartingAfter = data.next_starting_after;
hasMorePages = !!nextStartingAfter && accounts.length > 0;
pageCount++;
// Safety check to avoid infinite loops
if (pageCount > 100) {
break;
}
}
// Generate formatted output
let formattedText = '';
if (fetch_all) {
formattedText = generateAccountsSummary(allAccounts);
} else {
formattedText = generateAccountsTable(allAccounts, nextStartingAfter);
}
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error listing accounts: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
// Helper function to generate a table of accounts with pagination info
function generateAccountsTable(accounts: AccountResponse[], nextStartingAfter?: string): string {
let formattedText = '# Email Accounts\n\n';
if (accounts.length === 0) {
formattedText += 'No accounts found.\n';
return formattedText;
}
// Create a table for accounts
formattedText += "| Email | Name | Provider | Status | Daily Limit |\n|---|---|---|---|---|\n";
for (const account of accounts) {
const providerName = getProviderNameFromCode(account.provider_code);
const statusText = getWarmupStatusFromCode(account.warmup_status);
const dailyLimit = account.daily_limit || 'N/A';
formattedText += `| ${account.email} | ${account.first_name} ${account.last_name} | ${providerName} | ${statusText} | ${dailyLimit} |\n`;
}
formattedText += `\n**Total accounts in this page**: ${accounts.length}\n`;
if (nextStartingAfter) {
formattedText += `\n*For more results, use starting_after=${nextStartingAfter} or set fetch_all=true to automatically retrieve all pages*\n`;
}
return formattedText;
}
// Helper function to generate a comprehensive summary of all accounts
function generateAccountsSummary(accounts: AccountResponse[]): string {
let formattedText = '# Email Accounts Summary\n\n';
if (accounts.length === 0) {
formattedText += 'No accounts found.\n';
return formattedText;
}
// Count accounts by provider
const providerCounts: Record<string, number> = {};
// Count accounts by status
const statusCounts: Record<string, number> = {};
for (const account of accounts) {
const providerName = getProviderNameFromCode(account.provider_code);
const statusText = getWarmupStatusFromCode(account.warmup_status);
providerCounts[providerName] = (providerCounts[providerName] || 0) + 1;
statusCounts[statusText] = (statusCounts[statusText] || 0) + 1;
}
// Summary statistics
formattedText += `**Total Accounts**: ${accounts.length}\n\n`;
// Provider breakdown
formattedText += "## Accounts by Provider\n\n";
formattedText += "| Provider | Count | Percentage |\n|---|---|---|\n";
for (const [provider, count] of Object.entries(providerCounts)) {
const percentage = ((count / accounts.length) * 100).toFixed(1);
formattedText += `| ${provider} | ${count} | ${percentage}% |\n`;
}
// Status breakdown
formattedText += "\n## Accounts by Status\n\n";
formattedText += "| Status | Count | Percentage |\n|---|---|---|\n";
for (const [status, count] of Object.entries(statusCounts)) {
const percentage = ((count / accounts.length) * 100).toFixed(1);
formattedText += `| ${status} | ${count} | ${percentage}% |\n`;
}
// Add the first few accounts as examples
const sampleSize = Math.min(5, accounts.length);
formattedText += `\n## Sample Accounts (${sampleSize} of ${accounts.length})\n\n`;
formattedText += "| Email | Name | Provider | Status | Daily Limit |\n|---|---|---|---|---|\n";
for (let i = 0; i < sampleSize; i++) {
const account = accounts[i];
const providerName = getProviderNameFromCode(account.provider_code);
const statusText = getWarmupStatusFromCode(account.warmup_status);
const dailyLimit = account.daily_limit || 'N/A';
formattedText += `| ${account.email} | ${account.first_name} ${account.last_name} | ${providerName} | ${statusText} | ${dailyLimit} |\n`;
}
return formattedText;
}
async function handleGetAccount(email: string) {
try {
const response = await fetch(`https://api.instantly.ai/api/v2/accounts/${encodeURIComponent(email)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
const account = await response.json() as AccountResponse;
// Format the account details in markdown
let formattedText = `# Account Details: ${account.email}\n\n`;
formattedText += `**Name:** ${account.first_name} ${account.last_name}\n`;
formattedText += `**Provider:** ${getProviderNameFromCode(account.provider_code)}\n`;
formattedText += `**Status:** ${getWarmupStatusFromCode(account.warmup_status)}\n`;
formattedText += `**Created:** ${account.timestamp_created}\n`;
formattedText += `**Last Updated:** ${account.timestamp_updated}\n`;
if (account.daily_limit) {
formattedText += `**Daily Sending Limit:** ${account.daily_limit}\n`;
}
if (account.tracking_domain_name) {
formattedText += `**Tracking Domain:** ${account.tracking_domain_name}\n`;
}
formattedText += `**Setup Pending:** ${account.setup_pending ? 'Yes' : 'No'}\n`;
formattedText += `**Managed Account:** ${account.is_managed_account ? 'Yes' : 'No'}\n`;
return {
content: [{
type: "text",
text: formattedText
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error retrieving account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleUpdateAccount({
email,
first_name,
last_name,
daily_limit,
tracking_domain_name,
skip_cname_check,
remove_tracking_domain
}: {
email: string;
first_name?: string;
last_name?: string;
daily_limit?: number;
tracking_domain_name?: string;
skip_cname_check?: boolean;
remove_tracking_domain?: boolean;
}) {
try {
const payload: any = {};
if (first_name !== undefined) {
payload.first_name = first_name;
}
if (last_name !== undefined) {
payload.last_name = last_name;
}
if (daily_limit !== undefined || tracking_domain_name !== undefined) {
payload.warmup = {};
if (daily_limit !== undefined) {
payload.warmup.daily_limit = daily_limit;
}
if (tracking_domain_name !== undefined) {
payload.warmup.tracking_domain_name = tracking_domain_name;
}
}
if (skip_cname_check !== undefined) {
payload.skip_cname_check = skip_cname_check;
}
if (remove_tracking_domain !== undefined) {
payload.remove_tracking_domain = remove_tracking_domain;
}
const response = await fetch(`https://api.instantly.ai/api/v2/accounts/${encodeURIComponent(email)}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
const account = await response.json() as AccountResponse;
return {
content: [{
type: "text",
text: `Account ${account.email} updated successfully.\n\nCurrent details:\nName: ${account.first_name} ${account.last_name}\nStatus: ${getWarmupStatusFromCode(account.warmup_status)}\nDaily Limit: ${account.daily_limit || 'N/A'}\nTracking Domain: ${account.tracking_domain_name || 'None'}`
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error updating account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleDeleteAccount(email: string) {
try {
const response = await fetch(`https://api.instantly.ai/api/v2/accounts/${encodeURIComponent(email)}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
return {
content: [{
type: "text",
text: `Account ${email} has been deleted successfully.`
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error deleting account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handlePauseAccount(email: string) {
try {
const response = await fetch(`https://api.instantly.ai/api/v2/accounts/${encodeURIComponent(email)}/pause`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
return {
content: [{
type: "text",
text: `Account ${email} has been paused successfully.`
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error pausing account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
async function handleResumeAccount(email: string) {
try {
const response = await fetch(`https://api.instantly.ai/api/v2/accounts/${encodeURIComponent(email)}/resume`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getApiKey()}`
}
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
}
return {
content: [{
type: "text",
text: `Account ${email} has been resumed successfully.`
}],
isError: false
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error resuming account: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
// Helper functions for account status and provider codes
function getProviderNameFromCode(code: number): string {
switch (code) {
case 1: return 'Custom IMAP/SMTP';
case 2: return 'Google';
case 3: return 'Microsoft';
case 4: return 'AWS';
default: return 'Unknown';
}
}
function getWarmupStatusFromCode(code: number): string {
switch (code) {
case 0: return 'Paused';
case 1: return 'Active';
case -1: return 'Banned';
case -2: return 'Spam Folder Unknown';
case -3: return 'Permanent Suspension';
default: return 'Unknown';
}
}
// Server setup
const server = new Server(
{
name: "mcp-server/instantly",
version: "0.1.0",
},
{
capabilities: {
tools: {
instantly_create_lead: {},
instantly_get_lead: {},
instantly_list_leads: {},
instantly_update_lead: {},
instantly_delete_lead: {},
instantly_list_campaigns: {},
instantly_get_campaign: {},
instantly_get_warmup_analytics: {},
instantly_test_account_vitals: {},
instantly_get_campaign_analytics: {},
instantly_create_account: {},
instantly_list_accounts: {},
instantly_get_account: {},
instantly_update_account: {},
instantly_delete_account: {},
instantly_pause_account: {},
instantly_resume_account: {},
},
},
},
);
// Set up request handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: INSTANTLY_TOOLS,
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "instantly_create_lead": {
const params = request.params.arguments as {
email: string;
first_name?: string;
last_name?: string;
company_name?: string;
campaign?: string;
list_id?: string;
personalization?: string;
website?: string;
phone?: string;
custom_variables?: any;
};
return await handleCreateLead(params);
}
case "instantly_get_lead": {
const { id } = request.params.arguments as { id: string };
return await handleGetLead(id);
}
case "instantly_list_leads": {
const params = request.params.arguments as {
campaign?: string;
list_id?: string;
limit?: number;
starting_after?: string;
};
return await handleListLeads(params);
}
case "instantly_update_lead": {
const params = request.params.arguments as {
id: string;
first_name?: string;
last_name?: string;
company_name?: string;
personalization?: string;
website?: string;
phone?: string;
custom_variables?: any;
};
return await handleUpdateLead(params);
}
case "instantly_delete_lead": {
const { id } = request.params.arguments as { id: string };
return await handleDeleteLead(id);
}
case "instantly_list_campaigns": {
const params = request.params.arguments as {
limit?: number;
starting_after?: string;
status?: number;
};
return await handleListCampaigns(params);
}
case "instantly_get_campaign": {
const { id } = request.params.arguments as { id: string };
return await handleGetCampaign(id);
}
case "instantly_get_warmup_analytics": {
const params = request.params.arguments as {
emails: string[];
};
return await handleGetWarmupAnalytics(params.emails);
}
case "instantly_test_account_vitals": {
const params = request.params.arguments as {
accounts: string[];
};
return await handleTestAccountVitals(params.accounts);
}
case "instantly_get_campaign_analytics": {
const params = request.params.arguments as {
id?: string;
start_date: string;
end_date: string;
};
return await handleGetCampaignAnalytics(params);
}
case "instantly_create_account": {
const params = request.params.arguments as {
email: string;
first_name: string;
last_name: string;
provider_code: number;
imap_username: string;
imap_password: string;
imap_host: string;
imap_port: number;
smtp_username: string;
smtp_password: string;
smtp_host: string;
smtp_port: number;
daily_limit?: number;
tracking_domain_name?: string;
};
return await handleCreateAccount(params);
}
case "instantly_list_accounts": {
const params = request.params.arguments as {
limit?: number;
starting_after?: string;
search?: string;
status?: number;
provider_code?: number;
fetch_all?: boolean;
};
return await handleListAccounts(params);
}
case "instantly_get_account": {
const { email } = request.params.arguments as { email: string };
return await handleGetAccount(email);
}
case "instantly_update_account": {
const params = request.params.arguments as {
email: string;
first_name?: string;
last_name?: string;
daily_limit?: number;
tracking_domain_name?: string;
skip_cname_check?: boolean;
remove_tracking_domain?: boolean;
};
return await handleUpdateAccount(params);
}
case "instantly_delete_account": {
const { email } = request.params.arguments as { email: string };
return await handleDeleteAccount(email);
}
case "instantly_pause_account": {
const { email } = request.params.arguments as { email: string };
return await handlePauseAccount(email);
}
case "instantly_resume_account": {
const { email } = request.params.arguments as { email: string };
return await handleResumeAccount(email);
}
default:
return {
content: [{
type: "text",
text: `Unknown tool: ${request.params.name}`
}],
isError: true
};
}
} catch (error) {
return {
content: [{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Instantly MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});