@feedmob/applovin-reporting
Version:
MCP server for Applovin API
126 lines (125 loc) • 5.27 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fetch from "node-fetch";
import dotenv from "dotenv";
dotenv.config();
const server = new McpServer({
name: "AppLovin Reporting MCP Server",
version: "0.0.1"
});
const APPLOVIN_API_BASE_URL = "https://r.applovin.com/report";
const APPLOVIN_API_KEY = process.env.APPLOVIN_API_KEY || '';
if (!APPLOVIN_API_KEY) {
console.error("Missing AppLovin API credentials. Please set APPLOVIN_API_KEY environment variable.");
process.exit(1);
}
/**
* Make request to AppLovin Reporting API
*/
async function fetchAppLovinReport(params) {
// Construct URL with query parameters
const queryParams = new URLSearchParams({
api_key: APPLOVIN_API_KEY,
...params
});
const reportUrl = `${APPLOVIN_API_BASE_URL}?${queryParams.toString()}`;
try {
console.error(`Requesting AppLovin report with params: ${JSON.stringify(params)}`);
const response = await fetch(reportUrl, {
method: 'GET',
headers: {
'Accept': 'application/json; */*'
}
});
if (!response.ok) {
const errorBody = await response.text();
console.error(`AppLovin API Error Response: ${response.status} ${response.statusText} - ${errorBody}`);
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
else {
// If response is CSV or other format
return await response.text();
}
}
catch (error) {
console.error(`Error fetching AppLovin report:`, error);
throw new Error(`Failed to get AppLovin report: ${error.message}`);
}
}
// Tool: Get Advertiser Report
server.tool("get_advertiser_report", "Get campaign spending data from AppLovin Reporting API for advertisers.", {
start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Start date must be in YYYY-MM-DD format").describe("Start date for the report (YYYY-MM-DD)"),
end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "End date must be in YYYY-MM-DD format").describe("End date for the report (YYYY-MM-DD)"),
columns: z.string().optional().describe("Comma-separated list of columns to include (e.g., 'day,campaign,impressions,clicks,conversions,cost')"),
format: z.enum(["json", "csv"]).default("json").describe("Format of the report data"),
filter_campaign: z.string().optional().describe("Filter results by campaign name"),
filter_country: z.string().optional().describe("Filter results by country (e.g., 'US,JP')"),
filter_platform: z.string().optional().describe("Filter results by platform (e.g., 'android,ios')"),
sort_column: z.string().optional().describe("Column to sort by (e.g., 'cost')"),
sort_order: z.enum(["ASC", "DESC"]).optional().describe("Sort order (ASC or DESC)")
}, async ({ start_date, end_date, columns, format, filter_campaign, filter_country, filter_platform, sort_column, sort_order }) => {
try {
// Validate date range logic
if (new Date(start_date) > new Date(end_date)) {
throw new Error("Start date cannot be after end date.");
}
// Default columns if not specified
const reportColumns = columns || "day,campaign,impressions,clicks,ctr,conversions,conversion_rate,cost";
// Build parameters
const params = {
start: start_date,
end: end_date,
columns: reportColumns,
format: format,
report_type: "advertiser" // Specify advertiser report type
};
// Add optional filters if provided
if (filter_campaign)
params['filter_campaign'] = filter_campaign;
if (filter_country)
params['filter_country'] = filter_country;
if (filter_platform)
params['filter_platform'] = filter_platform;
// Add sorting if provided
if (sort_column && sort_order) {
params[`sort_${sort_column}`] = sort_order;
}
const data = await fetchAppLovinReport(params);
return {
content: [
{
type: "text",
text: typeof data === 'string' ? data : JSON.stringify(data, null, 2)
}
]
};
}
catch (error) {
const errorMessage = `Error getting AppLovin advertiser report: ${error.message}`;
return {
content: [
{
type: "text",
text: errorMessage
}
],
isError: true
};
}
});
// Start server
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("AppLovin Reporting MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});