From d8bd1803140d7bcaf08b10b7da3da9cfbd5a4861 Mon Sep 17 00:00:00 2001 From: Joel Horwitz Date: Fri, 5 Jun 2026 23:41:30 -0700 Subject: [PATCH] feat: add attribution tools v1.3.0 (get_top_creatives, analyze_customer_journeys, compare_attribution_models, get_recent_conversions, get_top_landing_pages) Exposes five read-only attribution analysis tools that let external Claude Code users query Synter's attribution data directly from their agentic workflows. All five tools route through the new POST /api/v1/attribution backend endpoint using the existing API key auth pattern. Tools added: - get_top_creatives: cross-platform creative leaderboard by spend/conversions/clicks/CTR/ROAS - analyze_customer_journeys: multi-touch journey analysis with sankey flow + channel overlap - compare_attribution_models: first/last/linear/time-decay/position/Markov/Shapley comparison - get_recent_conversions: live per-account conversion feed with attributed source - get_top_landing_pages: Synter LP + pixel-tracked site page performance leaderboard Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 2 +- src/index.ts | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2683116..82d08e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@synterai/mcp-server", - "version": "1.2.0", + "version": "1.3.0", "mcpName": "io.github.synter-media-ai/synter-ads", "description": "Model Context Protocol (MCP) server for AI agents to manage ad campaigns across Google, Meta, LinkedIn, Microsoft, Reddit, TikTok, and more. Create campaigns, adjust budgets, generate creatives, and pull performance data through natural conversation.", "author": "Synter Media ", diff --git a/src/index.ts b/src/index.ts index 8d97aa8..c10b383 100644 --- a/src/index.ts +++ b/src/index.ts @@ -833,6 +833,138 @@ const tools: Tool[] = [ required: ["script_name"], }, }, + + // ───────────────────────────────────────────────────────────────────────── + // ATTRIBUTION & PERFORMANCE ANALYSIS + // ───────────────────────────────────────────────────────────────────────── + { + name: "get_top_creatives", + description: + "Get a cross-platform creative leaderboard: top ads by spend, conversions, clicks, or CTR " + + "with thumbnail image URLs, headlines, CPA, and platform-reported ROAS. " + + "The result's granularity field is the native hierarchy level actually returned " + + "(ad, ad_group, or campaign). Use when you need to know which creatives perform best.", + annotations: { readOnlyHint: true }, + inputSchema: { + type: "object" as const, + properties: { + days: { + type: "number", + description: "Lookback window in days. Default 30.", + }, + platform: { + type: "string", + description: "Filter to one platform (e.g. META, GOOGLE, LINKEDIN, REDDIT, TIKTOK). Optional.", + }, + limit: { + type: "number", + description: "Number of creatives to return. Default 10.", + }, + level: { + type: "string", + enum: ["ad", "ad_group", "campaign"], + description: "Hierarchy level. Omit for auto (finest available).", + }, + sort_by: { + type: "string", + enum: ["spend", "conversions", "clicks", "ctr", "roas"], + description: "Ranking metric. Default spend.", + }, + }, + }, + }, + { + name: "analyze_customer_journeys", + description: + "Analyze multi-touch customer journeys: conversion paths across channels, sankey flow data, " + + "channel overlap (solo vs assisted), top paths, and average touches to convert. " + + "Use when you need to understand how customers find the advertiser or which channels assist conversions.", + annotations: { readOnlyHint: true }, + inputSchema: { + type: "object" as const, + properties: { + days: { + type: "number", + description: "Lookback window in days. Default 90.", + }, + }, + }, + }, + { + name: "compare_attribution_models", + description: + "Run multiple attribution models over the same conversion-path data and compare per-channel credit. " + + "Models include first_touch, last_touch, linear, time_decay, position_based, markov_chain, and shapley_value. " + + "Shows what last-click hides. Use to answer which channels really drive conversions.", + annotations: { readOnlyHint: true }, + inputSchema: { + type: "object" as const, + properties: { + days: { + type: "number", + description: "Lookback window in days. Default 90.", + }, + models: { + type: "array", + items: { + type: "string", + enum: [ + "first_touch", + "last_touch", + "linear", + "time_decay", + "position_based", + "markov_chain", + "shapley_value", + ], + }, + description: "Models to compare. Default is all seven.", + }, + }, + }, + }, + { + name: "get_recent_conversions", + description: + "Get the most recent tracked conversions for the authenticated account: timestamp, action type, value, " + + "and attributed source. Source is deterministic when a platform click ID was captured; " + + "otherwise falls back to utm_source. Use to get a live pulse on recent results.", + annotations: { readOnlyHint: true }, + inputSchema: { + type: "object" as const, + properties: { + days: { + type: "number", + description: "Lookback window in days. Default 7, max 90.", + }, + limit: { + type: "number", + description: "Max conversions to return. Default 20, max 50.", + }, + }, + }, + }, + { + name: "get_top_landing_pages", + description: + "Get top landing pages: Synter-built pages (views, CTA clicks, lead submissions) plus " + + "the customer's own site pages tracked by the Synter pixel (views, conversions, conversion rate). " + + "Use when you need to know which landing pages perform best or where conversions happen.", + annotations: { readOnlyHint: true }, + inputSchema: { + type: "object" as const, + properties: { + days: { + type: "number", + description: "Lookback window in days. Default 30, max 365.", + }, + limit: { + type: "number", + description: "Max pages per source. Default 10, max 25.", + }, + }, + }, + }, ]; // ============================================================================= @@ -868,6 +1000,35 @@ async function callSynterAPI( return data; } +async function callAttributionAPI( + tool: string, + args: Record +): Promise> { + if (!SYNTER_API_KEY) { + throw new Error( + "SYNTER_API_KEY not set. Get your API key at https://syntermedia.ai/developer" + ); + } + + const response = await fetch(`${SYNTER_API_URL}/api/v1/attribution`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${SYNTER_API_KEY}`, + }, + body: JSON.stringify({ tool, args }), + }); + + const data = await response.json() as Record; + + if (!response.ok) { + const errorMsg = (data.error as string) || (data.message as string) || `Attribution API error: ${response.status}`; + throw new Error(errorMsg); + } + + return data; +} + async function stageAudienceArtifact( body: string, scriptName: string, @@ -999,6 +1160,18 @@ async function handleTool( }); } + // Attribution tools — read-only, routed to /api/v1/attribution. + const ATTRIBUTION_TOOLS = new Set([ + "get_top_creatives", + "analyze_customer_journeys", + "compare_attribution_models", + "get_recent_conversions", + "get_top_landing_pages", + ]); + if (ATTRIBUTION_TOOLS.has(name)) { + return callAttributionAPI(name, args); + } + // Map tool names to script names and handle parameters const toolMappings: Record string[] }> = { // Campaign management @@ -1277,7 +1450,7 @@ async function main() { const server = new Server( { name: "synter-mcp", - version: "1.2.0", + version: "1.3.0", }, { capabilities: {