Skip to main content
Glama

Audiense Insights MCP Server

Official
by AudienseCo
audienseClient.ts8.45 kB
import fetch from "node-fetch"; import base64 from "base-64"; import { AuthResponse, IntelligenceReportsResponse, ReportInfoResponse } from "./types.js"; const { encode: base64Encode } = base64; import { AUDIENSE_API_BASE, CLIENT_ID, CLIENT_SECRET, TWITTER_BEARER_TOKEN, TWITTER_API_BASE } from "./config.js"; let accessToken: string | null = null; let tokenExpiration: number | null = null; /** * Retrieves an OAuth2 access token using client credentials. * Caches the token until it expires. */ async function getAccessToken(): Promise<string | null> { if (accessToken && tokenExpiration && Date.now() < tokenExpiration) { return accessToken; } const authHeader = `Basic ${Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64")}`; try { const response = await fetch(`${AUDIENSE_API_BASE}/login/token`, { method: "POST", headers: { Authorization: authHeader, "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "client_credentials" }).toString(), }); if (!response.ok) { console.error("Authentication error:", await response.text()); throw new Error(`Authentication error: ${response.status}`); } const data = (await response.json()) as AuthResponse; accessToken = data.access_token; tokenExpiration = Date.now() + data.expires_in * 1000; return accessToken; } catch (error) { console.error("Error retrieving access token:", error); return null; } } /** * Makes an authenticated request to the Audiense API. * Automatically retrieves and refreshes the access token if needed. */ async function makeAudienseRequest<T>(endpoint: string): Promise<T | null> { const token = await getAccessToken(); if (!token) { console.error("Failed to retrieve access token."); return null; } const url = `${AUDIENSE_API_BASE}${endpoint}`; try { const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${token}`, Accept: "application/json", }, }); if (!response.ok) { console.error(`Request error ${response.status} for ${endpoint}:`, await response.text()); return null; } return (await response.json()) as T; } catch (error) { console.error(`Request error for ${endpoint}:`, error); return null; } } /** * Retrieves the list of intelligence reports. */ export async function getIntelligenceReports(): Promise<IntelligenceReportsResponse | null> { return makeAudienseRequest<IntelligenceReportsResponse>("/reports/intelligence"); } /** * Retrieves details of a specific intelligence report. */ export async function getReportInfo(report_id: string): Promise<ReportInfoResponse | null> { return makeAudienseRequest<ReportInfoResponse>(`/reports/intelligence/${report_id}`); } /** * Retrieves audience insights for a given audience_insights_id. * If `insights` is provided, filters by those insights. */ export async function getAudienceInsights( audience_insights_id: string, insights?: string[] ): Promise<{ insights: { name: string; values: { key: string; value: string }[] }[] } | null> { const queryParams = insights ? `?insights=${insights.join(",")}` : ""; return makeAudienseRequest<{ insights: { name: string; values: { key: string; value: string }[] }[] }>( `/audience_insights/${audience_insights_id}${queryParams}` ); } type TwitterUser = { id: string; description?: string; is_identity_verified?: boolean; location?: string; name?: string; parody?: boolean; profile_image_url?: string; protected?: boolean; public_metrics?: Record<string, number>; url?: string; username?: string; verified?: boolean; verified_followers_count?: number; verified_type?: string; }; type TwitterAPIResponse = { data?: TwitterUser[]; }; /** * Fetches Twitter user details for up to 100 user IDs. */ async function fetchTwitterUserDetails(userIds: string[]): Promise<Record<string, TwitterUser>> { if (!TWITTER_BEARER_TOKEN) { console.error("Missing Twitter/X Bearer Token. Skipping Twitter/X enrichment."); return {}; } const idsParam = userIds.join(","); const userFields = [ "description", "is_identity_verified", "location", "name", "parody", "profile_image_url", "protected", "public_metrics", "url", "username", "verified", "verified_followers_count", "verified_type" ].join(","); const url = `${TWITTER_API_BASE}/users?ids=${idsParam}&user.fields=${userFields}`; try { const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${TWITTER_BEARER_TOKEN}`, "Content-Type": "application/json", }, }); if (!response.ok) { console.error("Twitter API error:", await response.text()); return {}; } const data = (await response.json()) as TwitterAPIResponse; if (!data.data) return {}; return Object.fromEntries(data.data.map((user) => [user.id, user])); } catch (error) { console.error("Error fetching Twitter user details:", error); return {}; } } /** * Compares the influencers of an audience with those of a baseline, enriched with Twitter user details. */ export async function compareAudienceInfluencers( audience_influencers_id: string, baseline_audience_influencers_id: string, cursor?: number, count?: number, bio_keyword?: string, entity_type?: "person" | "brand", followers_min?: number, followers_max?: number, categories?: string[], countries?: string[] ): Promise<{ cursor: { next: number; prev: number }; influencers: any[] } | null> { const queryParams = new URLSearchParams(); if (cursor !== undefined) queryParams.append("cursor", cursor.toString()); if (count !== undefined) queryParams.append("count", count.toString()); if (bio_keyword) queryParams.append("bio_keyword", bio_keyword); if (entity_type) queryParams.append("entity_type", entity_type); if (followers_min !== undefined) queryParams.append("followers_min", followers_min.toString()); if (followers_max !== undefined) queryParams.append("followers_max", followers_max.toString()); if (categories && categories.length > 0) queryParams.append("categories", categories.join(",")); if (countries && countries.length > 0) queryParams.append("countries", countries.join(",")); const endpoint = `/audience_influencers/${audience_influencers_id}/compared_to/${baseline_audience_influencers_id}?${queryParams.toString()}`; const data = await makeAudienseRequest<{ cursor: { next: number; prev: number }; influencers: { id: string; affinity: number; baseline_affinity: number; uniqueness: number }[] }>(endpoint); if (!data || !data.influencers.length) { return data; } // Get the first 100 influencers for enrichment const influencerIds = data.influencers.slice(0, 100).map((influencer) => influencer.id); const twitterData = await fetchTwitterUserDetails(influencerIds); // Merge Twitter details into influencer data const enrichedInfluencers = data.influencers.map((influencer) => ({ ...influencer, twitter: twitterData[influencer.id] || null, // Add Twitter data if available })); return { cursor: data.cursor, influencers: enrichedInfluencers, }; } /** * Retrieves the relevant content that an audience engages with. */ export async function getAudienceContent(audience_content_id: string): Promise<{ createdAt: string; startDate: string; endDate: string; status: string; likedContent: any; sharedContent: any; influentialContent: any; } | null> { return makeAudienseRequest<{ createdAt: string; startDate: string; endDate: string; status: string; likedContent: any; sharedContent: any; influentialContent: any; }>(`/audience_content/${audience_content_id}`); }

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/AudienseCo/mcp-audiense-insights'

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