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
37 changes: 37 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: "CodeQL"

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1"

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read

strategy:
fail-fast: false
matrix:
language: [actions]

steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Check warning on line 27 in .github/workflows/codeql.yml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

codeql.yml:27: action's hash pin has mismatched or missing version comment: points to commit 11d5960a3267

- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4

Check warning on line 30 in .github/workflows/codeql.yml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

codeql.yml:30: action's hash pin has mismatched or missing version comment: points to commit ff2f1c621b7f
with:
languages: ${{ matrix.language }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4

Check warning on line 35 in .github/workflows/codeql.yml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

codeql.yml:35: action's hash pin has mismatched or missing version comment: points to commit ff2f1c621b7f
with:
category: "/language:${{ matrix.language }}"
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ system_tests/local_test_setup
pylintrc
pylintrc.test

.agents/skills
.agents/skills
.stargazer-*
94 changes: 89 additions & 5 deletions analytics_mcp/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
# MCP Server Imports
import json
import sys
from json import tool
from mcp import types as mcp_types # Use alias to avoid conflict
from mcp.server.lowlevel import Server

Expand Down Expand Up @@ -54,6 +53,27 @@
run_conversions_report,
_run_conversions_report_description,
)
from analytics_mcp.tools.reporting.audience_exports import (
create_audience_export,
get_audience_export,
list_audience_exports,
query_audience_export,
)
from analytics_mcp.tools.measurement import (
validate_event,
send_event,
)
from analytics_mcp.tools.reporting.batch import (
batch_run_reports,
_batch_run_reports_description,
)
from analytics_mcp.tools.reporting.compatibility import check_compatibility
from analytics_mcp.tools.reporting.pivot import (
run_pivot_report,
_run_pivot_report_description,
batch_run_pivot_reports,
_batch_run_pivot_reports_description,
)

run_report_with_description = FunctionTool(run_report)
run_report_with_description.description = _run_report_description()
Expand All @@ -71,7 +91,23 @@
)

# Instantiate the ADK tools
run_pivot_report_with_description = FunctionTool(run_pivot_report)
run_pivot_report_with_description.description = _run_pivot_report_description()
batch_run_pivot_reports_with_description = FunctionTool(batch_run_pivot_reports)
batch_run_pivot_reports_with_description.description = (
_batch_run_pivot_reports_description()
)

batch_run_reports_with_description = FunctionTool(batch_run_reports)
batch_run_reports_with_description.description = (
_batch_run_reports_description()
)

tools = [
batch_run_reports_with_description,
FunctionTool(check_compatibility),
run_pivot_report_with_description,
batch_run_pivot_reports_with_description,
FunctionTool(get_account_summaries),
FunctionTool(list_google_ads_links),
FunctionTool(get_property_details),
Expand All @@ -81,6 +117,12 @@
run_realtime_report_with_description,
run_funnel_report_with_description,
run_conversions_report_with_description,
FunctionTool(create_audience_export),
FunctionTool(get_audience_export),
FunctionTool(list_audience_exports),
FunctionTool(query_audience_export),
FunctionTool(validate_event),
FunctionTool(send_event),
]

tool_map = {t.name: t for t in tools}
Expand Down Expand Up @@ -140,6 +182,26 @@ def sanitize_mcp_schema_properties(node: dict) -> None:
"dimensions",
"metrics",
]
elif tool.name == "batch_run_reports":
tool.inputSchema["required"] = [
"property_id",
"requests",
]
elif tool.name == "check_compatibility":
tool.inputSchema["required"] = ["property_id"]
elif tool.name == "run_pivot_report":
tool.inputSchema["required"] = [
"property_id",
"date_ranges",
"dimensions",
"metrics",
"pivots",
]
elif tool.name == "batch_run_pivot_reports":
tool.inputSchema["required"] = [
"property_id",
"requests",
]
elif tool.name == "run_realtime_report":
tool.inputSchema["required"] = ["property_id", "dimensions", "metrics"]
elif tool.name == "run_conversions_report":
Expand All @@ -150,6 +212,18 @@ def sanitize_mcp_schema_properties(node: dict) -> None:
"metrics",
"conversion_spec",
]
elif tool.name == "create_audience_export":
tool.inputSchema["required"] = [
"property_id",
"audience_id",
"dimensions",
]
elif tool.name in ("validate_event", "send_event"):
tool.inputSchema["required"] = [
"measurement_id",
"client_id",
"events",
]


@app.list_tools()
Expand All @@ -158,7 +232,9 @@ async def list_tools() -> list[mcp_types.Tool]:


@app.call_tool()
async def call_mcp_tool(name: str, arguments: dict) -> list[mcp_types.Content]:
async def call_mcp_tool(
name: str, arguments: dict
) -> list[mcp_types.Content] | mcp_types.CallToolResult:
if name in tool_map:
tool = tool_map[name]
try:
Expand All @@ -176,13 +252,21 @@ async def call_mcp_tool(name: str, arguments: dict) -> list[mcp_types.Content]:
f"MCP Server: Error executing ADK tool '{name}': {e}",
file=sys.stderr,
)
# Return an error message in MCP format
# Return an error message in MCP format. isError=True so MCP
# clients can detect the failure programmatically instead of
# treating the error text as a successful response.
error_text = json.dumps(
{"error": f"Failed to execute tool '{name}': {str(e)}"}
)
return [mcp_types.TextContent(type="text", text=error_text)]
return mcp_types.CallToolResult(
content=[mcp_types.TextContent(type="text", text=error_text)],
isError=True,
)

error_text = json.dumps(
{"error": f"Tool '{name}' not implemented by this server."}
)
return [mcp_types.TextContent(type="text", text=error_text)]
return mcp_types.CallToolResult(
content=[mcp_types.TextContent(type="text", text=error_text)],
isError=True,
)
2 changes: 1 addition & 1 deletion analytics_mcp/tools/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def _get_package_version_with_fallback():
"""
try:
return metadata.version("analytics-mcp")
except:
except Exception:
return "unknown"


Expand Down
Loading
Loading