Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions analytics_mcp/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,21 @@
from analytics_mcp.tools.reporting.metadata import (
get_custom_dimensions_and_metrics,
)
from analytics_mcp.tools.reporting.pivot import (
run_pivot_report,
_run_pivot_report_description,
)

run_report_with_description = FunctionTool(run_report)
run_report_with_description.description = _run_report_description()
run_realtime_report_with_description = FunctionTool(run_realtime_report)
run_realtime_report_with_description.description = (
_run_realtime_report_description()
)
run_pivot_report_with_description = FunctionTool(run_pivot_report)
run_pivot_report_with_description.description = (
_run_pivot_report_description()
)

# Instantiate the ADK tools
tools = [
Expand Down Expand Up @@ -123,6 +131,14 @@ def sanitize_mcp_schema_properties(node: dict) -> None:
]
elif tool.name == "run_realtime_report":
tool.inputSchema["required"] = ["property_id", "dimensions", "metrics"]
elif tool.name == "run_pivot_report":
tool.inputSchema["required"] = [
"property_id",
"date_ranges",
"dimensions",
"metrics",
"pivots",
]


@app.list_tools()
Expand Down
73 changes: 73 additions & 0 deletions analytics_mcp/tools/reporting/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,79 @@ def get_dimension_filter_hints():
""" + _FILTER_NOTES


def get_pivot_hints():
"""Returns hints and examples for pivots arguments."""
pivot_by_country = data_v1beta.Pivot(
field_names=["country"],
limit=5,
order_bys=[
data_v1beta.OrderBy(
metric=data_v1beta.OrderBy.MetricOrderBy(
metric_name="sessions",
),
desc=True,
)
],
)
pivot_by_browser = data_v1beta.Pivot(
field_names=["browser"],
limit=3,
offset=0,
)
pivot_by_source_medium = data_v1beta.Pivot(
field_names=["sessionSource", "sessionMedium"],
limit=10,
order_bys=[
data_v1beta.OrderBy(
dimension=data_v1beta.OrderBy.DimensionOrderBy(
dimension_name="sessionSource",
order_type=data_v1beta.OrderBy.DimensionOrderBy.OrderType.ALPHANUMERIC,
),
desc=False,
)
],
)

return f"""Example pivots arguments:

Each pivot in the `pivots` list must reference dimensions that are
also present in the report's `dimensions` argument.

A pivot report can contain multiple pivots. Every dimension
referenced by one of the report's dimensions must be included in
exactly one of the pivots.

1. A single pivot by country, returning the top 5 by sessions:
[ {proto_to_json(pivot_by_country)} ]

2. A simple pivot by browser, returning the first 3 rows:
[ {proto_to_json(pivot_by_browser)} ]

3. A pivot by multiple dimensions (source + medium):
[ {proto_to_json(pivot_by_source_medium)} ]

4. Multiple pivots (e.g. rows pivot by country, columns pivot by
browser). Every report dimension must appear in exactly one
pivot:
[
{proto_to_json(pivot_by_country)},
{proto_to_json(pivot_by_browser)}
]

### Pivot fields

- `field_names`: List of dimension names to pivot on. Must be a
subset of the report's `dimensions`.
- `limit`: The number of unique combinations of dimension values
to return in this pivot. Required. The `limit` parameter is
required for each Pivot.
- `offset`: The row count of the start row for this pivot.
Defaults to 0.
- `order_bys`: Specifies how dimensions and metrics in this pivot
are ordered. Uses the same OrderBy format as `run_report`.
"""


def get_order_bys_hints():
"""Returns hints and examples for order_bys arguments."""
dimension_alphanumeric_ascending = data_v1beta.OrderBy(
Expand Down
175 changes: 175 additions & 0 deletions analytics_mcp/tools/reporting/pivot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Copyright 2025 Google LLC All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tools for running pivot reports using the Data API."""

from typing import Any, Dict, List

from analytics_mcp.tools.reporting.metadata import (
get_date_ranges_hints,
get_dimension_filter_hints,
get_metric_filter_hints,
get_pivot_hints,
)
from analytics_mcp.tools.utils import (
construct_property_rn,
create_data_api_client,
proto_to_dict,
)
from google.analytics import data_v1beta


