Skip to main content
Glama
index.ts38.7 kB
#!/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 axios, { AxiosInstance } from "axios"; import dotenv from "dotenv"; dotenv.config(); const APOLLO_API_KEY = process.env.APOLLO_API_KEY; const APOLLO_BASE_URL = "https://api.apollo.io/v1"; if (!APOLLO_API_KEY) { console.error("Error: APOLLO_API_KEY environment variable is required"); process.exit(1); } class ApolloMCPServer { private server: Server; private axiosInstance: AxiosInstance; constructor() { this.server = new Server( { name: "apollo-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.axiosInstance = axios.create({ baseURL: APOLLO_BASE_URL, headers: { "Content-Type": "application/json", "Cache-Control": "no-cache", "X-Api-Key": APOLLO_API_KEY, }, }); this.setupHandlers(); } private setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.getTools(), })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "search_people": return await this.searchPeople(args); case "search_organizations": return await this.searchOrganizations(args); case "enrich_person": return await this.enrichPerson(args); case "enrich_organization": return await this.enrichOrganization(args); case "find_email": return await this.findEmail(args); case "list_sequences": return await this.listSequences(args); case "get_sequence": return await this.getSequence(args); case "analyze_sequence": return await this.analyzeSequence(args); case "add_to_sequence": return await this.addToSequence(args); case "remove_from_sequence": return await this.removeFromSequence(args); case "get_lists": return await this.getLists(args); case "get_list_contacts": return await this.getListContacts(args); case "analyze_list": return await this.analyzeList(args); case "create_contact": return await this.createContact(args); case "update_contact": return await this.updateContact(args); case "create_account": return await this.createAccount(args); case "get_account": return await this.getAccount(args); case "search_job_postings": return await this.searchJobPostings(args); case "get_person_activity": return await this.getPersonActivity(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error: any) { return { content: [ { type: "text", text: `Error: ${error.message}\n${error.response?.data ? JSON.stringify(error.response.data, null, 2) : ""}`, }, ], }; } }); } private getTools(): Tool[] { return [ { name: "search_people", description: "Search for people/contacts in Apollo's database with advanced filters. Use this to find prospects, leads, or specific individuals based on criteria like job title, company, location, industry, seniority, etc.", inputSchema: { type: "object", properties: { q_keywords: { type: "string", description: "Search keywords for person name or title", }, person_titles: { type: "array", items: { type: "string" }, description: 'Job titles (e.g., ["CEO", "CTO", "VP Sales"])', }, person_seniorities: { type: "array", items: { type: "string" }, description: 'Seniority levels: "senior", "manager", "director", "vp", "c_suite", "owner", "partner"', }, organization_ids: { type: "array", items: { type: "string" }, description: "Filter by specific organization IDs", }, organization_locations: { type: "array", items: { type: "string" }, description: 'Locations (e.g., ["San Francisco, CA", "New York, NY"])', }, organization_industry_tag_ids: { type: "array", items: { type: "string" }, description: "Industry tags to filter by", }, person_locations: { type: "array", items: { type: "string" }, description: "Person locations", }, page: { type: "number", description: "Page number (default: 1)", }, per_page: { type: "number", description: "Results per page (default: 25, max: 100)", }, }, }, }, { name: "search_organizations", description: "Search for companies/organizations in Apollo's database. Filter by industry, size, location, revenue, technology, and more. Great for building targeted account lists.", inputSchema: { type: "object", properties: { q_organization_name: { type: "string", description: "Organization name search", }, organization_locations: { type: "array", items: { type: "string" }, description: 'Locations (e.g., ["San Francisco, CA"])', }, organization_industry_tag_ids: { type: "array", items: { type: "string" }, description: "Industry tag IDs", }, organization_num_employees_ranges: { type: "array", items: { type: "string" }, description: 'Employee count ranges: "1-10", "11-50", "51-200", "201-500", "501-1000", "1001-5000", "5001-10000", "10001+"', }, revenue_range: { type: "object", properties: { min: { type: "number" }, max: { type: "number" }, }, description: "Revenue range filter", }, organization_keywords: { type: "array", items: { type: "string" }, description: "Keywords to search in organization data", }, page: { type: "number", description: "Page number (default: 1)", }, per_page: { type: "number", description: "Results per page (default: 25, max: 100)", }, }, }, }, { name: "enrich_person", description: "Enrich a person's data with email, phone, social profiles, employment info, and more. Provide either email, name+domain, or LinkedIn URL.", inputSchema: { type: "object", properties: { email: { type: "string", description: "Person's email address", }, first_name: { type: "string", description: "First name (use with domain)", }, last_name: { type: "string", description: "Last name (use with domain)", }, domain: { type: "string", description: "Company domain (e.g., apollo.io)", }, linkedin_url: { type: "string", description: "LinkedIn profile URL", }, reveal_personal_emails: { type: "boolean", description: "Include personal email addresses", }, }, }, }, { name: "enrich_organization", description: "Enrich an organization's data with detailed company information, employee count, revenue, technologies used, funding, and more. Provide domain name.", inputSchema: { type: "object", properties: { domain: { type: "string", description: "Company domain (e.g., apollo.io)", }, }, required: ["domain"], }, }, { name: "find_email", description: "Find and verify email addresses for a person. Provide name and company domain or LinkedIn URL.", inputSchema: { type: "object", properties: { first_name: { type: "string", description: "First name", }, last_name: { type: "string", description: "Last name", }, domain: { type: "string", description: "Company domain", }, linkedin_url: { type: "string", description: "LinkedIn profile URL", }, }, }, }, { name: "list_sequences", description: "List all email sequences in your Apollo account. Sequences are automated email campaigns.", inputSchema: { type: "object", properties: { page: { type: "number", description: "Page number", }, }, }, }, { name: "get_sequence", description: "Get detailed information about a specific sequence including steps, stats, and settings.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Sequence ID", }, }, required: ["id"], }, }, { name: "analyze_sequence", description: "Analyze a sequence's performance with detailed metrics: open rates, reply rates, bounce rates, contacts added, active contacts, and step-by-step analytics.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Sequence ID to analyze", }, }, required: ["id"], }, }, { name: "add_to_sequence", description: "Add contacts to a sequence. Provide sequence ID and contact email addresses or contact IDs.", inputSchema: { type: "object", properties: { sequence_id: { type: "string", description: "Sequence ID", }, contact_ids: { type: "array", items: { type: "string" }, description: "Array of contact IDs to add", }, contact_emails: { type: "array", items: { type: "string" }, description: "Array of contact emails to add", }, mailbox_id: { type: "string", description: "Mailbox ID to send from (optional)", }, }, required: ["sequence_id"], }, }, { name: "remove_from_sequence", description: "Remove contacts from a sequence. Provide sequence ID and contact IDs.", inputSchema: { type: "object", properties: { sequence_id: { type: "string", description: "Sequence ID", }, contact_ids: { type: "array", items: { type: "string" }, description: "Array of contact IDs to remove", }, }, required: ["sequence_id", "contact_ids"], }, }, { name: "get_lists", description: "Get all contact lists in your Apollo account. Lists are collections of saved contacts.", inputSchema: { type: "object", properties: { page: { type: "number", description: "Page number", }, }, }, }, { name: "get_list_contacts", description: "Scrape/retrieve all contacts from a specific list with full details including emails, titles, companies, etc.", inputSchema: { type: "object", properties: { id: { type: "string", description: "List ID", }, page: { type: "number", description: "Page number (default: 1)", }, per_page: { type: "number", description: "Results per page (default: 100)", }, }, required: ["id"], }, }, { name: "analyze_list", description: "Analyze a contact list with detailed breakdown: total contacts, job titles distribution, seniority levels, companies, locations, industries, and data completeness metrics.", inputSchema: { type: "object", properties: { id: { type: "string", description: "List ID to analyze", }, }, required: ["id"], }, }, { name: "create_contact", description: "Create a new contact in Apollo with details like name, email, title, company, etc.", inputSchema: { type: "object", properties: { first_name: { type: "string", description: "First name", }, last_name: { type: "string", description: "Last name", }, email: { type: "string", description: "Email address", }, title: { type: "string", description: "Job title", }, organization_name: { type: "string", description: "Company name", }, linkedin_url: { type: "string", description: "LinkedIn URL", }, phone_numbers: { type: "array", items: { type: "string" }, description: "Phone numbers", }, }, required: ["first_name", "last_name"], }, }, { name: "update_contact", description: "Update an existing contact's information in Apollo.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Contact ID", }, first_name: { type: "string", description: "First name", }, last_name: { type: "string", description: "Last name", }, email: { type: "string", description: "Email address", }, title: { type: "string", description: "Job title", }, linkedin_url: { type: "string", description: "LinkedIn URL", }, }, required: ["id"], }, }, { name: "create_account", description: "Create a new account/organization in Apollo with company details.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Company name", }, domain: { type: "string", description: "Company domain", }, phone_number: { type: "string", description: "Company phone", }, website_url: { type: "string", description: "Website URL", }, }, required: ["name"], }, }, { name: "get_account", description: "Get detailed information about an account/organization by ID or domain.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Account ID", }, domain: { type: "string", description: "Company domain", }, }, }, }, { name: "search_job_postings", description: "Search for job postings to identify companies that are hiring and find buying signals.", inputSchema: { type: "object", properties: { q_keywords: { type: "string", description: "Keywords to search in job postings", }, organization_ids: { type: "array", items: { type: "string" }, description: "Filter by organization IDs", }, page: { type: "number", description: "Page number", }, }, }, }, { name: "get_person_activity", description: "Get activity history and engagement data for a specific person/contact.", inputSchema: { type: "object", properties: { id: { type: "string", description: "Person/Contact ID", }, }, required: ["id"], }, }, ]; } // People Search private async searchPeople(args: any) { const response = await this.axiosInstance.post("/mixed_people/search", args); const people = response.data.people || []; const pagination = response.data.pagination || {}; let result = `Found ${pagination.total_entries || people.length} people\n`; result += `Page ${pagination.page || 1} of ${pagination.total_pages || 1}\n\n`; people.forEach((person: any, index: number) => { result += `${index + 1}. ${person.first_name} ${person.last_name}\n`; result += ` ID: ${person.id}\n`; result += ` Title: ${person.title || "N/A"}\n`; result += ` Company: ${person.organization?.name || "N/A"}\n`; result += ` Location: ${person.city ? `${person.city}, ${person.state || ""}` : "N/A"}\n`; result += ` Email: ${person.email || "N/A"}\n`; result += ` LinkedIn: ${person.linkedin_url || "N/A"}\n`; result += ` Seniority: ${person.seniority || "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Organization Search private async searchOrganizations(args: any) { const response = await this.axiosInstance.post("/mixed_companies/search", args); const organizations = response.data.organizations || []; const pagination = response.data.pagination || {}; let result = `Found ${pagination.total_entries || organizations.length} organizations\n`; result += `Page ${pagination.page || 1} of ${pagination.total_pages || 1}\n\n`; organizations.forEach((org: any, index: number) => { result += `${index + 1}. ${org.name}\n`; result += ` ID: ${org.id}\n`; result += ` Domain: ${org.website_url || org.primary_domain || "N/A"}\n`; result += ` Industry: ${org.industry || "N/A"}\n`; result += ` Employees: ${org.estimated_num_employees || "N/A"}\n`; result += ` Location: ${org.city ? `${org.city}, ${org.state || org.country}` : "N/A"}\n`; result += ` Revenue: ${org.annual_revenue ? `$${org.annual_revenue}` : "N/A"}\n`; result += ` Phone: ${org.phone || "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Enrich Person private async enrichPerson(args: any) { const response = await this.axiosInstance.post("/people/match", args); const person = response.data.person; if (!person) { return { content: [ { type: "text", text: "No person found with the provided information.", }, ], }; } let result = `Person Enrichment Results:\n\n`; result += `Name: ${person.first_name} ${person.last_name}\n`; result += `ID: ${person.id}\n`; result += `Title: ${person.title || "N/A"}\n`; result += `Email: ${person.email || "N/A"}\n`; result += `Phone: ${person.phone_numbers?.[0]?.raw_number || "N/A"}\n`; result += `LinkedIn: ${person.linkedin_url || "N/A"}\n`; result += `Location: ${person.city ? `${person.city}, ${person.state || ""}` : "N/A"}\n`; result += `Seniority: ${person.seniority || "N/A"}\n\n`; if (person.organization) { result += `Company Information:\n`; result += ` Name: ${person.organization.name}\n`; result += ` Domain: ${person.organization.website_url || "N/A"}\n`; result += ` Industry: ${person.organization.industry || "N/A"}\n`; result += ` Employees: ${person.organization.estimated_num_employees || "N/A"}\n`; } return { content: [ { type: "text", text: result, }, ], }; } // Enrich Organization private async enrichOrganization(args: any) { const response = await this.axiosInstance.post("/organizations/enrich", args); const org = response.data.organization; if (!org) { return { content: [ { type: "text", text: "No organization found with the provided domain.", }, ], }; } let result = `Organization Enrichment Results:\n\n`; result += `Name: ${org.name}\n`; result += `ID: ${org.id}\n`; result += `Domain: ${org.website_url || org.primary_domain || "N/A"}\n`; result += `Industry: ${org.industry || "N/A"}\n`; result += `Description: ${org.short_description || "N/A"}\n`; result += `Founded: ${org.founded_year || "N/A"}\n`; result += `Employees: ${org.estimated_num_employees || "N/A"}\n`; result += `Revenue: ${org.annual_revenue ? `$${org.annual_revenue}` : "N/A"}\n`; result += `Location: ${org.city ? `${org.city}, ${org.state || org.country}` : "N/A"}\n`; result += `Phone: ${org.phone || "N/A"}\n`; result += `LinkedIn: ${org.linkedin_url || "N/A"}\n`; result += `Facebook: ${org.facebook_url || "N/A"}\n`; result += `Twitter: ${org.twitter_url || "N/A"}\n\n`; if (org.technologies) { result += `Technologies Used: ${org.technologies.join(", ")}\n`; } return { content: [ { type: "text", text: result, }, ], }; } // Find Email private async findEmail(args: any) { const response = await this.axiosInstance.post("/people/match", args); const person = response.data.person; if (!person) { return { content: [ { type: "text", text: "No email found for the provided information.", }, ], }; } let result = `Email Found:\n\n`; result += `Name: ${person.first_name} ${person.last_name}\n`; result += `Email: ${person.email || "Not available"}\n`; result += `Status: ${person.email_status || "N/A"}\n`; result += `Company: ${person.organization?.name || "N/A"}\n`; result += `Title: ${person.title || "N/A"}\n`; return { content: [ { type: "text", text: result, }, ], }; } // List Sequences private async listSequences(args: any) { const response = await this.axiosInstance.get("/emailer_campaigns", { params: args, }); const sequences = response.data.emailer_campaigns || []; let result = `Email Sequences (${sequences.length}):\n\n`; sequences.forEach((seq: any, index: number) => { result += `${index + 1}. ${seq.name}\n`; result += ` ID: ${seq.id}\n`; result += ` Status: ${seq.active ? "Active" : "Inactive"}\n`; result += ` Contacts: ${seq.num_steps || 0} steps\n`; result += ` Created: ${seq.created_at ? new Date(seq.created_at).toLocaleDateString() : "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Get Sequence private async getSequence(args: any) { const response = await this.axiosInstance.get(`/emailer_campaigns/${args.id}`); const seq = response.data.emailer_campaign; let result = `Sequence Details:\n\n`; result += `Name: ${seq.name}\n`; result += `ID: ${seq.id}\n`; result += `Status: ${seq.active ? "Active" : "Inactive"}\n`; result += `Created: ${seq.created_at ? new Date(seq.created_at).toLocaleDateString() : "N/A"}\n`; result += `Steps: ${seq.num_steps || 0}\n\n`; if (seq.emailer_steps && seq.emailer_steps.length > 0) { result += `Sequence Steps:\n`; seq.emailer_steps.forEach((step: any, index: number) => { result += `\nStep ${index + 1}:\n`; result += ` Type: ${step.type || "Email"}\n`; result += ` Wait: ${step.wait_time || 0} days\n`; result += ` Subject: ${step.subject || "N/A"}\n`; }); } return { content: [ { type: "text", text: result, }, ], }; } // Analyze Sequence private async analyzeSequence(args: any) { const response = await this.axiosInstance.get(`/emailer_campaigns/${args.id}`); const seq = response.data.emailer_campaign; let result = `Sequence Analysis: ${seq.name}\n\n`; result += `=== Overview ===\n`; result += `ID: ${seq.id}\n`; result += `Status: ${seq.active ? "Active" : "Inactive"}\n`; result += `Created: ${seq.created_at ? new Date(seq.created_at).toLocaleDateString() : "N/A"}\n`; result += `Total Steps: ${seq.num_steps || 0}\n\n`; result += `=== Performance Metrics ===\n`; result += `Total Contacts Added: ${seq.unique_scheduled || 0}\n`; result += `Active Contacts: ${seq.num_contacted_people || 0}\n`; result += `Bounced: ${seq.bounce_rate ? `${(seq.bounce_rate * 100).toFixed(2)}%` : "0%"}\n`; result += `Replied: ${seq.reply_rate ? `${(seq.reply_rate * 100).toFixed(2)}%` : "0%"}\n`; result += `Opened: ${seq.open_rate ? `${(seq.open_rate * 100).toFixed(2)}%` : "0%"}\n`; result += `Clicked: ${seq.click_rate ? `${(seq.click_rate * 100).toFixed(2)}%` : "0%"}\n\n`; if (seq.emailer_steps && seq.emailer_steps.length > 0) { result += `=== Step-by-Step Breakdown ===\n`; seq.emailer_steps.forEach((step: any, index: number) => { result += `\nStep ${index + 1}: ${step.type || "Email"}\n`; result += ` Subject: ${step.subject || "N/A"}\n`; result += ` Wait Time: ${step.wait_time || 0} days\n`; result += ` Max Emails: ${step.max_emails_per_day || "Unlimited"}\n`; }); } result += `\n=== Insights ===\n`; if (seq.reply_rate && seq.reply_rate > 0.1) { result += `✓ Strong reply rate - this sequence is performing well\n`; } else if (seq.reply_rate && seq.reply_rate < 0.05) { result += `⚠ Low reply rate - consider reviewing messaging and targeting\n`; } if (seq.bounce_rate && seq.bounce_rate > 0.05) { result += `⚠ High bounce rate - verify email quality\n`; } if (seq.open_rate && seq.open_rate < 0.3) { result += `⚠ Low open rate - test different subject lines\n`; } return { content: [ { type: "text", text: result, }, ], }; } // Add to Sequence private async addToSequence(args: any) { const response = await this.axiosInstance.post( `/emailer_campaigns/${args.sequence_id}/add_contact_ids`, { contact_ids: args.contact_ids, emailer_campaign_id: args.sequence_id, send_email_from_email_account_id: args.mailbox_id, } ); return { content: [ { type: "text", text: `Successfully added ${args.contact_ids?.length || args.contact_emails?.length || 0} contact(s) to sequence ${args.sequence_id}`, }, ], }; } // Remove from Sequence private async removeFromSequence(args: any) { const response = await this.axiosInstance.post( `/emailer_campaigns/${args.sequence_id}/remove_contact_ids`, { contact_ids: args.contact_ids, } ); return { content: [ { type: "text", text: `Successfully removed ${args.contact_ids.length} contact(s) from sequence ${args.sequence_id}`, }, ], }; } // Get Lists private async getLists(args: any) { const response = await this.axiosInstance.get("/contact_lists", { params: args, }); const lists = response.data.contact_lists || []; let result = `Contact Lists (${lists.length}):\n\n`; lists.forEach((list: any, index: number) => { result += `${index + 1}. ${list.name}\n`; result += ` ID: ${list.id}\n`; result += ` Contacts: ${list.num_contacts || 0}\n`; result += ` Created: ${list.created_at ? new Date(list.created_at).toLocaleDateString() : "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Get List Contacts private async getListContacts(args: any) { const page = args.page || 1; const perPage = args.per_page || 100; const response = await this.axiosInstance.get(`/contact_lists/${args.id}/contacts`, { params: { page, per_page: perPage }, }); const contacts = response.data.contacts || []; const pagination = response.data.pagination || {}; let result = `List Contacts (${pagination.total_entries || contacts.length} total)\n`; result += `Page ${pagination.page || 1} of ${pagination.total_pages || 1}\n\n`; contacts.forEach((contact: any, index: number) => { result += `${index + 1}. ${contact.first_name} ${contact.last_name}\n`; result += ` ID: ${contact.id}\n`; result += ` Email: ${contact.email || "N/A"}\n`; result += ` Title: ${contact.title || "N/A"}\n`; result += ` Company: ${contact.account?.name || "N/A"}\n`; result += ` Phone: ${contact.phone_numbers?.[0]?.raw_number || "N/A"}\n`; result += ` LinkedIn: ${contact.linkedin_url || "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Analyze List private async analyzeList(args: any) { // Fetch list details const listResponse = await this.axiosInstance.get(`/contact_lists/${args.id}`); const list = listResponse.data.contact_list; // Fetch contacts const contactsResponse = await this.axiosInstance.get( `/contact_lists/${args.id}/contacts`, { params: { per_page: 1000 }, } ); const contacts = contactsResponse.data.contacts || []; let result = `List Analysis: ${list.name}\n\n`; result += `=== Overview ===\n`; result += `Total Contacts: ${list.num_contacts || contacts.length}\n`; result += `Created: ${list.created_at ? new Date(list.created_at).toLocaleDateString() : "N/A"}\n\n`; // Analyze job titles const titles: { [key: string]: number } = {}; const seniorities: { [key: string]: number } = {}; const companies: { [key: string]: number } = {}; const locations: { [key: string]: number } = {}; let emailCount = 0; let phoneCount = 0; let linkedinCount = 0; contacts.forEach((contact: any) => { if (contact.title) { titles[contact.title] = (titles[contact.title] || 0) + 1; } if (contact.seniority) { seniorities[contact.seniority] = (seniorities[contact.seniority] || 0) + 1; } if (contact.account?.name) { companies[contact.account.name] = (companies[contact.account.name] || 0) + 1; } if (contact.city) { const location = `${contact.city}, ${contact.state || contact.country || ""}`; locations[location] = (locations[location] || 0) + 1; } if (contact.email) emailCount++; if (contact.phone_numbers?.length > 0) phoneCount++; if (contact.linkedin_url) linkedinCount++; }); result += `=== Data Completeness ===\n`; result += `Contacts with Email: ${emailCount} (${((emailCount / contacts.length) * 100).toFixed(1)}%)\n`; result += `Contacts with Phone: ${phoneCount} (${((phoneCount / contacts.length) * 100).toFixed(1)}%)\n`; result += `Contacts with LinkedIn: ${linkedinCount} (${((linkedinCount / contacts.length) * 100).toFixed(1)}%)\n\n`; result += `=== Top Job Titles ===\n`; Object.entries(titles) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .forEach(([title, count]) => { result += `${title}: ${count}\n`; }); result += `\n=== Seniority Distribution ===\n`; Object.entries(seniorities) .sort((a, b) => b[1] - a[1]) .forEach(([seniority, count]) => { result += `${seniority}: ${count}\n`; }); result += `\n=== Top Companies ===\n`; Object.entries(companies) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .forEach(([company, count]) => { result += `${company}: ${count}\n`; }); result += `\n=== Top Locations ===\n`; Object.entries(locations) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .forEach(([location, count]) => { result += `${location}: ${count}\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Create Contact private async createContact(args: any) { const response = await this.axiosInstance.post("/contacts", args); const contact = response.data.contact; return { content: [ { type: "text", text: `Contact created successfully!\nID: ${contact.id}\nName: ${contact.first_name} ${contact.last_name}\nEmail: ${contact.email || "N/A"}`, }, ], }; } // Update Contact private async updateContact(args: any) { const { id, ...updateData } = args; const response = await this.axiosInstance.put(`/contacts/${id}`, updateData); const contact = response.data.contact; return { content: [ { type: "text", text: `Contact updated successfully!\nID: ${contact.id}\nName: ${contact.first_name} ${contact.last_name}`, }, ], }; } // Create Account private async createAccount(args: any) { const response = await this.axiosInstance.post("/accounts", args); const account = response.data.account; return { content: [ { type: "text", text: `Account created successfully!\nID: ${account.id}\nName: ${account.name}\nDomain: ${account.domain || "N/A"}`, }, ], }; } // Get Account private async getAccount(args: any) { const endpoint = args.id ? `/accounts/${args.id}` : `/accounts/search?q_organization_domains[]=${args.domain}`; const response = await this.axiosInstance.get(endpoint); const account = args.id ? response.data.account : response.data.accounts?.[0]; if (!account) { return { content: [ { type: "text", text: "Account not found.", }, ], }; } let result = `Account Details:\n\n`; result += `Name: ${account.name}\n`; result += `ID: ${account.id}\n`; result += `Domain: ${account.domain || account.website_url || "N/A"}\n`; result += `Industry: ${account.industry || "N/A"}\n`; result += `Employees: ${account.estimated_num_employees || "N/A"}\n`; result += `Phone: ${account.phone || "N/A"}\n`; return { content: [ { type: "text", text: result, }, ], }; } // Search Job Postings private async searchJobPostings(args: any) { const response = await this.axiosInstance.post("/job_postings/search", args); const jobPostings = response.data.job_postings || []; let result = `Job Postings Found: ${jobPostings.length}\n\n`; jobPostings.slice(0, 20).forEach((job: any, index: number) => { result += `${index + 1}. ${job.title}\n`; result += ` Company: ${job.organization?.name || "N/A"}\n`; result += ` Location: ${job.city || "N/A"}\n`; result += ` Posted: ${job.posted_at ? new Date(job.posted_at).toLocaleDateString() : "N/A"}\n`; result += ` URL: ${job.url || "N/A"}\n\n`; }); return { content: [ { type: "text", text: result, }, ], }; } // Get Person Activity private async getPersonActivity(args: any) { const response = await this.axiosInstance.get(`/people/${args.id}/activities`); const activities = response.data.activities || []; let result = `Activity History:\n\n`; activities.forEach((activity: any, index: number) => { result += `${index + 1}. ${activity.type}\n`; result += ` Date: ${activity.created_at ? new Date(activity.created_at).toLocaleDateString() : "N/A"}\n`; result += ` Details: ${activity.note || "N/A"}\n\n`; }); if (activities.length === 0) { result += "No activity found for this contact.\n"; } return { content: [ { type: "text", text: result, }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Apollo.io MCP Server running on stdio"); } } const server = new ApolloMCPServer(); server.run().catch(console.error);

Latest Blog Posts

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/masridigital/apollo.io-mcp'

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