Skip to main content
Glama
johnoconnor0

Google Ads MCP Server

by johnoconnor0
local_app_manager.py17.2 kB
""" Local & App Campaigns Manager Handles Local campaigns and App campaigns (Universal App Campaigns). Local Campaigns: - Promote physical business locations - Drive store visits and foot traffic - Optimize for local actions (calls, directions, visits) - Google My Business integration App Campaigns: - Promote mobile app installs - Drive app engagement - Automated across Google properties - App store optimization """ from typing import Dict, Any, List, Optional from google.ads.googleads.client import GoogleAdsClient from dataclasses import dataclass from enum import Enum class AppCampaignAppStore(str, Enum): """App store types.""" APPLE_APP_STORE = "APPLE_APP_STORE" GOOGLE_APP_STORE = "GOOGLE_APP_STORE" class AppCampaignBiddingStrategyGoalType(str, Enum): """App campaign bidding goals.""" OPTIMIZE_INSTALLS_TARGET_INSTALL_COST = "OPTIMIZE_INSTALLS_TARGET_INSTALL_COST" OPTIMIZE_IN_APP_CONVERSIONS_TARGET_INSTALL_COST = "OPTIMIZE_IN_APP_CONVERSIONS_TARGET_INSTALL_COST" OPTIMIZE_IN_APP_CONVERSIONS_TARGET_CONVERSION_COST = "OPTIMIZE_IN_APP_CONVERSIONS_TARGET_CONVERSION_COST" OPTIMIZE_RETURN_ON_ADVERTISING_SPEND = "OPTIMIZE_RETURN_ON_ADVERTISING_SPEND" OPTIMIZE_PRE_REGISTRATION_CONVERSION_VOLUME = "OPTIMIZE_PRE_REGISTRATION_CONVERSION_VOLUME" @dataclass class LocalCampaignConfig: """Configuration for local campaign creation.""" name: str budget_amount: float location_ids: List[str] # Google My Business location IDs optimization_goal: str = "STORE_VISITS" # STORE_VISITS, STORE_SALES @dataclass class AppCampaignConfig: """Configuration for app campaign creation.""" name: str app_id: str # App store ID app_store: AppCampaignAppStore budget_amount: float bidding_strategy_goal_type: AppCampaignBiddingStrategyGoalType target_cpa: Optional[float] = None class LocalAppManager: """Manager for Local and App campaigns.""" def __init__(self, client: GoogleAdsClient): """Initialize the local/app manager. Args: client: Authenticated GoogleAdsClient instance """ self.client = client def create_local_campaign( self, customer_id: str, config: LocalCampaignConfig ) -> Dict[str, Any]: """Create a Local campaign. Args: customer_id: Customer ID (without hyphens) config: Local campaign configuration Returns: Created campaign details """ campaign_service = self.client.get_service("CampaignService") campaign_budget_service = self.client.get_service("CampaignBudgetService") # Create campaign budget budget_operation = self.client.get_type("CampaignBudgetOperation") budget = budget_operation.create budget.name = f"{config.name} Budget" budget.amount_micros = int(config.budget_amount * 1_000_000) budget.delivery_method = self.client.enums.BudgetDeliveryMethodEnum.STANDARD budget_response = campaign_budget_service.mutate_campaign_budgets( customer_id=customer_id, operations=[budget_operation] ) budget_resource_name = budget_response.results[0].resource_name # Create local campaign campaign_operation = self.client.get_type("CampaignOperation") campaign = campaign_operation.create campaign.name = config.name campaign.advertising_channel_type = self.client.enums.AdvertisingChannelTypeEnum.LOCAL campaign.status = self.client.enums.CampaignStatusEnum.PAUSED campaign.campaign_budget = budget_resource_name # Local campaign settings campaign.local_campaign_setting.location_source_type = ( self.client.enums.LocationSourceTypeEnum.GOOGLE_MY_BUSINESS ) # Bidding strategy - maximize conversions for local actions campaign.maximize_conversions.CopyFrom(self.client.get_type("MaximizeConversions")) # Create campaign response = campaign_service.mutate_campaigns( customer_id=customer_id, operations=[campaign_operation] ) campaign_id = response.results[0].resource_name.split('/')[-1] return { 'campaign_id': campaign_id, 'campaign_name': config.name, 'resource_name': response.results[0].resource_name, 'budget': config.budget_amount, 'location_count': len(config.location_ids), 'optimization_goal': config.optimization_goal } def get_local_performance( self, customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> Dict[str, Any]: """Get Local campaign performance metrics. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range for metrics Returns: Local campaign performance data """ ga_service = self.client.get_service("GoogleAdsService") query = f""" SELECT campaign.id, campaign.name, metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, metrics.conversions, metrics.conversions_value, metrics.view_through_conversions FROM campaign WHERE campaign.advertising_channel_type = 'LOCAL' AND segments.date DURING {date_range} """ if campaign_id: query += f" AND campaign.id = {campaign_id}" query += " ORDER BY metrics.impressions DESC" response = ga_service.search(customer_id=customer_id, query=query) campaigns = [] for row in response: campaigns.append({ 'campaign_id': str(row.campaign.id), 'campaign_name': row.campaign.name, 'impressions': row.metrics.impressions, 'clicks': row.metrics.clicks, 'ctr': row.metrics.ctr, 'cost': row.metrics.cost_micros / 1_000_000, 'conversions': row.metrics.conversions, 'conversion_value': row.metrics.conversions_value, 'view_through_conversions': row.metrics.view_through_conversions }) return { 'campaigns': campaigns, 'total_campaigns': len(campaigns) } def get_store_visits( self, customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> Dict[str, Any]: """Get store visit conversion data. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range for metrics Returns: Store visit conversion data """ ga_service = self.client.get_service("GoogleAdsService") # Note: Store visits require Google My Business integration # and may take 4-6 weeks to accumulate data query = f""" SELECT campaign.id, campaign.name, metrics.conversions, metrics.conversions_value, segments.conversion_action_name FROM campaign WHERE campaign.advertising_channel_type = 'LOCAL' AND segments.date DURING {date_range} AND segments.conversion_action_name LIKE '%store visit%' """ if campaign_id: query += f" AND campaign.id = {campaign_id}" response = ga_service.search(customer_id=customer_id, query=query) store_visits = [] total_visits = 0 total_value = 0 for row in response: visits = row.metrics.conversions value = row.metrics.conversions_value store_visits.append({ 'campaign_id': str(row.campaign.id), 'campaign_name': row.campaign.name, 'conversion_action': row.segments.conversion_action_name, 'store_visits': visits, 'value': value }) total_visits += visits total_value += value return { 'campaigns': store_visits, 'total_store_visits': total_visits, 'total_value': total_value, 'has_data': len(store_visits) > 0 } def create_app_campaign( self, customer_id: str, config: AppCampaignConfig ) -> Dict[str, Any]: """Create an App campaign (Universal App Campaign). Args: customer_id: Customer ID (without hyphens) config: App campaign configuration Returns: Created campaign details """ campaign_service = self.client.get_service("CampaignService") campaign_budget_service = self.client.get_service("CampaignBudgetService") # Create campaign budget budget_operation = self.client.get_type("CampaignBudgetOperation") budget = budget_operation.create budget.name = f"{config.name} Budget" budget.amount_micros = int(config.budget_amount * 1_000_000) budget.delivery_method = self.client.enums.BudgetDeliveryMethodEnum.STANDARD budget_response = campaign_budget_service.mutate_campaign_budgets( customer_id=customer_id, operations=[budget_operation] ) budget_resource_name = budget_response.results[0].resource_name # Create app campaign campaign_operation = self.client.get_type("CampaignOperation") campaign = campaign_operation.create campaign.name = config.name campaign.advertising_channel_type = self.client.enums.AdvertisingChannelTypeEnum.MULTI_CHANNEL campaign.advertising_channel_sub_type = self.client.enums.AdvertisingChannelSubTypeEnum.APP_CAMPAIGN campaign.status = self.client.enums.CampaignStatusEnum.PAUSED campaign.campaign_budget = budget_resource_name # App campaign settings campaign.app_campaign_setting.app_id = config.app_id campaign.app_campaign_setting.app_store = self.client.enums.AppCampaignAppStoreEnum[ config.app_store.value ] campaign.app_campaign_setting.bidding_strategy_goal_type = ( self.client.enums.AppCampaignBiddingStrategyGoalTypeEnum[ config.bidding_strategy_goal_type.value ] ) # Set bidding strategy based on goal type if config.bidding_strategy_goal_type.value.startswith("OPTIMIZE_INSTALLS"): if config.target_cpa: campaign.target_cpa.target_cpa_micros = int(config.target_cpa * 1_000_000) else: campaign.maximize_conversions.CopyFrom(self.client.get_type("MaximizeConversions")) elif "TARGET_CONVERSION_COST" in config.bidding_strategy_goal_type.value: if config.target_cpa: campaign.target_cpa.target_cpa_micros = int(config.target_cpa * 1_000_000) else: campaign.maximize_conversions.CopyFrom(self.client.get_type("MaximizeConversions")) elif config.bidding_strategy_goal_type.value == "OPTIMIZE_RETURN_ON_ADVERTISING_SPEND": campaign.maximize_conversion_value.CopyFrom(self.client.get_type("MaximizeConversionValue")) else: campaign.maximize_conversions.CopyFrom(self.client.get_type("MaximizeConversions")) # Create campaign response = campaign_service.mutate_campaigns( customer_id=customer_id, operations=[campaign_operation] ) campaign_id = response.results[0].resource_name.split('/')[-1] return { 'campaign_id': campaign_id, 'campaign_name': config.name, 'resource_name': response.results[0].resource_name, 'app_id': config.app_id, 'app_store': config.app_store.value, 'budget': config.budget_amount, 'bidding_goal': config.bidding_strategy_goal_type.value } def get_app_performance( self, customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> Dict[str, Any]: """Get App campaign performance metrics. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range for metrics Returns: App campaign performance data """ ga_service = self.client.get_service("GoogleAdsService") query = f""" SELECT campaign.id, campaign.name, campaign.app_campaign_setting.app_id, campaign.app_campaign_setting.app_store, metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, metrics.conversions, metrics.conversions_value, metrics.cost_per_conversion FROM campaign WHERE campaign.advertising_channel_sub_type = 'APP_CAMPAIGN' AND segments.date DURING {date_range} """ if campaign_id: query += f" AND campaign.id = {campaign_id}" query += " ORDER BY metrics.impressions DESC" response = ga_service.search(customer_id=customer_id, query=query) campaigns = [] for row in response: campaigns.append({ 'campaign_id': str(row.campaign.id), 'campaign_name': row.campaign.name, 'app_id': row.campaign.app_campaign_setting.app_id, 'app_store': row.campaign.app_campaign_setting.app_store.name, 'impressions': row.metrics.impressions, 'clicks': row.metrics.clicks, 'ctr': row.metrics.ctr, 'cost': row.metrics.cost_micros / 1_000_000, 'conversions': row.metrics.conversions, 'conversion_value': row.metrics.conversions_value, 'cost_per_conversion': row.metrics.cost_per_conversion }) return { 'campaigns': campaigns, 'total_campaigns': len(campaigns) } def get_app_conversions( self, customer_id: str, campaign_id: Optional[str] = None, date_range: str = "LAST_30_DAYS" ) -> Dict[str, Any]: """Get app install and engagement conversion data. Args: customer_id: Customer ID (without hyphens) campaign_id: Optional campaign ID filter date_range: Date range for metrics Returns: App conversion data by type """ ga_service = self.client.get_service("GoogleAdsService") query = f""" SELECT campaign.id, campaign.name, segments.conversion_action_name, segments.conversion_action_category, metrics.conversions, metrics.conversions_value FROM campaign WHERE campaign.advertising_channel_sub_type = 'APP_CAMPAIGN' AND segments.date DURING {date_range} """ if campaign_id: query += f" AND campaign.id = {campaign_id}" query += " ORDER BY metrics.conversions DESC" response = ga_service.search(customer_id=customer_id, query=query) conversions_by_type = {} campaigns_data = {} for row in response: campaign_id_str = str(row.campaign.id) conversion_category = row.segments.conversion_action_category.name conversion_name = row.segments.conversion_action_name # Track by campaign if campaign_id_str not in campaigns_data: campaigns_data[campaign_id_str] = { 'campaign_name': row.campaign.name, 'conversions': {} } campaigns_data[campaign_id_str]['conversions'][conversion_name] = { 'category': conversion_category, 'conversions': row.metrics.conversions, 'value': row.metrics.conversions_value } # Track by type if conversion_category not in conversions_by_type: conversions_by_type[conversion_category] = { 'total_conversions': 0, 'total_value': 0 } conversions_by_type[conversion_category]['total_conversions'] += row.metrics.conversions conversions_by_type[conversion_category]['total_value'] += row.metrics.conversions_value return { 'campaigns': campaigns_data, 'by_type': conversions_by_type, 'total_campaigns': len(campaigns_data) }

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