Skip to content

fix(server): drop the redundant inner traceback import (FR-174) - #212

Closed
Avicennasis wants to merge 11 commits into
googleanalytics:mainfrom
Avicennasis:fix/fr-174-traceback-import
Closed

Avicennasis wants to merge 11 commits into
googleanalytics:mainfrom
Avicennasis:fix/fr-174-traceback-import

Conversation

@Avicennasis

Copy link
Copy Markdown

Closes #48368. Removes the duplicate import traceback inside the except block. py_compile clean.

Audience exports (Data API v1beta, analytics.readonly-compatible):
create_audience_export starts the async export job and returns the
operation metadata for polling; get/list/query retrieve state and
user rows.

Measurement Protocol (plain HTTP, write-capable):
validate_event hits only the debug endpoint and never records data.
send_event is guarded for AI callers — the API secret comes solely
from the ANALYTICS_MCP_MP_API_SECRET env var, dry-run validation is
the default, recording requires an explicit confirm=True, and even
confirmed sends abort if validation fails.
Fixes from a stargazer review of the server:

- coordinator: call_mcp_tool now returns CallToolResult(content=[...],
  isError=True) on both failure paths (tool raised, and tool-not-found)
  instead of a plain TextContent that MCP clients read as a *successful*
  response. This is the one genuinely client-observable fix — clients can
  now detect failures programmatically. [FR-179]
- coordinator: drop the unused `from json import tool` import, which bound
  `tool` to the json.tool CLI module only for it to be shadowed by later
  `for tool in ...` loops. [FR-173]
- client: `except:` -> `except Exception:` in the package-version fallback
  so Ctrl-C / SystemExit are no longer swallowed into "unknown". [FR-175]
- reporting (core, realtime, audience_exports, conversions): replace
  truthy `if limit:` / `if offset:` / `if currency_code:` guards with
  `is not None`. NOTE: these RunReportRequest fields are proto3 scalars
  with no field presence, so offset=0 / limit=0 serialize identically to
  unset — this is a correctness/consistency cleanup and a guard against
  presence-aware fields, NOT a behavior change today (verified by
  comparing serialized requests). [FR-170, FR-171, FR-172, FR-181]

Verified: black --check clean; existing suite (19 tests) green; ad-hoc
harness confirms isError=True on both failure paths, the success path still
returns list[Content], and offset/limit=0 requests are wire-identical to
unset.
Rebuilt on top of current main rather than rebased — the original branch's
coordinator.py hunk conflicted through an unterminated import statement, so a
union merge produced nested open imports. Reapplied the branch's actual
additions (imports, three FunctionTool wrappers, three registry entries, three
inputSchema clauses) at the correct anchors in main's current file.

pivot.py, compatibility.py and their tests are unchanged from the original branch.
Reapplied onto main after #4 (pivot tools) merged — that touched the same
coordinator.py registry, so this branch conflicted again. Same four additions
at the same anchors; verified the pivot registrations from #4 are still intact
(6 references) rather than clobbered.

batch_test.py uses asyncio.run() to match the repo's other tests, and
black --check -l 80 is clean across all 27 files.
* feat: add list_key_events tool

Lists the key events (conversions) configured for a GA4 property via
the Admin API properties.keyEvents.list method. Read-only; fits the
existing analytics.readonly scope.

* feat: add list_data_streams tool

Lists the data streams (web, Android, iOS) configured for a GA4
property via the Admin API properties.dataStreams.list method,
including platform-specific details such as web measurement IDs.
Read-only; fits the existing analytics.readonly scope.

* feat: add get_metadata tool for the full dimension and metric catalog

Exposes the complete properties.getMetadata response — standard and
custom dimensions and metrics with descriptions, categories, and
deprecation status — so an LLM can discover available fields before
building a report. The existing get_custom_dimensions_and_metrics
tool is unchanged and still returns only custom definitions.

* feat: add list_properties tool

Lists the GA4 properties under an account via the Admin API
properties.list method with a parent account filter. Returns full
property objects (display name, industry, time zone, currency,
service level), complementing the summary info available from
get_account_summaries. Adds a construct_account_rn helper to utils
mirroring construct_property_rn.

* feat: add list_custom_dimensions and list_custom_metrics tools

Lists custom dimensions and metrics via the Admin API, which returns
richer detail than the Data API metadata endpoint: scope, parameter
name, description, measurement unit, and ads personalization /
restricted metric flags. Read-only; fits the existing
analytics.readonly scope.

* feat: add run_access_report tool

Runs a data access report via the Admin API runAccessReport method,
showing who accessed Analytics data, when, and how. Supports both
property-level and account-level entities. Read-only; the method
accepts the analytics.readonly scope.

* feat: add get_data_retention_settings tool

Retrieves a property's data retention settings via the Admin API,
showing how long event-level and user-level data is kept. Helps
answer compliance questions and explains why older data may be
missing from reports. Read-only; fits the existing
analytics.readonly scope.

* feat: add list_audiences tool

Lists the audiences defined on a GA4 property via the Admin API
alpha channel, following the same pattern as the existing
list_property_annotations tool. Read-only; fits the existing
analytics.readonly scope.

* feat: add get_property_quotas tool

Retrieves the current Data API quota snapshot for a property via the
alpha getPropertyQuotasSnapshot method, so callers can check token
consumption before running expensive batches and back off
intelligently. Read-only; fits the existing analytics.readonly
scope.

* fix: validate property ID before constructing the Data API client

get_metadata and get_custom_dimensions_and_metrics both built the client
in the same expression that validated the property ID:

    create_data_api_client().get_metadata(
        name=f"{construct_property_rn(property_id)}/metadata"
    )

Python evaluates create_data_api_client() first, so on any machine without
Application Default Credentials an invalid property ID surfaced as
google.auth.exceptions.DefaultCredentialsError instead of the ValueError
the docstring promises — which is what failed
metadata_test.TestGetMetadata.test_invalid_property_id_raises in CI.

Hoisting the resource-name construction validates the input before any
credential lookup. Verified by running the suite with ADC disabled
(CLOUDSDK_CONFIG pointed at an empty dir): the test fails with
DefaultCredentialsError before this change and passes after, 31/31 green.
traceback is imported at module top; the re-import inside the except block
is redundant.
@Avicennasis

Copy link
Copy Markdown
Author

Opened against the wrong base by mistake — this is an internal change to our fork (Avicennasis/google-analytics-mcp), not an upstream contribution. Closing.

@Avicennasis
Avicennasis deleted the fix/fr-174-traceback-import branch September 19, 2026 02:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant