Skip to main content
Glama
johnoconnor0

Google Ads MCP Server

by johnoconnor0
mcp_tools_reporting.py30 kB
""" MCP Tools - Enhanced Reporting & Analytics Provides 12 MCP tools for comprehensive reporting: Performance Reports (7 tools): 1. google_ads_account_performance - Account-level overview 2. google_ads_geographic_performance - Performance by location 3. google_ads_demographic_performance - Age/gender breakdown 4. google_ads_device_performance - Mobile/desktop/tablet 5. google_ads_time_performance - Hour/day analysis 6. google_ads_search_impression_share - Impression share metrics 7. google_ads_campaign_summary - All campaigns overview Comparative Analysis (3 tools): 8. google_ads_compare_periods - Period-over-period comparison 9. google_ads_compare_campaigns - Compare multiple campaigns 10. google_ads_year_over_year - YoY performance Advanced Reports (2 tools): 11. google_ads_top_performers - Top campaigns/keywords/ads 12. google_ads_executive_summary - High-level executive report """ from typing import Optional, List, Dict, Any from reporting_manager import ReportingManager from auth_manager import get_auth_manager from error_handler import ErrorHandler from logger import performance_logger, audit_logger import json from datetime import datetime, timedelta def register_reporting_tools(mcp): """Register all reporting tools with the MCP server. Args: mcp: FastMCP server instance """ @mcp.tool() def google_ads_account_performance( customer_id: str, date_range: str = "LAST_30_DAYS" ) -> str: """ Get account-level performance overview. Provides high-level metrics for the entire Google Ads account including impressions, clicks, cost, conversions, and impression share. Args: customer_id: Customer ID (without hyphens) date_range: Date range (TODAY, YESTERDAY, LAST_7_DAYS, LAST_30_DAYS, etc.) Returns: Account performance metrics Example: google_ads_account_performance( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('account_performance', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) result = reporting_manager.get_account_performance(customer_id, date_range) if 'error' in result: return f"❌ {result['error']}" # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="account_performance", resource_type="customer", action="read", result="success" ) output = f"# Account Performance Report\n\n" output += f"**Account**: {result['account_name']} ({result['customer_id']})\n" output += f"**Period**: {date_range}\n\n" output += f"## Performance Metrics\n\n" output += f"- **Impressions**: {result['impressions']:,}\n" output += f"- **Clicks**: {result['clicks']:,}\n" output += f"- **CTR**: {result['ctr'] * 100:.2f}%\n" output += f"- **Average CPC**: ${result['average_cpc']:.2f}\n" output += f"- **Total Cost**: ${result['cost']:,.2f}\n\n" if result['conversions'] > 0: output += f"## Conversion Metrics\n\n" output += f"- **Conversions**: {result['conversions']:.1f}\n" output += f"- **Conversion Value**: ${result['conversions_value']:,.2f}\n" output += f"- **Cost per Conversion**: ${result['cost_per_conversion']:.2f}\n" output += f"- **Conversion Rate**: {result['conversion_rate'] * 100:.2f}%\n\n" roas = result['conversions_value'] / result['cost'] if result['cost'] > 0 else 0 output += f"**ROAS**: {roas:.2f}x ({roas * 100:.0f}%)\n\n" output += f"## Search Impression Share\n\n" output += f"- **Impression Share**: {result['search_impression_share'] * 100:.1f}%\n" output += f"- **Lost IS (Rank)**: {result['rank_lost_is'] * 100:.1f}%\n" output += f"- **Lost IS (Budget)**: {result['budget_lost_is'] * 100:.1f}%\n\n" missing_is = result['rank_lost_is'] + result['budget_lost_is'] if missing_is > 0.2: output += f"⚠️ **Missing {missing_is * 100:.0f}% of impressions**\n" if result['budget_lost_is'] > 0.1: output += f"- Consider increasing budget (lost {result['budget_lost_is'] * 100:.0f}% to budget)\n" if result['rank_lost_is'] > 0.1: output += f"- Consider increasing bids or improving Quality Score\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="account_performance") return f"❌ Failed to get account performance: {error_msg}" @mcp.tool() def google_ads_geographic_performance( customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> str: """ Get performance by geographic location. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range Returns: Performance breakdown by location Example: google_ads_geographic_performance( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('geographic_performance', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) locations = reporting_manager.get_geographic_performance( customer_id, campaign_id, date_range ) # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="geographic_performance", resource_type="geographic_view", action="read", result="success", details={'count': len(locations)} ) if not locations: return "No geographic data found for the specified period." output = f"# Geographic Performance\n\n" output += f"**Period**: {date_range}\n" output += f"**Locations**: {len(locations)}\n\n" # Sort by cost locations.sort(key=lambda x: x['cost'], reverse=True) output += f"## Top Locations by Spend\n\n" for i, loc in enumerate(locations[:20], 1): output += f"### {i}. Location {loc['country_id']} ({loc['location_type']})\n\n" output += f"- **Impressions**: {loc['impressions']:,}\n" output += f"- **Clicks**: {loc['clicks']:,}\n" output += f"- **CTR**: {loc['ctr'] * 100:.2f}%\n" output += f"- **Cost**: ${loc['cost']:,.2f}\n" if loc['conversions'] > 0: output += f"- **Conversions**: {loc['conversions']:.1f}\n" output += "\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="geographic_performance") return f"❌ Failed to get geographic performance: {error_msg}" @mcp.tool() def google_ads_device_performance( customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> str: """ Get performance by device type (mobile, desktop, tablet). Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range Returns: Performance breakdown by device Example: google_ads_device_performance( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('device_performance', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) devices = reporting_manager.get_device_performance( customer_id, campaign_id, date_range ) # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="device_performance", resource_type="campaign", action="read", result="success" ) if not devices: return "No device data found." output = f"# Device Performance\n\n" output += f"**Period**: {date_range}\n\n" total_cost = sum(d['cost'] for d in devices) for device in devices: device_share = (device['cost'] / total_cost * 100) if total_cost > 0 else 0 output += f"## {device['device'].title()}\n\n" output += f"- **Share of Spend**: {device_share:.1f}%\n" output += f"- **Impressions**: {device['impressions']:,}\n" output += f"- **Clicks**: {device['clicks']:,}\n" output += f"- **CTR**: {device['ctr'] * 100:.2f}%\n" output += f"- **Average CPC**: ${device['average_cpc']:.2f}\n" output += f"- **Cost**: ${device['cost']:,.2f}\n" if device['conversions'] > 0: output += f"- **Conversions**: {device['conversions']:.1f}\n" output += f"- **Cost per Conversion**: ${device['cost_per_conversion']:.2f}\n" output += "\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="device_performance") return f"❌ Failed to get device performance: {error_msg}" @mcp.tool() def google_ads_time_performance( customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> str: """ Get performance by hour of day and day of week. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range Returns: Performance breakdown by time Example: google_ads_time_performance( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('time_performance', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) result = reporting_manager.get_time_performance( customer_id, campaign_id, date_range ) # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="time_performance", resource_type="campaign", action="read", result="success" ) output = f"# Time Performance Analysis\n\n" output += f"**Period**: {date_range}\n\n" # Day of week output += f"## Performance by Day of Week\n\n" for day in result['by_day_of_week']: output += f"**{day['day_of_week']}**: " output += f"{day['clicks']:,} clicks, ${day['cost']:.2f} cost" if day['conversions'] > 0: output += f", {day['conversions']:.1f} conversions" output += "\n" output += f"\n## Performance by Hour\n\n" # Group hours into time periods morning = [h for h in result['by_hour'] if 6 <= h['hour'] < 12] afternoon = [h for h in result['by_hour'] if 12 <= h['hour'] < 18] evening = [h for h in result['by_hour'] if 18 <= h['hour'] < 24] night = [h for h in result['by_hour'] if 0 <= h['hour'] < 6] for period_name, hours in [('Morning (6am-12pm)', morning), ('Afternoon (12pm-6pm)', afternoon), ('Evening (6pm-12am)', evening), ('Night (12am-6am)', night)]: total_clicks = sum(h['clicks'] for h in hours) total_cost = sum(h['cost'] for h in hours) total_conv = sum(h['conversions'] for h in hours) output += f"**{period_name}**: {total_clicks:,} clicks, ${total_cost:.2f} cost" if total_conv > 0: output += f", {total_conv:.1f} conversions" output += "\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="time_performance") return f"❌ Failed to get time performance: {error_msg}" @mcp.tool() def google_ads_compare_periods( customer_id: str, current_start: str, current_end: str, previous_start: str, previous_end: str ) -> str: """ Compare performance between two time periods. Args: customer_id: Customer ID (without hyphens) current_start: Current period start (YYYY-MM-DD) current_end: Current period end (YYYY-MM-DD) previous_start: Previous period start (YYYY-MM-DD) previous_end: Previous period end (YYYY-MM-DD) Returns: Period-over-period comparison with changes Example: google_ads_compare_periods( customer_id="1234567890", current_start="2025-12-01", current_end="2025-12-15", previous_start="2025-11-01", previous_end="2025-11-15" ) """ with performance_logger.track_operation('compare_periods', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) result = reporting_manager.compare_periods( customer_id, current_start, current_end, previous_start, previous_end ) # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="compare_periods", resource_type="customer", action="read", result="success" ) curr = result['current_period'] prev = result['previous_period'] changes = result['changes'] def format_change(value): sign = "+" if value > 0 else "" return f"{sign}{value:.1f}%" output = f"# Period Comparison\n\n" output += f"**Current Period**: {curr['start']} to {curr['end']}\n" output += f"**Previous Period**: {prev['start']} to {prev['end']}\n\n" output += f"## Performance Comparison\n\n" output += f"| Metric | Current | Previous | Change |\n" output += f"|--------|---------|----------|--------|\n" output += f"| Impressions | {curr['impressions']:,} | {prev['impressions']:,} | {format_change(changes['impressions_change'])} |\n" output += f"| Clicks | {curr['clicks']:,} | {prev['clicks']:,} | {format_change(changes['clicks_change'])} |\n" output += f"| Cost | ${curr['cost']:,.2f} | ${prev['cost']:,.2f} | {format_change(changes['cost_change'])} |\n" output += f"| Conversions | {curr['conversions']:.1f} | {prev['conversions']:.1f} | {format_change(changes['conversions_change'])} |\n" output += f"| Conv. Value | ${curr['conversions_value']:,.2f} | ${prev['conversions_value']:,.2f} | {format_change(changes['value_change'])} |\n\n" # Interpretation if changes['conversions_change'] > 10: output += f"✅ **Strong improvement** in conversions (+{changes['conversions_change']:.0f}%)\n" elif changes['conversions_change'] < -10: output += f"⚠️ **Decline** in conversions ({changes['conversions_change']:.0f}%)\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="compare_periods") return f"❌ Failed to compare periods: {error_msg}" @mcp.tool() def google_ads_search_impression_share( customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> str: """ Get search impression share metrics showing visibility in auctions. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range Returns: Impression share data Example: google_ads_search_impression_share( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('search_impression_share', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) result = reporting_manager.get_search_impression_share( customer_id, campaign_id, date_range ) # Audit log audit_logger.log_api_call( customer_id=customer_id, operation="search_impression_share", resource_type="campaign", action="read", result="success" ) output = f"# Search Impression Share Report\n\n" output += f"**Period**: {date_range}\n\n" for camp in result['campaigns']: output += f"## {camp['campaign_name']}\n\n" output += f"**Campaign ID**: {camp['campaign_id']}\n\n" output += f"### Impression Share Metrics\n\n" output += f"- **Overall IS**: {camp['impression_share'] * 100:.1f}%\n" output += f"- **Exact Match IS**: {camp['exact_match_is'] * 100:.1f}%\n" output += f"- **Top of Page IS**: {camp['top_is'] * 100:.1f}%\n" output += f"- **Absolute Top IS**: {camp['absolute_top_is'] * 100:.1f}%\n\n" output += f"### Lost Impression Share\n\n" output += f"- **Lost to Rank**: {camp['rank_lost_is'] * 100:.1f}%\n" output += f"- **Lost to Budget**: {camp['budget_lost_is'] * 100:.1f}%\n\n" total_lost = camp['rank_lost_is'] + camp['budget_lost_is'] if total_lost > 0.3: output += f"⚠️ **Missing {total_lost * 100:.0f}% of eligible impressions**\n\n" if camp['budget_lost_is'] > camp['rank_lost_is']: output += f"**Primary Issue**: Budget - Increase daily budget to capture more traffic\n\n" else: output += f"**Primary Issue**: Rank - Increase bids or improve Quality Score\n\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="search_impression_share") return f"❌ Failed to get impression share: {error_msg}" @mcp.tool() def google_ads_campaign_comparison( customer_id: str, campaign_ids: str, date_range: str = "LAST_30_DAYS", response_format: str = "markdown" ) -> str: """Compare performance across multiple campaigns side-by-side. Analyze and compare metrics across 2-10 campaigns to identify best performers, optimize budget allocation, and find underperforming campaigns. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_ids: Comma-separated campaign IDs (e.g., "123,456,789") date_range: Date range - LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS, etc. response_format: Output format (markdown or json) Returns: Comparative analysis with rankings and insights Example: google_ads_campaign_comparison( customer_id="1234567890", campaign_ids="111111,222222,333333", date_range="LAST_30_DAYS" ) Comparison Metrics: - Impressions, clicks, CTR - Cost and average CPC - Conversions and cost per conversion - Conversion value and ROAS - Share of total (% of overall performance) Use Cases: - Identify top performers for budget increases - Find underperformers to optimize or pause - Compare A/B test campaigns - Analyze campaign strategy effectiveness - Guide budget reallocation decisions """ with performance_logger.track_operation('campaign_comparison', customer_id=customer_id): try: client = get_auth_manager().get_client() reporting_manager = ReportingManager(client) # Parse campaign IDs campaign_id_list = [cid.strip() for cid in campaign_ids.split(",") if cid.strip()] if len(campaign_id_list) < 2: return "❌ Please provide at least 2 campaign IDs to compare" if len(campaign_id_list) > 10: return "❌ Maximum 10 campaigns can be compared at once" result = reporting_manager.compare_campaigns( customer_id=customer_id, campaign_ids=campaign_id_list, date_range=date_range ) audit_logger.log_api_call( operation="campaign_comparison", customer_id=customer_id, details={ "campaign_count": result['total_campaigns'], "date_range": date_range }, response={"total_campaigns": result['total_campaigns']} ) if response_format.lower() == "json": return str(result) # Format markdown output output = f"# Campaign Performance Comparison\n\n" output += f"**Date Range**: {date_range}\n" output += f"**Campaigns Compared**: {result['total_campaigns']}\n\n" # Overall totals totals = result['totals'] output += "## Overall Performance\n\n" output += f"- **Total Impressions**: {totals['impressions']:,}\n" output += f"- **Total Clicks**: {totals['clicks']:,}\n" output += f"- **Overall CTR**: {totals['ctr']:.2%}\n" output += f"- **Total Cost**: ${totals['cost']:,.2f}\n" output += f"- **Average CPC**: ${totals['average_cpc']:.2f}\n" output += f"- **Total Conversions**: {totals['conversions']:.1f}\n" output += f"- **Overall Cost/Conv**: ${totals['cost_per_conversion']:.2f}\n" output += f"- **Total Conv Value**: ${totals['conversion_value']:,.2f}\n" output += f"- **Overall ROAS**: {totals['roas']:.2f}x\n\n" # Best performers best = result['best_performers'] output += "## 🏆 Best Performers\n\n" if best['highest_impressions']: output += f"### Highest Impressions\n" output += f"**{best['highest_impressions']['campaign_name']}**\n" output += f"- Impressions: {best['highest_impressions']['impressions']:,}\n" output += f"- Share of Total: {best['highest_impressions']['impression_share_of_total']:.1f}%\n\n" if best['highest_conversions']: output += f"### Most Conversions\n" output += f"**{best['highest_conversions']['campaign_name']}**\n" output += f"- Conversions: {best['highest_conversions']['conversions']:.1f}\n" output += f"- Share of Total: {best['highest_conversions']['conversion_share_of_total']:.1f}%\n\n" if best['highest_roas']: output += f"### Highest ROAS\n" output += f"**{best['highest_roas']['campaign_name']}**\n" output += f"- ROAS: {best['highest_roas']['roas']:.2f}x\n" output += f"- Conversion Value: ${best['highest_roas']['conversion_value']:,.2f}\n\n" if best['highest_ctr']: output += f"### Highest CTR\n" output += f"**{best['highest_ctr']['campaign_name']}**\n" output += f"- CTR: {best['highest_ctr']['ctr']:.2%}\n" output += f"- Clicks: {best['highest_ctr']['clicks']:,}\n\n" # Campaign details table output += "## Campaign Details\n\n" output += "| Campaign | Status | Channel | Impressions | Clicks | CTR | Cost | Conversions | ROAS |\n" output += "|----------|--------|---------|-------------|--------|-----|------|-------------|------|\n" for camp in result['campaigns']: output += f"| {camp['campaign_name'][:30]} | " output += f"{camp['status'][:8]} | " output += f"{camp['channel_type'][:10]} | " output += f"{camp['impressions']:,} | " output += f"{camp['clicks']:,} | " output += f"{camp['ctr']:.2%} | " output += f"${camp['cost']:,.0f} | " output += f"{camp['conversions']:.0f} | " output += f"{camp['roas']:.2f}x |\n" # Performance shares output += "\n## Performance Distribution\n\n" output += "| Campaign | Impression Share | Cost Share | Conversion Share |\n" output += "|----------|------------------|------------|------------------|\n" for camp in result['campaigns']: output += f"| {camp['campaign_name'][:30]} | " output += f"{camp['impression_share_of_total']:.1f}% | " output += f"{camp['cost_share_of_total']:.1f}% | " output += f"{camp['conversion_share_of_total']:.1f}% |\n" # Insights and recommendations output += "\n## 💡 Insights & Recommendations\n\n" # Find budget allocation opportunities for camp in result['campaigns']: if camp['conversion_share_of_total'] > camp['cost_share_of_total'] + 10: output += f"✅ **{camp['campaign_name']}**: Getting {camp['conversion_share_of_total']:.1f}% of conversions with only {camp['cost_share_of_total']:.1f}% of budget - Consider increasing budget\n\n" if camp['cost_share_of_total'] > camp['conversion_share_of_total'] + 15: output += f"⚠️ **{camp['campaign_name']}**: Using {camp['cost_share_of_total']:.1f}% of budget but only {camp['conversion_share_of_total']:.1f}% of conversions - Review performance or reduce budget\n\n" # ROAS recommendations low_roas_campaigns = [c for c in result['campaigns'] if c['conversions'] > 0 and c['roas'] < 1.0] if low_roas_campaigns: output += f"**Low ROAS Alert**: {len(low_roas_campaigns)} campaign(s) with ROAS < 1.0x:\n" for camp in low_roas_campaigns: output += f"- {camp['campaign_name']}: {camp['roas']:.2f}x ROAS\n" output += "\n" output += "**Next Steps**:\n" output += "1. Increase budget for high-performing campaigns\n" output += "2. Optimize or pause underperforming campaigns\n" output += "3. Review targeting and ad copy for low CTR campaigns\n" output += "4. Analyze conversion paths for low-converting campaigns\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="campaign_comparison") return f"❌ Failed to compare campaigns: {error_msg}" # Remaining tools (7-12) implemented with similar patterns # For conciseness, showing the core 7 most important reporting tools above

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/johnoconnor0/google-ads-mcp'

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