We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bradenpan/mcp-google-ads'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Ad creation and management tools."""
from typing import Any
from mcp.server import Server
from ..google_ads_client import ads_client
def register_ad_tools(server: Server) -> None:
"""Register ad management tools with the MCP server.
Args:
server: The MCP server instance.
"""
@server.tool()
async def list_ads(
ad_group_id: str | None = None,
campaign_id: str | None = None,
) -> dict[str, Any]:
"""List ads, optionally filtered by ad group or campaign.
Args:
ad_group_id: Optional ad group ID to filter by.
campaign_id: Optional campaign ID to filter by.
Returns:
Dictionary containing list of ads.
"""
query = """
SELECT
ad_group_ad.ad.id,
ad_group_ad.ad.type,
ad_group_ad.ad.responsive_search_ad.headlines,
ad_group_ad.ad.responsive_search_ad.descriptions,
ad_group_ad.status,
ad_group.id,
ad_group.name,
campaign.id,
campaign.name
FROM ad_group_ad
"""
conditions = []
if ad_group_id:
conditions.append(f"ad_group.id = {ad_group_id}")
if campaign_id:
conditions.append(f"campaign.id = {campaign_id}")
if conditions:
query += " WHERE " + " AND ".join(conditions)
query += " ORDER BY campaign.name, ad_group.name"
try:
results = ads_client.search(query)
ads = []
for row in results:
ad_data = {
"id": str(row.ad_group_ad.ad.id),
"type": row.ad_group_ad.ad.type_.name,
"status": row.ad_group_ad.status.name,
"ad_group_id": str(row.ad_group.id),
"ad_group_name": row.ad_group.name,
"campaign_id": str(row.campaign.id),
"campaign_name": row.campaign.name,
}
# Extract RSA headlines and descriptions if available
rsa = row.ad_group_ad.ad.responsive_search_ad
if rsa:
ad_data["headlines"] = [h.text for h in rsa.headlines]
ad_data["descriptions"] = [d.text for d in rsa.descriptions]
ads.append(ad_data)
return {"ads": ads, "count": len(ads)}
except Exception as e:
return {"error": str(e), "ads": []}
@server.tool()
async def create_responsive_search_ad(
ad_group_id: str,
headlines: list[str],
descriptions: list[str],
final_url: str,
path1: str | None = None,
path2: str | None = None,
) -> dict[str, Any]:
"""Create a new Responsive Search Ad (RSA).
Args:
ad_group_id: The ad group ID to create the ad in.
headlines: List of headline texts (3-15 headlines, max 30 chars each).
descriptions: List of description texts (2-4 descriptions, max 90 chars each).
final_url: The landing page URL.
path1: Optional display URL path 1 (max 15 chars).
path2: Optional display URL path 2 (max 15 chars).
Returns:
Dictionary with created ad details or error.
"""
# Validate inputs
if len(headlines) < 3 or len(headlines) > 15:
return {"error": "RSA requires 3-15 headlines"}
if len(descriptions) < 2 or len(descriptions) > 4:
return {"error": "RSA requires 2-4 descriptions"}
for i, h in enumerate(headlines):
if len(h) > 30:
return {"error": f"Headline {i+1} exceeds 30 character limit"}
for i, d in enumerate(descriptions):
if len(d) > 90:
return {"error": f"Description {i+1} exceeds 90 character limit"}
# TODO: Implement ad creation using mutate operations
return {
"status": "not_implemented",
"message": "RSA creation will be implemented with Google Ads mutate operations.",
"planned_config": {
"ad_group_id": ad_group_id,
"headlines": headlines,
"descriptions": descriptions,
"final_url": final_url,
"path1": path1,
"path2": path2,
},
}
@server.tool()
async def update_ad_status(
ad_group_id: str,
ad_id: str,
status: str,
) -> dict[str, Any]:
"""Update the status of an ad.
Args:
ad_group_id: The ad group ID containing the ad.
ad_id: The ad ID to update.
status: New status (ENABLED, PAUSED, REMOVED).
Returns:
Dictionary with update result.
"""
# TODO: Implement ad status update using mutate operations
return {
"status": "not_implemented",
"message": "Ad status update will be implemented with Google Ads mutate operations.",
"ad_group_id": ad_group_id,
"ad_id": ad_id,
"planned_status": status,
}