def _run_pivot_report_description() -> str:
"""Returns the description for the `run_pivot_report` tool."""
return f"""
{run_pivot_report.__doc__}

## Hints for arguments

Here are some hints that outline the expected format and requirements
for arguments.

### Hints for `dimensions`

The `dimensions` list must consist solely of either of the following:

1. Standard dimensions defined in the HTML table at
https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#dimensions.
These dimensions are available to *every* property.
2. Custom dimensions for the `property_id`. Use the
`get_custom_dimensions_and_metrics` tool to retrieve the list of
custom dimensions for a property.

### Hints for `metrics`

The `metrics` list must consist solely of either of the following:

1. Standard metrics defined in the HTML table at
https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#metrics.
These metrics are available to *every* property.
2. Custom metrics for the `property_id`. Use the
`get_custom_dimensions_and_metrics` tool to retrieve the list of
custom metrics for a property.

### Hints for `pivots`:
{get_pivot_hints()}

### Hints for `date_ranges`:
{get_date_ranges_hints()}

### Hints for `dimension_filter`:
{get_dimension_filter_hints()}

### Hints for `metric_filter`:
{get_metric_filter_hints()}

"""


async def run_pivot_report(
property_id: int | str,
date_ranges: List[Dict[str, Any]],
dimensions: List[str],
metrics: List[str],
pivots: List[Dict[str, Any]],
dimension_filter: Dict[str, Any] = None,
metric_filter: Dict[str, Any] = None,
currency_code: str = None,
return_property_quota: bool = False,
) -> Dict[str, Any]:
"""Runs a Google Analytics Data API pivot report.

Pivot reports are similar to pivot tables in spreadsheets. Pivots
reorganize the information in the report by rotating (pivoting)
your data on shared dimensions. For example, you can pivot on the
`country` dimension to create a column for each country, then
examine metric values for each country across a single row.

See
https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runPivotReport
for more information.

Note that the reference docs at
https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta
all use camelCase field names, but field names passed to this method should
be in snake_case since the tool is using the protocol buffers (protobuf)
format. The protocol buffers for the Data API are available at
https://github.com/googleapis/googleapis/tree/master/google/analytics/data/v1beta.

Args:
property_id: The Google Analytics property ID. Accepted formats are:
- A number
- A string consisting of 'properties/' followed by a number
date_ranges: A list of date ranges
(https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/DateRange)
to include in the report.
dimensions: A list of dimensions to include in the report.
metrics: A list of metrics to include in the report.
pivots: A list of pivot definitions. Each pivot specifies which
dimensions from the report to include in the pivot table, as
well as how many rows and the sort order. The `field_names`
in each pivot must be a subset of the report's `dimensions`.
See
https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/Pivot
for the Pivot schema.
dimension_filter: A Data API FilterExpression
(https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/FilterExpression)
to apply to the dimensions. Don't use this for filtering
metrics. Use metric_filter instead. The `field_name` in a
`dimension_filter` must be a dimension, as defined in the
`get_standard_dimensions` and `get_dimensions` tools.
metric_filter: A Data API FilterExpression
(https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/FilterExpression)
to apply to the metrics. Don't use this for filtering
dimensions. Use dimension_filter instead. The `field_name`
in a `metric_filter` must be a metric, as defined in the
`get_standard_metrics` and `get_metrics` tools.
currency_code: The currency code to use for currency values. Must be in
ISO4217 format, such as "AED", "USD", "JPY". If the field is empty,
the report uses the property's default currency.
return_property_quota: Whether to return property quota in the
response.
"""
request = data_v1beta.RunPivotReportRequest(
property=construct_property_rn(property_id),
dimensions=[
data_v1beta.Dimension(name=dimension)
for dimension in dimensions
],
metrics=[
data_v1beta.Metric(name=metric) for metric in metrics
],
date_ranges=[
data_v1beta.DateRange(dr) for dr in date_ranges
],
pivots=[data_v1beta.Pivot(pivot) for pivot in pivots],
return_property_quota=return_property_quota,
)

if dimension_filter:
request.dimension_filter = data_v1beta.FilterExpression(
dimension_filter
)

if metric_filter:
request.metric_filter = data_v1beta.FilterExpression(
metric_filter
)

if currency_code:
request.currency_code = currency_code

response = await create_data_api_client().run_pivot_report(request)

return proto_to_dict(response)