Skip to main content
Glama
by mendeel
index.ts44.8 kB
#!/usr/bin/env node /** * Mixpanel MCP Server * * A Model Context Protocol server for Mixpanel analytics. * Provides AI assistants with access to Mixpanel data, events, and analytics. * * Usage: * npx mcp-mixpanel * npx mcp-mixpanel --username YOUR_USERNAME --password YOUR_PASSWORD --project-id YOUR_PROJECT_ID * npx mcp-mixpanel -u YOUR_USERNAME -p YOUR_PASSWORD -i YOUR_PROJECT_ID */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; /** * Parse command line arguments */ async function parseArgs() { const args = process.argv.slice(2); // Help flag if (args.includes('--help') || args.includes('-h')) { console.log(` Mixpanel MCP Server Usage: npx mcp-mixpanel [options] Options: --username, -u <username> Mixpanel service account username --password, -p <password> Mixpanel service account password --project-id, -i <project_id> Default Mixpanel project ID --region, -r <region> Mixpanel region (us or eu) - default: us --help, -h Show this help message --version, -v Show version information Examples: npx mcp-mixpanel --username myuser --password mypass --project-id 12345 npx mcp-mixpanel -u myuser -p mypass -i 12345 -r eu Environment Variables: MIXPANEL_SERVICE_ACCOUNT_USERNAME Mixpanel service account username MIXPANEL_SERVICE_ACCOUNT_PASSWORD Mixpanel service account password MIXPANEL_PROJECT_ID Default Mixpanel project ID MIXPANEL_REGION Mixpanel region (us or eu) - default: us LOG_LEVEL Log level (debug, info, warn, error) - default: info For more information, visit: https://github.com/your-org/mcp-mixpanel `); process.exit(0); } // Version flag if (args.includes('--version') || args.includes('-v')) { // Read version from package.json try { const fs = await import('fs'); const path = await import('path'); const { fileURLToPath } = await import('url'); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const packagePath = path.join(__dirname, '..', 'package.json'); const packageContent = fs.readFileSync(packagePath, 'utf8'); const packageJson = JSON.parse(packageContent); console.log(`mcp-mixpanel v${packageJson.version}`); } catch (error) { console.log('mcp-mixpanel v1.0.0'); } process.exit(0); } // Parse username const usernameIndex = args.findIndex(arg => arg === '--username' || arg === '-u'); let username = null; if (usernameIndex !== -1 && args[usernameIndex + 1]) { username = args[usernameIndex + 1]; } else if (process.env.MIXPANEL_SERVICE_ACCOUNT_USERNAME) { username = process.env.MIXPANEL_SERVICE_ACCOUNT_USERNAME; } // Parse password const passwordIndex = args.findIndex(arg => arg === '--password' || arg === '-p'); let password = null; if (passwordIndex !== -1 && args[passwordIndex + 1]) { password = args[passwordIndex + 1]; } else if (process.env.MIXPANEL_SERVICE_ACCOUNT_PASSWORD) { password = process.env.MIXPANEL_SERVICE_ACCOUNT_PASSWORD; } // Parse project ID const projectIdIndex = args.findIndex(arg => arg === '--project-id' || arg === '-i'); let projectId = null; if (projectIdIndex !== -1 && args[projectIdIndex + 1]) { projectId = args[projectIdIndex + 1]; } else if (process.env.MIXPANEL_PROJECT_ID) { projectId = process.env.MIXPANEL_PROJECT_ID; } // Parse region const regionIndex = args.findIndex(arg => arg === '--region' || arg === '-r'); let region = 'us'; // default value if (regionIndex !== -1 && args[regionIndex + 1]) { region = args[regionIndex + 1]; } else if (process.env.MIXPANEL_REGION) { region = process.env.MIXPANEL_REGION; } // Validate region if (!['us', 'eu'].includes(region)) { console.error(`Error: Invalid region '${region}'. Must be 'us' or 'eu'.`); process.exit(1); } return { username, password, projectId, region: region as 'us' | 'eu' }; } const configSchema = z.object({ service_account_username: z.string().describe("Mixpanel service account username for API authentication"), service_account_password: z.string().describe("Mixpanel service account password for API authentication"), project_id: z.string().describe("Default Mixpanel project ID to use when not specified in tool calls"), region: z.enum(["us", "eu"]).default("us").describe("Mixpanel region - 'us' for mixpanel.com or 'eu' for eu.mixpanel.com"), }); /** * Main function to start the MCP server */ async function main() { try { console.error('Starting Mixpanel MCP Server...'); // Parse command line arguments and environment variables const args = await parseArgs(); // Validate required arguments if (!args.username) { console.error("Error: Mixpanel service account username is required. Use --username or set MIXPANEL_SERVICE_ACCOUNT_USERNAME environment variable."); process.exit(1); } if (!args.password) { console.error("Error: Mixpanel service account password is required. Use --password or set MIXPANEL_SERVICE_ACCOUNT_PASSWORD environment variable."); process.exit(1); } if (!args.projectId) { console.error("Error: Mixpanel project ID is required. Use --project-id or set MIXPANEL_PROJECT_ID environment variable."); process.exit(1); } // Create config object const config = { service_account_username: args.username, service_account_password: args.password, project_id: args.projectId, region: args.region }; // Validate config with schema const validatedConfig = configSchema.parse(config); // Initialize the MCP server with metadata and capabilities const server = new Server( { name: "mcp-mixpanel", version: "1.0.0", }, { capabilities: { tools: {} } } ); const SERVICE_ACCOUNT_USER_NAME = validatedConfig.service_account_username; const SERVICE_ACCOUNT_PASSWORD = validatedConfig.service_account_password; const DEFAULT_PROJECT_ID = validatedConfig.project_id; const MIXPANEL_REGION = validatedConfig.region; const MIXPANEL_BASE_URL = MIXPANEL_REGION === "eu" ? "https://eu.mixpanel.com/api/query" : "https://mixpanel.com/api/query"; // Setup request handlers setupHandlers(server, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); // Start server using stdio transport const transport = new StdioServerTransport(); console.error('Transport initialized: stdio'); await server.connect(transport); console.error('Server started successfully'); } catch (error) { console.error('Failed to start server', error); process.exit(1); } } // Setup handlers function function setupHandlers(server: Server, config: { SERVICE_ACCOUNT_USER_NAME: string; SERVICE_ACCOUNT_PASSWORD: string; DEFAULT_PROJECT_ID: string; MIXPANEL_BASE_URL: string; }) { const { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL } = config; // List tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // Event Tools { name: "get_today_top_events", description: "Get today's top events from Mixpanel. Useful for quickly identifying the most active events happening today, spotting trends, and monitoring real-time user activity.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, type: { type: "string", enum: ["general", "average", "unique"], description: "The type of events to fetch, either general, average, or unique, defaults to general" }, limit: { type: "number", description: "Maximum number of events to return" } } } }, { name: "get_top_events", description: "Get a list of the most common events over the last 31 days. Useful for identifying key user actions, prioritizing feature development, and understanding overall platform usage patterns.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, type: { type: "string", enum: ["general", "average", "unique"], description: "The type of events to fetch, either general, average, or unique, defaults to general" }, limit: { type: "number", description: "Maximum number of events to return" } } } }, { name: "aggregate_event_counts", description: "Get event counts over time periods. Useful for analyzing event volume trends and identifying patterns in user activity over time.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, event: { type: "string", description: "The event name to get counts for" }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" }, unit: { type: "string", enum: ["hour", "day", "week", "month"], description: "The time unit for aggregation, defaults to day" } }, required: ["event", "from_date", "to_date"] } }, { name: "aggregated_event_property_values", description: "Analyze specific event properties and their values. Useful for understanding property distributions and identifying common values.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, event: { type: "string", description: "The event name to analyze properties for" }, property: { type: "string", description: "The property name to get values for" }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" }, limit: { type: "number", description: "Maximum number of property values to return" } }, required: ["event", "property", "from_date", "to_date"] } }, // User Profile Tools { name: "profile_event_activity", description: "Get data for a profile's event activity. Useful for understanding individual user journeys, troubleshooting user-specific issues, and analyzing behavior patterns of specific users.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, workspace_id: { type: "string", description: "The ID of the workspace if applicable" }, distinct_ids: { type: "string", description: "A JSON array as a string representing the distinct_ids to return activity feeds for. Example: [\"12a34aa567eb8d-9ab1c26f345b67-89123c45-6aeaa7-89f12af345f678\"]" }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" } }, required: ["distinct_ids", "from_date", "to_date"] } }, { name: "query_profiles", description: "Query user profiles with filtering. Useful for finding users based on profile properties and analyzing user segments.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, where: { type: "string", description: "JSON string representing the filter conditions for profiles" }, select: { type: "string", description: "JSON array string of properties to return. If not specified, returns all properties" }, limit: { type: "number", description: "Maximum number of profiles to return" } } } }, // Analytics Tools { name: "query_retention_report", description: "Analyze user retention patterns. Useful for understanding how well you retain users over time and identifying cohort behavior.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" }, born_event: { type: "string", description: "The event that defines when users are 'born' for retention analysis" }, event: { type: "string", description: "The event to measure retention for (optional, defaults to any event)" }, born_where: { type: "string", description: "JSON string representing additional filters for the born event" }, where: { type: "string", description: "JSON string representing additional filters for the retention event" }, interval: { type: "string", enum: ["day", "week", "month"], description: "The time interval for retention analysis, defaults to day" }, interval_count: { type: "number", description: "Number of intervals to analyze, defaults to 30" } }, required: ["from_date", "to_date", "born_event"] } }, { name: "query_funnel_report", description: "Get funnel conversion data. Useful for analyzing conversion rates through multi-step user flows and identifying drop-off points.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" }, events: { type: "string", description: "JSON array string of events that make up the funnel steps" }, funnel_window: { type: "number", description: "Number of days users have to complete the funnel" }, interval: { type: "string", enum: ["day", "week", "month"], description: "The time interval for funnel analysis" } }, required: ["from_date", "to_date", "events"] } }, { name: "list_saved_funnels", description: "List available saved funnels in the project. Useful for discovering existing funnel analyses and getting funnel IDs for further analysis.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." } } } }, { name: "list_saved_cohorts", description: "List user cohorts in the project. Useful for discovering existing user segments and getting cohort IDs for analysis.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." } } } }, // Advanced Tools { name: "custom_jql", description: "Run custom JQL (JSON Query Language) queries. Useful for advanced analytics and custom data analysis beyond standard reports.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, script: { type: "string", description: "The JQL script to execute" } }, required: ["script"] } }, { name: "query_segmentation_report", description: "Segment events by properties. Useful for analyzing how different user segments behave and comparing event patterns across groups.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, event: { type: "string", description: "The event to segment" }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" }, on: { type: "string", description: "The property to segment by" }, where: { type: "string", description: "JSON string representing additional filters" }, unit: { type: "string", enum: ["hour", "day", "week", "month"], description: "The time unit for segmentation, defaults to day" } }, required: ["event", "from_date", "to_date", "on"] } }, { name: "query_insights_report", description: "Get saved insights reports. Useful for accessing pre-configured analytics reports and dashboards.", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "The Mixpanel project ID. Optional since it has a default." }, report_id: { type: "string", description: "The ID of the saved insights report to retrieve" }, from_date: { type: "string", description: "The date in yyyy-mm-dd format to begin querying from (inclusive)" }, to_date: { type: "string", description: "The date in yyyy-mm-dd format to query to (inclusive)" } }, required: ["report_id"] } } ] }; }); // Tool call handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { // Event Tools case "get_today_top_events": return await handleGetTodayTopEvents(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "get_top_events": return await handleGetTopEvents(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "aggregate_event_counts": return await handleAggregateEventCounts(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "aggregated_event_property_values": return await handleAggregatedEventPropertyValues(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); // User Profile Tools case "profile_event_activity": return await handleProfileEventActivity(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "query_profiles": return await handleQueryProfiles(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); // Analytics Tools case "query_retention_report": return await handleQueryRetentionReport(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "query_funnel_report": return await handleQueryFunnelReport(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "list_saved_funnels": return await handleListSavedFunnels(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "list_saved_cohorts": return await handleListSavedCohorts(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); // Advanced Tools case "custom_jql": return await handleCustomJQL(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "query_segmentation_report": return await handleQuerySegmentationReport(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); case "query_insights_report": return await handleQueryInsightsReport(args, { SERVICE_ACCOUNT_USER_NAME, SERVICE_ACCOUNT_PASSWORD, DEFAULT_PROJECT_ID, MIXPANEL_BASE_URL }); default: throw new Error(`Unknown tool: ${name}`); } }); } // Tool handlers async function handleGetTodayTopEvents(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, type = "general", limit = 10 } = args; try { // Create authorization header using base64 encoding of credentials const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); // Construct URL with query parameters const url = `${config.MIXPANEL_BASE_URL}/events/top?project_id=${project_id}&type=${type}${limit ? `&limit=${limit}` : ''}`; // Set up request options const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; // Make the API request const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error fetching Mixpanel events:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error fetching Mixpanel events: ${errorMessage}` } ], isError: true }; } } async function handleGetTopEvents(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, type = "general", limit = 10 } = args; try { // Create authorization header using base64 encoding of credentials const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); // Construct URL with query parameters const url = `${config.MIXPANEL_BASE_URL}/events/names?project_id=${project_id}&type=${type}${limit ? `&limit=${limit}` : ''}`; // Set up request options const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; // Make the API request const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error fetching Mixpanel events:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error fetching Mixpanel events: ${errorMessage}` } ], isError: true }; } } async function handleProfileEventActivity(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, workspace_id, distinct_ids, from_date, to_date } = args; try { // Create authorization header using base64 encoding of credentials const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); // Construct URL with query parameters let url = `${config.MIXPANEL_BASE_URL}/stream/query?project_id=${project_id}&distinct_ids=${encodeURIComponent(distinct_ids)}&from_date=${from_date}&to_date=${to_date}`; // Add optional workspace_id if provided if (workspace_id) { url += `&workspace_id=${workspace_id}`; } // Set up request options const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; // Make the API request const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`API request failed with status ${response.status}: ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error) { console.error('Error fetching profile event activity:', error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error fetching profile event activity: ${errorMessage}` } ], isError: true }; } } // Additional tool handlers for the new tools async function handleAggregateEventCounts(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, event, from_date, to_date, unit = "day" } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const url = `${config.MIXPANEL_BASE_URL}/events?project_id=${project_id}&event=${encodeURIComponent(event)}&from_date=${from_date}&to_date=${to_date}&unit=${unit}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error fetching event counts:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error fetching event counts: ${errorMessage}` } ], isError: true }; } } async function handleAggregatedEventPropertyValues(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, event, property, from_date, to_date, limit = 100 } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const url = `${config.MIXPANEL_BASE_URL}/events/properties/values?project_id=${project_id}&event=${encodeURIComponent(event)}&name=${encodeURIComponent(property)}&from_date=${from_date}&to_date=${to_date}&limit=${limit}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error fetching event property values:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error fetching event property values: ${errorMessage}` } ], isError: true }; } } async function handleQueryProfiles(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, where, select, limit = 100 } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); let url = `${config.MIXPANEL_BASE_URL}/engage?project_id=${project_id}`; if (where) { url += `&where=${encodeURIComponent(where)}`; } if (select) { url += `&select=${encodeURIComponent(select)}`; } if (limit) { url += `&limit=${limit}`; } const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error querying profiles:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error querying profiles: ${errorMessage}` } ], isError: true }; } } async function handleQueryRetentionReport(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, from_date, to_date, born_event, event, born_where, where, interval = "day", interval_count = 30 } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); let url = `${config.MIXPANEL_BASE_URL}/retention?project_id=${project_id}&from_date=${from_date}&to_date=${to_date}&born_event=${encodeURIComponent(born_event)}&interval=${interval}&interval_count=${interval_count}`; if (event) { url += `&event=${encodeURIComponent(event)}`; } if (born_where) { url += `&born_where=${encodeURIComponent(born_where)}`; } if (where) { url += `&where=${encodeURIComponent(where)}`; } const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error querying retention report:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error querying retention report: ${errorMessage}` } ], isError: true }; } } async function handleQueryFunnelReport(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, from_date, to_date, events, funnel_window = 14, interval = "day" } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const url = `${config.MIXPANEL_BASE_URL}/funnels?project_id=${project_id}&from_date=${from_date}&to_date=${to_date}&events=${encodeURIComponent(events)}&funnel_window=${funnel_window}&interval=${interval}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error querying funnel report:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error querying funnel report: ${errorMessage}` } ], isError: true }; } } async function handleListSavedFunnels(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const url = `${config.MIXPANEL_BASE_URL}/funnels/list?project_id=${project_id}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error listing saved funnels:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error listing saved funnels: ${errorMessage}` } ], isError: true }; } } async function handleListSavedCohorts(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const url = `${config.MIXPANEL_BASE_URL}/cohorts/list?project_id=${project_id}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error listing saved cohorts:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error listing saved cohorts: ${errorMessage}` } ], isError: true }; } } async function handleCustomJQL(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, script } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); const options = { method: 'POST', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}`, 'content-type': 'application/json' }, body: JSON.stringify({ project_id: project_id, script: script }) }; const response = await fetch(`${config.MIXPANEL_BASE_URL}/jql`, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error executing custom JQL:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error executing custom JQL: ${errorMessage}` } ], isError: true }; } } async function handleQuerySegmentationReport(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, event, from_date, to_date, on, where, unit = "day" } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); let url = `${config.MIXPANEL_BASE_URL}/segmentation?project_id=${project_id}&event=${encodeURIComponent(event)}&from_date=${from_date}&to_date=${to_date}&on=${encodeURIComponent(on)}&unit=${unit}`; if (where) { url += `&where=${encodeURIComponent(where)}`; } const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error querying segmentation report:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error querying segmentation report: ${errorMessage}` } ], isError: true }; } } async function handleQueryInsightsReport(args: any, config: any) { const { project_id = config.DEFAULT_PROJECT_ID, report_id, from_date, to_date } = args; try { const credentials = `${config.SERVICE_ACCOUNT_USER_NAME}:${config.SERVICE_ACCOUNT_PASSWORD}`; const encodedCredentials = Buffer.from(credentials).toString('base64'); let url = `${config.MIXPANEL_BASE_URL}/insights?project_id=${project_id}&report_id=${report_id}`; if (from_date) { url += `&from_date=${from_date}`; } if (to_date) { url += `&to_date=${to_date}`; } const options = { method: 'GET', headers: { 'accept': 'application/json', 'authorization': `Basic ${encodedCredentials}` } }; const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); } const data = await response.json(); return { content: [ { type: "text", text: JSON.stringify(data) } ] }; } catch (error: unknown) { console.error("Error querying insights report:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error querying insights report: ${errorMessage}` } ], isError: true }; } } // Start the server main().catch((error) => { console.error('Unhandled startup error', error); process.exit(1); });

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/mendeel/mixpanel-mcp'

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