@rockship/apollo-io-mcp
Version:
A powerful Model Context Protocol (MCP) server implementation for seamless Apollo.io API integration, enabling AI assistants to interact with Apollo.io data
243 lines • 8.81 kB
JavaScript
import axios from "axios";
import dotenv from "dotenv";
import { contactSearchMappingResponse, organizationSearchMappingResponse, peopleSearchMappingResponse, } from "../../utils/mapping-response.js";
import { stripUrl } from "../../utils/url-helpers.js";
// Load environment variables
dotenv.config();
export class ApolloClient {
apiKey;
baseUrl;
headers;
axiosInstance;
constructor(apiKey) {
this.apiKey = apiKey || process.env.APOLLO_IO_API_KEY || "";
if (!this.apiKey) {
throw new Error("APOLLO_IO_API_KEY environment variable is required");
}
this.baseUrl = "https://api.apollo.io/api/v1";
this.headers = {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
"x-api-key": this.apiKey,
};
this.axiosInstance = this.getAxiosInstance({ baseUrl: this.baseUrl });
}
getAxiosInstance({ baseUrl }) {
return axios.create({
baseURL: baseUrl,
headers: this.headers,
});
}
async request({ baseUrl = "", url = "", method = "post", data, query, }) {
let response = null;
try {
// console.log("url", url);
// console.log("query", query);
const config = {};
if (query) {
config.params = query;
}
if (method === "get" || method === "delete") {
// For GET and DELETE, data (request body) should be passed in the config object
response = baseUrl
? await this.getAxiosInstance({ baseUrl })[method](url, config)
: await this.axiosInstance[method](url, config);
}
else {
// For POST, PUT, PATCH, data is the second argument, and config is the third
response = baseUrl
? await this.getAxiosInstance({ baseUrl })[method](url, data, config)
: await this.axiosInstance[method](url, data, config);
}
if (response.status === 200) {
return response.data;
}
else {
// console.log(`Error: ${response.status} - ${response.statusText}`);
return null;
}
}
catch (error) {
// console.log(
// `Error: ${error.response ? error.response.status : "N/A"} - ${
// error.response
// ? error.response.statusText || error.message
// : error.message
// }`
// );
return null;
}
}
/**
* Use the Contact Search endpoint to find contacts.
* https://docs.apollo.io/reference/contact-search
*/
async contactSearch(query) {
const response = await this.request({
url: "/contacts/search",
method: "post",
query,
});
return contactSearchMappingResponse(response);
}
/**
* Use the People Enrichment endpoint to enrich data for 1 person.
* https://docs.apollo.io/reference/people-enrichment
*/
async peopleEnrichment(query) {
const response = await this.request({
url: "/people/match",
method: "post",
query,
});
return response;
}
/**
* Use the Organization Enrichment endpoint to enrich data for 1 company.
* https://docs.apollo.io/reference/organization-enrichment
*/
async organizationEnrichment(query) {
const response = await this.request({
url: "/organizations/enrich",
method: "post",
query,
});
return response;
}
/**
* Use the People Search endpoint to find people.
* https://docs.apollo.io/reference/people-search
*/
async peopleSearch(query) {
const response = await this.request({
url: "/mixed_people/search",
method: "post",
query,
});
return peopleSearchMappingResponse(response);
}
/**
* Use the Organization Search endpoint to find organizations.
* https://docs.apollo.io/reference/organization-search
*/
async organizationSearch(query) {
const response = await this.request({
url: "/mixed_companies/search",
method: "post",
query,
});
return organizationSearchMappingResponse(response);
}
/**
* Use the Organization Job Postings endpoint to find job postings for a specific organization.
* https://docs.apollo.io/reference/organization-jobs-postings
*/
async organizationJobPostings(organizationId) {
const response = await this.request({
url: `/organizations/${organizationId}/job_postings`,
method: "get",
});
return response;
}
/**
* Get email address for a person using their Apollo ID
*/
async getPersonEmail(apolloId) {
if (!apolloId) {
throw new Error("Apollo ID is required");
}
const payload = {
entity_ids: [apolloId],
analytics_context: "Searcher: Individual Add Button",
skip_fetching_people: true,
cta_name: "Access email",
cacheKey: Date.now(),
};
const response = await this.request({
baseUrl: "https://app.apollo.io/api/v1",
url: "/mixed_people/add_to_my_prospects",
method: "post",
data: payload,
});
const emails = ((response && response.contacts) || []).map((contact) => contact.email);
return emails;
}
/**
* Find employees of a company using company name or website/LinkedIn URL
*/
async employeesOfCompany(query) {
const { company, website_url, linkedin_url } = query;
if (!company) {
throw new Error("Company name is required");
}
const strippedWebsiteUrl = stripUrl(website_url);
const strippedLinkedinUrl = stripUrl(linkedin_url);
// First search for the company
const companySearchPayload = {
q_organization_name: company,
page: 1,
per_page: 100,
};
const mixedCompaniesResponse = await this.request({
url: "/mixed_companies/search",
method: "post",
query: companySearchPayload,
});
if (!mixedCompaniesResponse) {
throw new Error("No data received from Apollo API");
}
let organizations = mixedCompaniesResponse.organizations;
if (organizations.length === 0) {
throw new Error("No organizations found");
}
// Filter companies by website or LinkedIn URL if provided
const companyObjs = organizations.filter((item) => {
const companyLinkedin = stripUrl(item.linkedin_url);
const companyWebsite = stripUrl(item.website_url);
if (strippedLinkedinUrl &&
companyLinkedin &&
companyLinkedin === strippedLinkedinUrl) {
return true;
}
else if (strippedWebsiteUrl &&
companyWebsite &&
companyWebsite === strippedWebsiteUrl) {
return true;
}
return false;
});
// If we have filtered results, use the first one, otherwise use the first from the original search
const companyObj = companyObjs.length > 0 ? companyObjs[0] : organizations[0];
const companyId = companyObj.id;
if (!companyId) {
throw new Error("Could not determine company ID");
}
// Now search for employees
const peopleSearchPayload = {
organization_ids: [companyId],
page: 1,
per_page: 100,
};
// Add optional filters if provided in the tool config
if (query.person_seniorities) {
peopleSearchPayload.person_titles = (query.person_seniorities || "")
.split(",")
.map((item) => item.trim());
}
if (query.contact_email_status) {
peopleSearchPayload.contact_email_status_v2 = (query.contact_email_status || "")
.split(",")
.map((item) => item.trim());
}
const peopleResponse = await this.request({
url: "/mixed_people/search",
method: "post",
query: peopleSearchPayload,
});
if (!peopleResponse) {
throw new Error("No data received from Apollo API");
}
return peopleSearchMappingResponse(peopleResponse);
}
}
//# sourceMappingURL=apollo-client.js.map