google-analytics-mcp 2.7.2__tar.gz → 2.7.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/PKG-INFO +1 -1
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/coordinator.py +15 -2
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/resources.py +19 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/server.py +3 -1
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/setup_flow.py +45 -3
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/telemetry.py +36 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/tools/reporting.py +5 -3
- google_analytics_mcp-2.7.3/ga4_mcp/tools/troubleshooting.py +73 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/PKG-INFO +1 -1
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/pyproject.toml +1 -1
- google_analytics_mcp-2.7.2/ga4_mcp/tools/troubleshooting.py +0 -28
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/LICENSE +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/README.md +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/__init__.py +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/__main__.py +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/ga4_mcp/tools/metadata.py +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/SOURCES.txt +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/dependency_links.txt +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/entry_points.txt +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/requires.txt +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/top_level.txt +0 -0
- {google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-analytics-mcp
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
4
4
|
Summary: Model Context Protocol for Google Analytics 4 (Data API) allowing autonomous agents to query dimensions and metrics. Gives agents analysis-ready GA4 access with schema discovery, server-side aggregation, and smart defaults.
|
|
5
5
|
Author-email: Surendran B <reachsuren@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -126,8 +126,20 @@ def _telemetry_tool(*args, **kwargs):
|
|
|
126
126
|
is_async = inspect.iscoroutinefunction(func)
|
|
127
127
|
|
|
128
128
|
def _intercept(name):
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
if not SERVER_INIT_ERROR or name in _INIT_ERROR_EXEMPT:
|
|
130
|
+
return None
|
|
131
|
+
# Agent-directed recovery playbook, not a description. The old wording
|
|
132
|
+
# ("Configuration Error: ...") read as retryable, so agents re-called
|
|
133
|
+
# the data tool in a loop. Lead with STOP + category, then the fix.
|
|
134
|
+
lead = {
|
|
135
|
+
"ADCExpired": "The user's Google credentials have EXPIRED (a returning-user auth issue, not a query problem).",
|
|
136
|
+
"IAMError": "The service account lacks access to this GA4 property (a permissions grant is missing).",
|
|
137
|
+
"InitError": "The GA4 MCP server is not configured yet (missing Property ID or credentials).",
|
|
138
|
+
}.get(SERVER_INIT_ERROR_CATEGORY,
|
|
139
|
+
"The GA4 MCP server setup is incomplete.")
|
|
140
|
+
return (f"STOP — do not retry this tool; it will fail identically until setup is fixed. {lead} "
|
|
141
|
+
f"NEXT ACTION: call setup_ga4_access now — it fixes this interactively and reconnects "
|
|
142
|
+
f"without a restart. If that is unavailable, relay this to the user. Details: {SERVER_INIT_ERROR}")
|
|
131
143
|
|
|
132
144
|
if is_async:
|
|
133
145
|
@functools.wraps(func)
|
|
@@ -243,6 +255,7 @@ def reinitialize():
|
|
|
243
255
|
metadata.PROPERTY_SCHEMA = schema
|
|
244
256
|
reporting.PROPERTY_SCHEMA = schema
|
|
245
257
|
SERVER_INIT_ERROR = None
|
|
258
|
+
telemetry.mark_ever_worked()
|
|
246
259
|
return True, "ok", "initialized"
|
|
247
260
|
except Exception as e:
|
|
248
261
|
err = str(e)
|
|
@@ -52,3 +52,22 @@ def get_setup_guide() -> str:
|
|
|
52
52
|
"""Provides instructions to the agent on how to heal the human's MCP setup."""
|
|
53
53
|
send_telemetry("resource_read", {"resource_uri": "docs://setup_guide"})
|
|
54
54
|
return SETUP_GUIDE_MD
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Category-addressed fix guides, same content the get_troubleshooting_guide tool
|
|
58
|
+
# serves — exposed as resources for clients that surface resources to the model.
|
|
59
|
+
# One source of truth (tools.troubleshooting._GUIDES); progressive enhancement
|
|
60
|
+
# on top of the always-reaches error playbook.
|
|
61
|
+
from .tools.troubleshooting import _GUIDES as _FIX_GUIDES
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _make_fix_resource(topic):
|
|
65
|
+
def _read() -> str:
|
|
66
|
+
send_telemetry("resource_read", {"resource_uri": f"docs://fix/{topic}"})
|
|
67
|
+
return _FIX_GUIDES[topic]
|
|
68
|
+
_read.__name__ = f"get_fix_{topic}"
|
|
69
|
+
return mcp.resource(f"docs://fix/{topic}")(_read)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
for _t in _FIX_GUIDES:
|
|
73
|
+
_make_fix_resource(_t)
|
|
@@ -21,7 +21,8 @@ def main():
|
|
|
21
21
|
"""
|
|
22
22
|
print("Starting GA4 MCP server...", file=sys.stderr)
|
|
23
23
|
import ga4_mcp.coordinator as coordinator
|
|
24
|
-
|
|
24
|
+
from ga4_mcp import telemetry
|
|
25
|
+
import ga4_mcp.tools.troubleshooting # Register the troubleshooting tool
|
|
25
26
|
import ga4_mcp.setup_flow # Register the interactive setup-recovery tool
|
|
26
27
|
config_status = "valid"
|
|
27
28
|
|
|
@@ -97,6 +98,7 @@ def main():
|
|
|
97
98
|
try:
|
|
98
99
|
PROPERTY_SCHEMA = metadata.get_property_schema_uncached(property_id)
|
|
99
100
|
print("Schema loaded successfully.", file=sys.stderr)
|
|
101
|
+
telemetry.mark_ever_worked()
|
|
100
102
|
except Exception as e:
|
|
101
103
|
print(f"FATAL: Could not fetch GA4 property schema: {e}", file=sys.stderr)
|
|
102
104
|
err_str = str(e)
|
|
@@ -6,14 +6,44 @@ session, avoiding a client restart. Falls back to guided text if the client
|
|
|
6
6
|
doesn't support elicitation."""
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
+
import uuid
|
|
9
10
|
|
|
10
11
|
from pydantic import BaseModel, Field
|
|
11
12
|
from mcp.server.fastmcp import Context
|
|
12
13
|
|
|
13
14
|
from .coordinator import mcp
|
|
14
|
-
from .telemetry import send_telemetry
|
|
15
|
+
from .telemetry import send_telemetry, client_supports_url_elicitation
|
|
15
16
|
from . import coordinator
|
|
16
17
|
|
|
18
|
+
# Pages we own or that land the user exactly where the fix happens. URL-mode
|
|
19
|
+
# elicitation opens these at the client; no OAuth, no secrets — guided navigation.
|
|
20
|
+
_GA4_ADMIN_URL = "https://analytics.google.com/analytics/web/#/admin"
|
|
21
|
+
_SETUP_PAGE_URL = "https://ga4.builditwithai.xyz/setup"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def _offer_page(ctx, message, url, branch):
|
|
25
|
+
"""URL-mode elicitation: ask the client to open a helpful page before we
|
|
26
|
+
collect the value. No-op on clients without URL elicitation (falls through
|
|
27
|
+
to the existing form/text). Records whether the offer was accepted."""
|
|
28
|
+
if not client_supports_url_elicitation():
|
|
29
|
+
return None
|
|
30
|
+
try:
|
|
31
|
+
r = await ctx.request_context.session.elicit_url(
|
|
32
|
+
message=message,
|
|
33
|
+
url=url,
|
|
34
|
+
elicitation_id=f"setup_{uuid.uuid4().hex[:12]}",
|
|
35
|
+
related_request_id=ctx.request_id,
|
|
36
|
+
)
|
|
37
|
+
action = str(getattr(r, "action", None))
|
|
38
|
+
send_telemetry("setup_flow", {
|
|
39
|
+
"flow_branch": branch, "flow_outcome": "url_offered",
|
|
40
|
+
"url_elicit_action": action,
|
|
41
|
+
"error_category_at_entry": coordinator.SERVER_INIT_ERROR_CATEGORY,
|
|
42
|
+
})
|
|
43
|
+
return action
|
|
44
|
+
except Exception:
|
|
45
|
+
return None
|
|
46
|
+
|
|
17
47
|
|
|
18
48
|
class _PropertyId(BaseModel):
|
|
19
49
|
property_id: str = Field(description="Your numeric GA4 Property ID, e.g. 123456789")
|
|
@@ -62,9 +92,15 @@ async def setup_ga4_access(ctx: Context) -> str:
|
|
|
62
92
|
branch = "other"
|
|
63
93
|
|
|
64
94
|
try:
|
|
65
|
-
# Missing Property ID — collect it.
|
|
95
|
+
# Missing Property ID — offer to open GA4 Admin, then collect it.
|
|
66
96
|
if not prop:
|
|
67
97
|
branch = "property_id"
|
|
98
|
+
await _offer_page(
|
|
99
|
+
ctx,
|
|
100
|
+
"I'll open Google Analytics Admin so you can copy your Property ID "
|
|
101
|
+
"(Admin > Property details, a numeric id like 123456789).",
|
|
102
|
+
_GA4_ADMIN_URL, "property_id_open_admin",
|
|
103
|
+
)
|
|
68
104
|
r = await ctx.elicit(
|
|
69
105
|
"What is your GA4 Property ID? Find it at analytics.google.com > Admin > "
|
|
70
106
|
"Property details (a numeric id like 123456789).",
|
|
@@ -75,9 +111,15 @@ async def setup_ga4_access(ctx: Context) -> str:
|
|
|
75
111
|
return "Setup paused — no Property ID provided. Re-run setup_ga4_access when ready."
|
|
76
112
|
os.environ["GA4_PROPERTY_ID"] = r.data.property_id.strip()
|
|
77
113
|
|
|
78
|
-
# Missing/invalid credentials path — collect
|
|
114
|
+
# Missing/invalid credentials path — offer the setup page, then collect.
|
|
79
115
|
elif not creds or (creds and not os.path.exists(creds)):
|
|
80
116
|
branch = "credentials"
|
|
117
|
+
await _offer_page(
|
|
118
|
+
ctx,
|
|
119
|
+
"I'll open the setup guide — it walks through creating a service-account "
|
|
120
|
+
"key or using gcloud, with the exact steps.",
|
|
121
|
+
_SETUP_PAGE_URL, "credentials_open_setup",
|
|
122
|
+
)
|
|
81
123
|
r = await ctx.elicit(
|
|
82
124
|
"Where is your Google service-account JSON key on this machine? "
|
|
83
125
|
"Paste its absolute path. (Or, to use gcloud instead, run "
|
|
@@ -72,6 +72,30 @@ def _init_anonymous_identity():
|
|
|
72
72
|
INSTALLATION_ID, IS_FIRST_INSTALL = _init_anonymous_identity()
|
|
73
73
|
SESSION_ID = f"sess_{uuid.uuid4()}" # one per process
|
|
74
74
|
|
|
75
|
+
|
|
76
|
+
def _has_ever_worked() -> bool:
|
|
77
|
+
"""True if this install successfully initialized in a PRIOR session. Lets a
|
|
78
|
+
query tell a first-time setup failure (never worked) from a returning-user
|
|
79
|
+
credential decay (worked before, broke since). Bool only, non-PII."""
|
|
80
|
+
try:
|
|
81
|
+
return (Path.home() / ".ga4_mcp" / "ever_worked").exists()
|
|
82
|
+
except Exception:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
HAS_EVER_WORKED = _has_ever_worked()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def mark_ever_worked():
|
|
90
|
+
"""Write the 'has successfully worked' marker once, on first successful init.
|
|
91
|
+
Additive to the frozen identity contract — a separate flag file, not the id."""
|
|
92
|
+
try:
|
|
93
|
+
f = Path.home() / ".ga4_mcp" / "ever_worked"
|
|
94
|
+
if not f.exists():
|
|
95
|
+
f.write_text("1", encoding="utf-8")
|
|
96
|
+
except Exception:
|
|
97
|
+
pass
|
|
98
|
+
|
|
75
99
|
IN_VIRTUAL_ENV = sys.prefix != sys.base_prefix
|
|
76
100
|
CPU_ARCH = platform.machine()
|
|
77
101
|
TIMEZONE_OFFSET = -time.timezone if (time.localtime().tm_isdst == 0) else -time.altzone
|
|
@@ -333,6 +357,17 @@ def capture_client_info(mcp_server):
|
|
|
333
357
|
pass
|
|
334
358
|
|
|
335
359
|
|
|
360
|
+
def client_supports_url_elicitation() -> bool:
|
|
361
|
+
"""True if the handshake advertised URL-mode elicitation (elicitation.url).
|
|
362
|
+
Read from the raw capabilities we capture; used to offer guided-navigation
|
|
363
|
+
recovery only to clients that can open a URL."""
|
|
364
|
+
caps = _RUNTIME_CLIENT.get("caps_raw")
|
|
365
|
+
if not isinstance(caps, dict):
|
|
366
|
+
return False
|
|
367
|
+
elicit = caps.get("elicitation")
|
|
368
|
+
return isinstance(elicit, dict) and "url" in elicit
|
|
369
|
+
|
|
370
|
+
|
|
336
371
|
# In-flight sender threads, drained briefly at exit — short-lived sessions
|
|
337
372
|
# (a large share of real boots) otherwise lose their events to process death.
|
|
338
373
|
_PENDING_SENDS = []
|
|
@@ -374,6 +409,7 @@ def send_telemetry(event: str, properties: dict = None):
|
|
|
374
409
|
"run_context": RUN_CONTEXT,
|
|
375
410
|
"discovery_channel": DISCOVERY_CHANNEL,
|
|
376
411
|
"launch_channel": DISCOVERY_CHANNEL,
|
|
412
|
+
"has_ever_worked": HAS_EVER_WORKED,
|
|
377
413
|
"raw_env": ENV_SIGNALS, # the raw clues behind run_context/agent_name
|
|
378
414
|
"session_id": SESSION_ID,
|
|
379
415
|
**(properties or {}),
|
|
@@ -172,9 +172,11 @@ def get_ga4_data(
|
|
|
172
172
|
warning and execute the query anyway.
|
|
173
173
|
enable_aggregation: (Optional) If True, uses server-side aggregation when
|
|
174
174
|
beneficial. Defaults to True.
|
|
175
|
-
intent: (Optional)
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
intent: (Optional) A short, plain-language description of what the user is
|
|
176
|
+
trying to learn from this query — in your own words, not a fixed
|
|
177
|
+
category. Examples: "weekly retention trend for release planning",
|
|
178
|
+
"check for bot traffic from non-Japan countries",
|
|
179
|
+
"which signup flow converts best".
|
|
178
180
|
|
|
179
181
|
COMMON MISTAKES (checked against real usage — read before guessing names):
|
|
180
182
|
- Metric is 'keyEvents', not 'conversions'. 'totalUsers', not 'users'.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""In-server troubleshooting guides. Content lives with the code (bundled at
|
|
4
|
+
release), NOT fetched from the web — instant, offline-safe, and versioned with
|
|
5
|
+
the package. Written for the AI agent to act on and relay to the user."""
|
|
6
|
+
|
|
7
|
+
from ga4_mcp.coordinator import mcp
|
|
8
|
+
|
|
9
|
+
_SETUP_GUIDE = """--- SETUP GUIDE ---
|
|
10
|
+
|
|
11
|
+
The GA4 MCP server needs two things to work: a Property ID and Google credentials.
|
|
12
|
+
|
|
13
|
+
1. GA4_PROPERTY_ID — the numeric Property ID (e.g. 123456789), NOT the
|
|
14
|
+
Measurement ID that starts with 'G-'. Find it at analytics.google.com >
|
|
15
|
+
Admin > Property details.
|
|
16
|
+
2. GOOGLE_APPLICATION_CREDENTIALS — the absolute path to a Google Cloud
|
|
17
|
+
service-account JSON key, OR run 'gcloud auth application-default login'
|
|
18
|
+
and use that credentials file.
|
|
19
|
+
|
|
20
|
+
Set both in the env block of this server in the MCP client config, e.g.:
|
|
21
|
+
"env": {
|
|
22
|
+
"GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/key.json",
|
|
23
|
+
"GA4_PROPERTY_ID": "123456789"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
FASTEST PATH: call setup_ga4_access — it collects the missing value
|
|
27
|
+
interactively and reconnects without a restart."""
|
|
28
|
+
|
|
29
|
+
_IAM_GUIDE = """--- IAM / 403 PERMISSION GUIDE ---
|
|
30
|
+
|
|
31
|
+
A 403 / PermissionDenied means the credentials are valid but the service
|
|
32
|
+
account has no access to the GA4 property. Grant it:
|
|
33
|
+
|
|
34
|
+
1. Open analytics.google.com > Admin > Property Access Management.
|
|
35
|
+
2. Click '+' > Add users.
|
|
36
|
+
3. Enter the service account email — it is the 'client_email' field inside
|
|
37
|
+
the JSON key file.
|
|
38
|
+
4. Assign the 'Viewer' role and save.
|
|
39
|
+
5. Wait a minute for permissions to propagate, then retry.
|
|
40
|
+
|
|
41
|
+
If credentials instead need re-authentication (a 'Reauthentication is needed'
|
|
42
|
+
or invalid_grant / expired error), the user's Application Default Credentials
|
|
43
|
+
have expired — have them run: gcloud auth application-default login"""
|
|
44
|
+
|
|
45
|
+
_SCHEMA_GUIDE = """--- SCHEMA GUIDE ---
|
|
46
|
+
|
|
47
|
+
Do not guess dimension/metric names — use search_schema first. Common fixes
|
|
48
|
+
(verified against real usage):
|
|
49
|
+
- Metric is 'keyEvents', not 'conversions'. 'totalUsers', not 'users'.
|
|
50
|
+
'itemsViewed', not 'itemViews'. 'ecommercePurchases', not 'purchases'.
|
|
51
|
+
- Dimension is 'sessionDefaultChannelGroup', not 'sessionDefaultChannelGrouping'.
|
|
52
|
+
- A simple dimension_filter MUST nest inside a "filter" key:
|
|
53
|
+
{"filter": {"fieldName": ..., "stringFilter": {...}}} — not fieldName at top level.
|
|
54
|
+
- Logical groups are "andGroup"/"orGroup"/"notExpression" — not and_filter/or_filter."""
|
|
55
|
+
|
|
56
|
+
_GUIDES = {"setup": _SETUP_GUIDE, "iam": _IAM_GUIDE, "schema": _SCHEMA_GUIDE}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@mcp.tool()
|
|
60
|
+
def get_troubleshooting_guide(topic: str) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Returns the troubleshooting/setup guide for a topic, served from inside the
|
|
63
|
+
server (no network needed). Use whenever you hit a schema error,
|
|
64
|
+
dimension_filter parse error, IAM / 403 authorization error, or a
|
|
65
|
+
boot-time setup error.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
topic: One of "setup", "iam", or "schema".
|
|
69
|
+
"""
|
|
70
|
+
guide = _GUIDES.get((topic or "").strip().lower())
|
|
71
|
+
if guide is None:
|
|
72
|
+
return "Invalid topic. Choose 'setup', 'iam', or 'schema'."
|
|
73
|
+
return guide
|
{google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-analytics-mcp
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
4
4
|
Summary: Model Context Protocol for Google Analytics 4 (Data API) allowing autonomous agents to query dimensions and metrics. Gives agents analysis-ready GA4 access with schema discovery, server-side aggregation, and smart defaults.
|
|
5
5
|
Author-email: Surendran B <reachsuren@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "google-analytics-mcp"
|
|
7
|
-
version = "2.7.
|
|
7
|
+
version = "2.7.3"
|
|
8
8
|
description = "Model Context Protocol for Google Analytics 4 (Data API) allowing autonomous agents to query dimensions and metrics. Gives agents analysis-ready GA4 access with schema discovery, server-side aggregation, and smart defaults."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import urllib.request
|
|
2
|
-
import urllib.error
|
|
3
|
-
from ga4_mcp.coordinator import mcp
|
|
4
|
-
|
|
5
|
-
# Fetches troubleshooting guides directly from the documentation site.
|
|
6
|
-
OTA_BASE_URL = "https://ga4mcp.com"
|
|
7
|
-
|
|
8
|
-
@mcp.tool()
|
|
9
|
-
def get_troubleshooting_guide(topic: str) -> str:
|
|
10
|
-
"""
|
|
11
|
-
Fetches the latest troubleshooting and setup guides Over-The-Air (OTA) from ga4mcp.com.
|
|
12
|
-
Use this tool whenever you encounter a schema error, dimension_filter parse error,
|
|
13
|
-
IAM / 403 authorization error, or boot-time setup error.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
topic: The topic of the guide to fetch. Valid options are "setup", "iam", or "schema".
|
|
17
|
-
"""
|
|
18
|
-
if topic not in ["setup", "iam", "schema"]:
|
|
19
|
-
return "Invalid topic. Please choose 'setup', 'iam', or 'schema'."
|
|
20
|
-
|
|
21
|
-
url = f"{OTA_BASE_URL}/{topic}.md"
|
|
22
|
-
try:
|
|
23
|
-
with urllib.request.urlopen(url, timeout=10) as response:
|
|
24
|
-
content = response.read().decode('utf-8')
|
|
25
|
-
return f"--- {topic.upper()} GUIDE ---\n\n{content}"
|
|
26
|
-
except urllib.error.URLError as e:
|
|
27
|
-
# Fallback if network is completely unreachable (rare, since GA4 needs network anyway)
|
|
28
|
-
return f"Warning: Could not fetch OTA guide for '{topic}' due to network error: {e}. Please use your best judgment based on generic GA4 API documentation."
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_analytics_mcp-2.7.2 → google_analytics_mcp-2.7.3}/google_analytics_mcp.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|