Skip to main content
Glama
johnoconnor0

Google Ads MCP Server

by johnoconnor0
mcp_tools_shopping_pmax.py37.4 kB
""" MCP Tools - Shopping & Performance Max Campaigns Tools for Google Shopping campaigns and Performance Max campaigns. Shopping Tools: 1. google_ads_create_shopping_campaign - Create shopping campaigns 2. google_ads_create_product_group - Product partition management 3. google_ads_shopping_feed_status - Check Merchant Center feed 4. google_ads_shopping_performance - Shopping-specific metrics Performance Max Tools: 5. google_ads_create_performance_max_campaign - Create PMax campaigns 6. google_ads_create_asset_group - Asset group management 7. google_ads_upload_pmax_assets - Upload text assets 8. google_ads_set_audience_signals - Configure audience signals 9. google_ads_pmax_insights - Performance Max insights """ from typing import List, Optional from shopping_pmax_manager import ( ShoppingPMaxManager, ShoppingCampaignConfig, ShoppingCampaignPriority, PerformanceMaxCampaignConfig ) from auth_manager import get_auth_manager from error_handler import ErrorHandler from logger import get_logger, get_performance_logger, get_audit_logger import json logger = get_logger(__name__) performance_logger = get_performance_logger() audit_logger = get_audit_logger() def register_shopping_pmax_tools(mcp): """Register all Shopping and Performance Max MCP tools.""" @mcp.tool() def google_ads_create_shopping_campaign( customer_id: str, campaign_name: str, merchant_center_id: str, budget_amount: float, priority: str = "LOW", target_roas: Optional[float] = None, enable_local: bool = False ) -> str: """Create a Google Shopping campaign. Shopping campaigns promote products from your Google Merchant Center account. They show product ads with images, prices, and store names on Google Search and Google Shopping. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_name: Name for the shopping campaign merchant_center_id: Your Merchant Center account ID budget_amount: Daily budget in currency units (e.g., 50.00 for $50/day) priority: Campaign priority (LOW, MEDIUM, HIGH) - affects bidding when multiple campaigns target the same product target_roas: Optional target return on ad spend (e.g., 3.0 for 300% ROAS) enable_local: Enable local inventory ads (requires local product feed) Returns: Shopping campaign creation result Example: google_ads_create_shopping_campaign( customer_id="1234567890", campaign_name="Holiday Shopping Campaign", merchant_center_id="123456789", budget_amount=100.00, priority="HIGH", target_roas=3.5 ) """ with performance_logger.track_operation('create_shopping_campaign', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) # Validate priority try: priority_enum = ShoppingCampaignPriority[priority.upper()] except KeyError: return f"❌ Invalid priority. Must be: LOW, MEDIUM, or HIGH" config = ShoppingCampaignConfig( name=campaign_name, merchant_center_id=merchant_center_id, budget_amount=budget_amount, priority=priority_enum, target_roas=target_roas, enable_local=enable_local ) result = shopping_manager.create_shopping_campaign(customer_id, config) audit_logger.log_api_call( customer_id=customer_id, operation='create_shopping_campaign', campaign_id=result['campaign_id'], status='success' ) output = f"# 🛍️ Shopping Campaign Created\n\n" output += f"**Campaign Name**: {result['campaign_name']}\n" output += f"**Campaign ID**: {result['campaign_id']}\n" output += f"**Merchant Center ID**: {result['merchant_center_id']}\n" output += f"**Priority**: {result['priority']}\n" output += f"**Daily Budget**: ${result['budget']:.2f}\n" output += f"**Status**: PAUSED (ready for products)\n\n" output += "## Next Steps\n\n" output += "1. ✅ Shopping campaign created successfully\n" output += "2. 📦 Ensure products are approved in Merchant Center\n" output += "3. 🎯 Create product groups to organize bidding\n" output += "4. 💰 Set bids for product groups\n" output += "5. ▶️ Enable campaign when ready\n\n" if enable_local: output += "📍 **Local inventory ads enabled** - products from your local stores will show\n\n" if target_roas: output += f"🎯 **Target ROAS set to {target_roas:.1f}x** - Google will optimize for this return\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="create_shopping_campaign") return f"❌ Failed to create shopping campaign: {error_msg}" @mcp.tool() def google_ads_create_product_group( customer_id: str, ad_group_id: str, product_condition: Optional[str] = None, product_type: Optional[str] = None, is_subdivision: bool = False ) -> str: """Create a product group (product partition) in a shopping ad group. Product groups organize your products and allow different bids for different product segments. You can partition by: brand, category, condition, type, etc. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) ad_group_id: Shopping ad group ID product_condition: Filter by condition (NEW, USED, REFURBISHED) product_type: Filter by product type from your feed is_subdivision: True to create subdivision (for further partitioning), False to create bidding unit Returns: Product group creation result Example: google_ads_create_product_group( customer_id="1234567890", ad_group_id="12345678", product_condition="NEW", is_subdivision=False ) """ with performance_logger.track_operation('create_product_group', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) result = shopping_manager.create_product_group( customer_id=customer_id, ad_group_id=ad_group_id, product_condition=product_condition, product_type=product_type, is_subdivision=is_subdivision ) audit_logger.log_api_call( customer_id=customer_id, operation='create_product_group', ad_group_id=ad_group_id, status='success' ) output = f"# 📦 Product Group Created\n\n" output += f"**Ad Group ID**: {result['ad_group_id']}\n" output += f"**Criterion ID**: {result['criterion_id']}\n" output += f"**Type**: {result['type'].upper()}\n\n" if product_condition: output += f"**Condition Filter**: {product_condition}\n" if product_type: output += f"**Product Type**: {product_type}\n" output += "\n" if is_subdivision: output += "🔄 **Subdivision created** - you can now create child partitions\n\n" output += "**Next Steps**:\n" output += "- Create child product groups under this subdivision\n" output += "- Further segment by brand, category, or other dimensions\n" else: output += "💰 **Bidding unit created** - set a CPC bid for this product group\n\n" output += "**Next Steps**:\n" output += "- Adjust the CPC bid based on product profitability\n" output += "- Monitor performance and optimize bids\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="create_product_group") return f"❌ Failed to create product group: {error_msg}" @mcp.tool() def google_ads_shopping_feed_status( customer_id: str, merchant_center_id: str ) -> str: """Check the status of your Google Merchant Center feed connection. Verifies that your Merchant Center account is properly linked to Google Ads and that products can flow from Merchant Center to your shopping campaigns. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) merchant_center_id: Your Merchant Center account ID Returns: Merchant Center feed status Example: google_ads_shopping_feed_status( customer_id="1234567890", merchant_center_id="123456789" ) """ with performance_logger.track_operation('shopping_feed_status', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) result = shopping_manager.get_shopping_feed_status( customer_id=customer_id, merchant_center_id=merchant_center_id ) audit_logger.log_api_call( customer_id=customer_id, operation='shopping_feed_status', status='success' ) output = f"# 📊 Merchant Center Feed Status\n\n" output += f"**Merchant Center ID**: {result['merchant_center_id']}\n" output += f"**Status**: {result['status']}\n\n" if result['status'] == 'NOT_LINKED': output += "❌ **Merchant Center account is not linked**\n\n" output += "**To link your Merchant Center account**:\n" output += "1. Go to Google Ads → Tools & Settings → Linked accounts\n" output += "2. Find Google Merchant Center and click 'Link'\n" output += "3. Approve the link request in Merchant Center\n" elif result['status'] == 'ENABLED': output += "✅ **Merchant Center is linked and active**\n\n" output += f"**Link ID**: {result.get('link_id', 'N/A')}\n\n" output += "Your products are ready to use in shopping campaigns!\n" else: output += f"⚠️ **Status**: {result['message']}\n\n" output += "Check your Merchant Center account for issues.\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="shopping_feed_status") return f"❌ Failed to get feed status: {error_msg}" @mcp.tool() def google_ads_shopping_performance( customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> str: """Get performance metrics for Shopping campaigns. Provides detailed performance data including ROAS (return on ad spend), which is critical for shopping campaign optimization. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_id: Optional specific shopping campaign ID date_range: Date range (LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS) Returns: Shopping campaign performance metrics Example: google_ads_shopping_performance( customer_id="1234567890", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('shopping_performance', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) result = shopping_manager.get_shopping_performance( customer_id=customer_id, campaign_id=campaign_id, date_range=date_range ) audit_logger.log_api_call( customer_id=customer_id, operation='shopping_performance', status='success' ) output = f"# 🛍️ Shopping Campaign Performance\n\n" output += f"**Date Range**: {date_range}\n" output += f"**Total Campaigns**: {result['total_campaigns']}\n\n" if result['total_campaigns'] == 0: output += "❌ No shopping campaigns found or no data for this period.\n" return output output += "## Campaign Performance\n\n" output += "| Campaign | Impressions | Clicks | Cost | Conv. Value | ROAS |\n" output += "|----------|-------------|--------|------|-------------|------|\n" for campaign in result['campaigns'][:10]: # Top 10 output += f"| {campaign['campaign_name'][:30]} | " output += f"{campaign['impressions']:,} | " output += f"{campaign['clicks']:,} | " output += f"${campaign['cost']:,.2f} | " output += f"${campaign['conversion_value']:,.2f} | " output += f"{campaign['roas']:.2f}x |\n" output += "\n## Key Metrics Summary\n\n" total_cost = sum(c['cost'] for c in result['campaigns']) total_value = sum(c['conversion_value'] for c in result['campaigns']) overall_roas = (total_value / total_cost) if total_cost > 0 else 0 output += f"- **Total Spend**: ${total_cost:,.2f}\n" output += f"- **Total Conversion Value**: ${total_value:,.2f}\n" output += f"- **Overall ROAS**: {overall_roas:.2f}x\n\n" # ROAS insights if overall_roas > 4.0: output += "✅ **Excellent ROAS!** Your shopping campaigns are highly profitable.\n" elif overall_roas > 2.0: output += "💚 **Good ROAS.** Campaigns are profitable with room for scaling.\n" elif overall_roas > 1.0: output += "⚠️ **Marginal ROAS.** Consider optimizing product groups and bids.\n" else: output += "🚨 **Low ROAS.** Review product pricing, margins, and targeting.\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="shopping_performance") return f"❌ Failed to get shopping performance: {error_msg}" @mcp.tool() def google_ads_create_performance_max_campaign( customer_id: str, campaign_name: str, budget_amount: float, conversion_goals_json: str, target_roas: Optional[float] = None, target_cpa: Optional[float] = None ) -> str: """Create a Performance Max campaign. Performance Max uses Google's AI to optimize across all Google channels: Search, Display, YouTube, Gmail, Discover, and Maps. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_name: Name for the Performance Max campaign budget_amount: Daily budget in currency units conversion_goals_json: JSON array of conversion action names target_roas: Optional target return on ad spend (e.g., 3.0 for 300%) target_cpa: Optional target cost per acquisition (if not using ROAS) Example: google_ads_create_performance_max_campaign( customer_id="1234567890", campaign_name="PMax - All Products", budget_amount=150.00, conversion_goals_json='["Purchase", "Add to Cart"]', target_roas=4.0 ) """ with performance_logger.track_operation('create_performance_max', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) # Parse conversion goals try: conversion_goals = json.loads(conversion_goals_json) except json.JSONDecodeError: return "❌ Invalid JSON format for conversion_goals_json" config = PerformanceMaxCampaignConfig( name=campaign_name, budget_amount=budget_amount, conversion_goals=conversion_goals, target_roas=target_roas, target_cpa=target_cpa ) result = shopping_manager.create_performance_max_campaign(customer_id, config) audit_logger.log_api_call( customer_id=customer_id, operation='create_performance_max_campaign', campaign_id=result['campaign_id'], status='success' ) output = f"# 🚀 Performance Max Campaign Created\n\n" output += f"**Campaign Name**: {result['campaign_name']}\n" output += f"**Campaign ID**: {result['campaign_id']}\n" output += f"**Daily Budget**: ${result['budget']:.2f}\n" output += f"**Bidding Strategy**: {result['bidding_strategy']}\n" output += f"**Status**: PAUSED\n\n" if target_roas: output += f"🎯 **Target ROAS**: {target_roas:.1f}x\n\n" elif target_cpa: output += f"🎯 **Target CPA**: ${target_cpa:.2f}\n\n" output += "## What is Performance Max?\n\n" output += "Performance Max uses Google's AI to show your ads across:\n" output += "- 🔍 Google Search\n" output += "- 📺 YouTube\n" output += "- 📧 Gmail\n" output += "- 🌐 Display Network\n" output += "- 📱 Discover\n" output += "- 🗺️ Google Maps\n\n" output += "## Next Steps\n\n" output += "1. ✅ Campaign created successfully\n" output += "2. 📦 Create asset groups with:\n" output += " - Headlines (3-15)\n" output += " - Descriptions (2-5)\n" output += " - Images (at least 1)\n" output += " - Videos (optional)\n" output += "3. 🎯 Add audience signals to guide AI\n" output += "4. ▶️ Enable campaign when assets are ready\n\n" output += "💡 **Tip**: Performance Max needs 6-8 weeks of learning to optimize fully.\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="create_performance_max") return f"❌ Failed to create Performance Max campaign: {error_msg}" @mcp.tool() def google_ads_create_asset_group( customer_id: str, campaign_id: str, asset_group_name: str, final_urls: str ) -> str: """Create an asset group for a Performance Max campaign. Asset groups contain the creative assets (images, videos, text) that Performance Max uses across different Google channels. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_id: Performance Max campaign ID asset_group_name: Name for this asset group final_urls: Comma-separated list of landing page URLs Returns: Asset group creation result Example: google_ads_create_asset_group( customer_id="1234567890", campaign_id="12345678", asset_group_name="Main Products", final_urls="https://example.com/products,https://example.com/shop" ) """ with performance_logger.track_operation('create_asset_group', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) # Parse URLs urls = [url.strip() for url in final_urls.split(',')] result = shopping_manager.create_asset_group( customer_id=customer_id, campaign_id=campaign_id, asset_group_name=asset_group_name, final_urls=urls ) audit_logger.log_api_call( customer_id=customer_id, operation='create_asset_group', campaign_id=campaign_id, status='success' ) output = f"# 📦 Asset Group Created\n\n" output += f"**Asset Group Name**: {result['asset_group_name']}\n" output += f"**Asset Group ID**: {result['asset_group_id']}\n" output += f"**Campaign ID**: {result['campaign_id']}\n" output += f"**Status**: PAUSED\n\n" output += "**Final URLs**:\n" for url in result['final_urls']: output += f"- {url}\n" output += "\n## Next Steps\n\n" output += "1. ✅ Asset group created\n" output += "2. 📝 Upload text assets (headlines, descriptions)\n" output += "3. 🖼️ Upload image assets (various sizes)\n" output += "4. 📹 Upload video assets (optional)\n" output += "5. 🎯 Add audience signals\n" output += "6. ▶️ Enable asset group\n\n" output += "💡 **Asset Requirements**:\n" output += "- Headlines: 3-15 (30 characters max)\n" output += "- Long headline: 1 (90 characters max)\n" output += "- Descriptions: 2-5 (90 characters max)\n" output += "- Images: Minimum 1 landscape + 1 square\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="create_asset_group") return f"❌ Failed to create asset group: {error_msg}" @mcp.tool() def google_ads_upload_pmax_assets( customer_id: str, asset_group_id: str, headlines_json: str, descriptions_json: str, long_headline: str ) -> str: """Upload text assets to a Performance Max asset group. Text assets include headlines, descriptions, and a long headline. Google's AI will mix and match these across different placements. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) asset_group_id: Asset group ID headlines_json: JSON array of headlines (3-15, max 30 chars each) descriptions_json: JSON array of descriptions (2-5, max 90 chars each) long_headline: Single long headline (max 90 characters) Example: google_ads_upload_pmax_assets( customer_id="1234567890", asset_group_id="12345678", headlines_json='["Buy Now", "Free Shipping", "Best Prices"]', descriptions_json='["Shop the latest products", "Quality guaranteed"]', long_headline="Shop Our Complete Product Line Today" ) """ with performance_logger.track_operation('upload_pmax_assets', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) # Parse JSON try: headlines = json.loads(headlines_json) descriptions = json.loads(descriptions_json) except json.JSONDecodeError as e: return f"❌ Invalid JSON format: {str(e)}" # Validate counts if len(headlines) < 3 or len(headlines) > 15: return "❌ Headlines must be between 3 and 15" if len(descriptions) < 2 or len(descriptions) > 5: return "❌ Descriptions must be between 2 and 5" # Validate character limits for i, headline in enumerate(headlines): if len(headline) > 30: return f"❌ Headline {i+1} exceeds 30 characters: '{headline}'" for i, description in enumerate(descriptions): if len(description) > 90: return f"❌ Description {i+1} exceeds 90 characters" if len(long_headline) > 90: return f"❌ Long headline exceeds 90 characters" result = shopping_manager.upload_pmax_text_asset( customer_id=customer_id, asset_group_id=asset_group_id, headlines=headlines, descriptions=descriptions, long_headline=long_headline ) audit_logger.log_api_call( customer_id=customer_id, operation='upload_pmax_assets', asset_group_id=asset_group_id, status='success' ) output = f"# 📝 Text Assets Uploaded\n\n" output += f"**Asset Group ID**: {result['asset_group_id']}\n" output += f"**Total Assets Uploaded**: {result['total_assets']}\n\n" output += "## Assets Summary\n\n" output += f"- **Headlines**: {result['headlines']}\n" output += f"- **Descriptions**: {result['descriptions']}\n" output += f"- **Long Headline**: 1\n\n" output += "## Uploaded Assets\n\n" # Show headlines output += "**Headlines**:\n" for asset in result['assets']: if asset['type'] == 'HEADLINE': output += f"- {asset['text']}\n" output += "\n**Descriptions**:\n" for asset in result['assets']: if asset['type'] == 'DESCRIPTION': output += f"- {asset['text']}\n" output += f"\n**Long Headline**: {long_headline}\n\n" output += "## Next Steps\n\n" output += "1. ✅ Text assets uploaded\n" output += "2. 🖼️ Upload image assets (required)\n" output += "3. 📹 Upload video assets (optional but recommended)\n" output += "4. 🎯 Add audience signals\n" output += "5. ▶️ Enable asset group and campaign\n\n" output += "💡 **Tip**: Google's AI will test different combinations to find what performs best.\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="upload_pmax_assets") return f"❌ Failed to upload assets: {error_msg}" @mcp.tool() def google_ads_set_audience_signals( customer_id: str, asset_group_id: str, audience_segments_json: str ) -> str: """Configure audience signals for a Performance Max asset group. Audience signals help Google's AI understand who your ideal customers are. The AI uses these as starting points and expands to find similar audiences. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) asset_group_id: Asset group ID audience_segments_json: JSON array of audience resource names Example: google_ads_set_audience_signals( customer_id="1234567890", asset_group_id="12345678", audience_segments_json='["customers/1234567890/userLists/123", ...]' ) """ with performance_logger.track_operation('set_audience_signals', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) # Parse audience segments try: audience_segments = json.loads(audience_segments_json) except json.JSONDecodeError: return "❌ Invalid JSON format for audience_segments_json" result = shopping_manager.set_audience_signals( customer_id=customer_id, asset_group_id=asset_group_id, audience_segments=audience_segments ) audit_logger.log_api_call( customer_id=customer_id, operation='set_audience_signals', asset_group_id=asset_group_id, status='success' ) output = f"# 🎯 Audience Signals Configured\n\n" output += f"**Asset Group ID**: {result['asset_group_id']}\n" output += f"**Audience Signals Added**: {result['audience_signals_added']}\n\n" output += "## What are Audience Signals?\n\n" output += "Audience signals are **suggestions** to Google's AI about your target customers.\n" output += "The AI uses these as starting points and expands beyond them to find:\n" output += "- Similar audiences\n" output += "- High-converting users\n" output += "- New customer segments\n\n" output += "## Benefits\n\n" output += "✅ **Faster Learning**: AI learns quicker with good signals\n" output += "✅ **Better Targeting**: More relevant audience discovery\n" output += "✅ **Higher Performance**: Improved conversion rates\n\n" output += "## Types of Signals You Can Add\n\n" output += "- 👥 Customer Match lists (your existing customers)\n" output += "- 🔄 Remarketing audiences (website visitors)\n" output += "- 🎯 In-Market audiences (ready to buy)\n" output += "- 💼 Affinity audiences (interests)\n" output += "- 📊 Similar audiences (lookalikes)\n\n" output += "💡 **Tip**: Start with your best-performing audiences from other campaigns.\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="set_audience_signals") return f"❌ Failed to set audience signals: {error_msg}" @mcp.tool() def google_ads_pmax_insights( customer_id: str, campaign_id: str, date_range: str = "LAST_30_DAYS" ) -> str: """Get performance insights for a Performance Max campaign. Provides comprehensive metrics including all-conversions data which captures conversions across all Google properties. Args: customer_id: Google Ads customer ID (10 digits, no hyphens) campaign_id: Performance Max campaign ID date_range: Date range (LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS) Returns: Performance Max campaign insights Example: google_ads_pmax_insights( customer_id="1234567890", campaign_id="12345678", date_range="LAST_30_DAYS" ) """ with performance_logger.track_operation('pmax_insights', customer_id=customer_id): try: client = get_auth_manager().get_client() shopping_manager = ShoppingPMaxManager(client) result = shopping_manager.get_pmax_insights( customer_id=customer_id, campaign_id=campaign_id, date_range=date_range ) if 'error' in result: return f"❌ {result['error']}" audit_logger.log_api_call( customer_id=customer_id, operation='pmax_insights', campaign_id=campaign_id, status='success' ) metrics = result['metrics'] output = f"# 🚀 Performance Max Insights\n\n" output += f"**Campaign**: {result['campaign_name']}\n" output += f"**Campaign ID**: {result['campaign_id']}\n" output += f"**Date Range**: {date_range}\n\n" output += "## Campaign Performance\n\n" output += f"- **Impressions**: {metrics['impressions']:,}\n" output += f"- **Clicks**: {metrics['clicks']:,}\n" output += f"- **CTR**: {metrics['ctr']:.2%}\n" output += f"- **Cost**: ${metrics['cost']:,.2f}\n" output += f"- **Conversions**: {metrics['conversions']:.1f}\n" output += f"- **Conversion Value**: ${metrics['conversion_value']:,.2f}\n" output += f"- **ROAS**: {metrics['roas']:.2f}x\n\n" output += "## All-Conversions Metrics\n\n" output += "*Includes conversions from all Google properties*\n\n" output += f"- **All Conversions**: {metrics['all_conversions']:.1f}\n" output += f"- **All Conversion Value**: ${metrics['all_conversions_value']:,.2f}\n\n" # Asset Groups output += f"## Asset Groups ({result['total_asset_groups']})\n\n" if result['total_asset_groups'] > 0: output += "| Asset Group | Status | Impressions | Clicks | Conv. |\n" output += "|-------------|--------|-------------|--------|-------|\n" for ag in result['asset_groups'][:5]: # Top 5 output += f"| {ag['asset_group_name'][:20]} | " output += f"{ag['status']} | " output += f"{ag['impressions']:,} | " output += f"{ag['clicks']:,} | " output += f"{ag['conversions']:.1f} |\n" else: output += "No asset groups found. Create asset groups to start serving ads.\n" output += "\n## Performance Analysis\n\n" # ROAS analysis if metrics['roas'] > 4.0: output += "✅ **Excellent ROAS!** Campaign is highly profitable.\n" output += "💡 Consider increasing budget to scale.\n" elif metrics['roas'] > 2.0: output += "💚 **Good ROAS.** Campaign is performing well.\n" output += "💡 Monitor and optimize asset groups.\n" elif metrics['roas'] > 1.0: output += "⚠️ **Marginal ROAS.** Room for improvement.\n" output += "💡 Review assets and audience signals.\n" else: output += "🚨 **Low ROAS.** Campaign needs optimization.\n" output += "💡 Check assets, budgets, and conversion tracking.\n" output += "\n## Optimization Tips\n\n" output += "1. 📝 Test different headlines and descriptions\n" output += "2. 🖼️ Add high-quality images in multiple sizes\n" output += "3. 🎯 Refine audience signals based on performance\n" output += "4. 💰 Ensure adequate budget (at least 10x target CPA)\n" output += "5. ⏱️ Allow 6-8 weeks for AI learning\n" return output except Exception as e: error_msg = ErrorHandler.handle_error(e, context="pmax_insights") return f"❌ Failed to get Performance Max insights: {error_msg}" logger.info("Shopping and Performance Max tools registered (9 tools)")

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