mcp-google-ads

repository·main·Indexed 20 days ago

https://github.com/cohnen/mcp-google-ads

A Model Context Protocol (MCP) server that integrates the Google Ads API with AI assistants like Claude and Cursor. It enables natural language querying and analysis of advertising data through tools such as list_accounts, execute_gaql_query, get_campaign_performance, get_ad_performance, and run_gaql. The server supports both OAuth 2.0 and Service Account authentication methods for accessing Google Ads data.

Tokens
29.2K
Snippets
97
Records
109
Agent score
70%

What's inside mcp-google-ads

  1. Overview of Google Ads MCP

    main
    Google Ads MCP is a Model Context Protocol (MCP) server that connects Google Ads with AI assistants like Claude or code editors like Cursor. It allows users to analyze advertising data, manage accounts, track campaign performance, and perform keyword/ad analysis using natural language. The tool acts as a bridge between the AI and the Google Ads API, enabling the AI to fetch data, visualize metrics, and provide actionable insights.
  2. Understand Google Ads Query Language (GAQL) structure

    main

    GAQL is used to retrieve resources, attributes, segments, and metrics from the Google Ads API. A standard query follows this structure:

    SELECT
      <field_1>,
      <field_2>
    FROM <resource>
    WHERE <condition>
    ORDER BY <field> [ASC|DESC]
    LIMIT <number>

    Field Categories

    • RESOURCE: The primary entity in the FROM clause (e.g., campaign, ad_group).
    • ATTRIBUTE: Properties of a resource (e.g., campaign.id).
    • SEGMENT: Fields that segment results (e.g., segments.date, segments.device).
    • METRIC: Performance data that does not segment queries (e.g., metrics.clicks).
    SELECT
      campaign.id,
      campaign.name,
      metrics.impressions,
      segments.device
    FROM campaign
  3. Expose data using Resources

    main

    Resources are used to expose data to LLMs (similar to GET endpoints in REST). They should provide data without significant computation or side effects. FastMCP supports both static and dynamic resources using templates.

    Static Resource:

    @mcp.resource("config://app")
    def get_config() -> str:
        """Static configuration data"""
        return "App configuration here"

    Dynamic Resource (with parameters):

    @mcp.resource("users://{user_id}/profile")
    def get_user_profile(user_id: str) -> str:
        """Dynamic user data"""
        return f"Profile data for user {user_id}"
    @mcp.resource("users://{user_id}/profile")
    def get_user_profile(user_id: str) -> str:
        return f"Profile data for user {user_id}"
  4. Access MCP capabilities via Context

    main

    The Context object allows tools and resources to interact with the MCP environment. To use it, include a parameter annotated with fastmcp.Context in your function signature.

    Capabilities provided by Context:

    • Progress reporting: ctx.report_progress(current, total)
    • Logging: ctx.debug(), ctx.info(), ctx.warning(), ctx.error()
    • Resource access: await ctx.read_resource(uri)
    • Metadata: ctx.request_id, ctx.client_id

    Example usage:

    from fastmcp import FastMCP, Context
    
    @mcp.tool()
    async def long_task(files: list[str], ctx: Context) -> str:
        for i, file in enumerate(files):
            ctx.info(f"Processing {file}")
            await ctx.report_progress(i, len(files))
            data = await ctx.read_resource(f"file://{file}")
        return "Processing complete"
  5. Define interaction patterns with Prompts

    main

    Prompts are reusable templates that help LLMs interact with your server effectively. They can be simple strings or structured sequences of messages.

    Simple String Prompt:

    @mcp.prompt()
    def review_code(code: str) -> str:
        return f"Please review this code:\n\n{code}"

    Structured Message Prompt:

    from fastmcp.prompts.base import UserMessage, AssistantMessage
    
    @mcp.prompt()
    def debug_error(error: str) -> list[Message]:
        return [
            UserMessage("I'm seeing this error:"),
            UserMessage(error),
            AssistantMessage("I'll help debug that. What have you tried so far?")
        ]
  6. Specify Custom Server Object Names

    main

    By default, FastMCP commands look for a server object named mcp, app, or server in your file. If you use a different name or have multiple servers in one file, you must use the filename.py:object_name syntax.

    # If your object is named 'my_custom_server' in server.py
    fastmcp run server.py:my_custom_server
  7. Execute actions using Tools

    main

    Tools allow LLMs to take actions through your server (similar to POST endpoints). They are intended to perform computation or produce side effects. You can use standard Python types, async functions, or complex pydantic models for input validation.

    Simple Tool:

    @mcp.tool()
    def calculate_bmi(weight_kg: float, height_m: float) -> float:
        """Calculate BMI"""
        return weight_kg / (height_m ** 2)

    Async Tool (HTTP request):

    import httpx
    
    @mcp.tool()
    async def fetch_weather(city: str) -> str:
        async with httpx.AsyncClient() as client:
            response = await client.get(f"https://api.weather.com/{city}")
            return response.text

    Complex Input with Pydantic:

    from pydantic import BaseModel, Field
    from typing import Annotated
    
    class ShrimpTank(BaseModel):
        class Shrimp(BaseModel):
            name: Annotated[str, Field(max_length=10)]
        shrimp: list[Shrimp]
    
    @mcp.tool()
    def name_shrimp(tank: ShrimpTank, extra_names: Annotated[list[str], Field(max_length=10)]) -> list[str]:
        return [shrimp.name for shrimp in tank.shrimp] + extra_names
    @mcp.tool()
    def calculate_bmi(weight_kg: float, height_m: float) -> float:
        return weight_kg / (height_m ** 2)
  8. Handle images with the Image class

    main

    FastMCP provides an Image class that automatically handles image data, conversion, and MIME types. Images can be returned by both tools and resources.

    Creating a thumbnail:

    from fastmcp import FastMCP, Image
    from PIL import Image as PILImage
    
    @mcp.tool()
    def create_thumbnail(image_path: str) -> Image:
        img = PILImage.open(image_path)
        img.thumbnail((100, 100))
        return Image(data=img.tobytes(), format="png")

    Loading from disk:

    @mcp.tool()
    def load_image(path: str) -> Image:
        return Image(path=path)
    from fastmcp import Image
    
    @mcp.tool()
    def load_image(path: str) -> Image:
        return Image(path=path)
  9. Use the Context object in tools and resources

    main

    The Context object provides access to MCP capabilities during a request. To use it, add a parameter to your tool or resource function with the Context type annotation. The parameter name can be anything.

    Capabilities provided by Context:

    • Logging: ctx.info(), ctx.debug(), ctx.warning(), ctx.error().
    • Progress Reporting: ctx.report_progress(progress, total).
    • Resource Access: ctx.read_resource(uri).
    • Metadata: Access ctx.request_id and ctx.client_id.
    • Server Access: Access the underlying FastMCP instance via ctx.fastmcp or the RequestContext via ctx.request_context (only available during an active request).
    @server.tool()
    def my_tool(x: int, ctx: Context) -> str:
        # Log messages to the client
        ctx.info(f"Processing {x}")
        ctx.debug("Debug info")
        ctx.warning("Warning message")
        ctx.error("Error message")
    
        # Report progress
        ctx.report_progress(50, 100)
    
        # Access resources
        data = ctx.read_resource("resource://data")
    
        # Get request info
        request_id = ctx.request_id
        client_id = ctx.client_id
    
        return str(x)
  10. Quickstart: Create and run a FastMCP server

    main

    You can create a simple MCP server by importing FastMCP and using decorators to define tools and resources.

    1. Define your server:

    from fastmcp import FastMCP
    
    mcp = FastMCP("Demo")
    
    @mcp.tool()
    def add(a: int, b: int) -> int:
        """Add two numbers"""
        return a + b
    
    @mcp.resource("greeting://{name}")
    def get_greeting(name: str) -> str:
        """Get a personalized greeting"""
        return f"Hello, {name}!"

    2. Run or Install:

    • To install the server in Claude Desktop: fastmcp install server.py
    • To test the server with the MCP Inspector: fastmcp dev server.py
  11. Execute FastMCP Server Directly

    main

    For advanced use cases like custom deployments or running without Claude, you can execute the server directly.

    Important: When running directly, FastMCP ignores the dependencies list provided in the FastMCP constructor. You are responsible for ensuring all required dependencies are available in your current environment.

    To run a server, ensure your script includes the mcp.run() call in the entry point.

    from fastmcp import FastMCP
    
    mcp = FastMCP("My App")
    
    if __name__ == "__main__":
        mcp.run()

    Run via FastMCP CLI

    fastmcp run server.py

    Run via Python or uv

    python server.py uv run python server.py

  12. Install Google Ads MCP locally

    main

    Follow these steps to install the project and its dependencies:

    1. Clone the repository:

      git clone https://github.com/ixigo/mcp-google-ads.git
      cd mcp-google-ads
    2. Create and activate a virtual environment:

      # Using uv (recommended)
      pip install uv
      uv venv .venv
      source .venv/bin/activate  # Mac/Linux
      # OR
      .venv\Scripts\activate    # Windows
    3. Install dependencies:

      uv pip install -r requirements.txt
      # OR
      pip install -r requirements.txt
    git clone https://github.com/ixigo/mcp-google-ads.git
    cd mcp-google-ads
    uv venv .venv
    source .venv/bin/activate
    uv pip install -r requirements.txt