mcp-ga4 1.0.1 → 2.0.0
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.
- package/LICENSE +21 -0
- package/README.md +131 -64
- package/config.example.json +10 -0
- package/dist/build-info.json +1 -0
- package/dist/errors.d.ts +3 -6
- package/dist/errors.js +17 -34
- package/dist/index.d.ts +0 -7
- package/dist/index.js +366 -221
- package/dist/resilience.d.ts +0 -3
- package/dist/resilience.js +5 -30
- package/dist/tools.d.ts +0 -3
- package/dist/tools.js +42 -141
- package/package.json +18 -24
- package/dist/auth.d.ts +0 -34
- package/dist/auth.js +0 -130
- package/dist/ga4.d.ts +0 -23
- package/dist/ga4.js +0 -151
- package/dist/setup.d.ts +0 -13
- package/dist/setup.js +0 -253
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 drak-marketing
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,102 +1,169 @@
|
|
|
1
1
|
# mcp-ga4
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server for Google Analytics 4 -- run reports, realtime data, custom dimensions, and property management via Claude.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- 9 tools covering reporting, realtime data, custom dimensions/metrics, data streams, and feedback
|
|
8
|
+
- Two configuration modes: single-property (env vars) and multi-client (config.json)
|
|
9
|
+
- Supports both service account and OAuth credentials
|
|
10
|
+
- Relative date support (today, yesterday, 7daysAgo, 30daysAgo, 90daysAgo)
|
|
11
|
+
- Built on official Google SDKs with resilience patterns
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
8
14
|
|
|
9
15
|
```bash
|
|
10
|
-
|
|
16
|
+
npm install mcp-ga4
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
19
|
+
Or clone the repository:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone https://github.com/drak-marketing/mcp-ga4.git
|
|
23
|
+
cd mcp-ga4
|
|
24
|
+
npm install
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
Click **Advanced** then **Go to mcp-ga4 (unsafe)** to continue. This is safe --
|
|
21
|
-
the app only requests read-only access to your analytics data.
|
|
28
|
+
## Configuration
|
|
22
29
|
|
|
23
|
-
###
|
|
30
|
+
### Mode 1: Single Property (env vars)
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
Set environment variables to connect to a single GA4 property:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
GA4_PROPERTY_ID=123456789
|
|
36
|
+
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
|
|
37
|
+
```
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
For OAuth credentials instead of a service account:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
GA4_PROPERTY_ID=123456789
|
|
43
|
+
GA4_CREDENTIALS_FILE=/path/to/oauth-credentials.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Mode 2: Multi-Client (config.json)
|
|
47
|
+
|
|
48
|
+
Create a `config.json` in the project root to map multiple GA4 properties to project directories. The server auto-detects which property to use based on the caller's working directory.
|
|
28
49
|
|
|
29
50
|
```json
|
|
30
51
|
{
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
52
|
+
"credentials_file": "/path/to/oauth-credentials.json",
|
|
53
|
+
"clients": {
|
|
54
|
+
"client-a": {
|
|
55
|
+
"name": "Client A",
|
|
56
|
+
"folder": "/path/to/client-a/project",
|
|
57
|
+
"property_id": "123456789"
|
|
58
|
+
},
|
|
59
|
+
"client-b": {
|
|
60
|
+
"name": "Client B",
|
|
61
|
+
"folder": "/path/to/client-b/project",
|
|
62
|
+
"property_id": "987654321"
|
|
37
63
|
}
|
|
38
64
|
}
|
|
39
65
|
}
|
|
40
66
|
```
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
- **Claude Code CLI:** `.mcp.json` in your project directory
|
|
68
|
+
## Usage
|
|
44
69
|
|
|
45
|
-
|
|
70
|
+
### Claude Code (.mcp.json)
|
|
46
71
|
|
|
47
|
-
|
|
72
|
+
Single-property mode:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"ga4": {
|
|
78
|
+
"command": "npx",
|
|
79
|
+
"args": ["mcp-ga4"],
|
|
80
|
+
"env": {
|
|
81
|
+
"GA4_PROPERTY_ID": "123456789",
|
|
82
|
+
"GOOGLE_APPLICATION_CREDENTIALS": "/path/to/credentials.json"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Multi-client mode:
|
|
48
90
|
|
|
49
|
-
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"ga4": {
|
|
95
|
+
"command": "node",
|
|
96
|
+
"args": ["/path/to/mcp-ga4/dist/index.js"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
50
101
|
|
|
51
|
-
|
|
52
|
-
- "Show me traffic by source for the past 30 days"
|
|
53
|
-
- "What's my bounce rate trend this month?"
|
|
54
|
-
- "Which campaigns drove the most conversions?"
|
|
55
|
-
- "What devices are my users on?"
|
|
56
|
-
- "Show me real-time active users"
|
|
102
|
+
## Common Query Patterns
|
|
57
103
|
|
|
58
|
-
|
|
104
|
+
**Top pages:**
|
|
105
|
+
dimensions=`pagePath`, metrics=`screenPageViews`, order_by=`screenPageViews`
|
|
59
106
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
be improved for everyone.
|
|
107
|
+
**Traffic sources:**
|
|
108
|
+
dimensions=`sessionSource,sessionMedium`, metrics=`sessions,totalUsers`
|
|
63
109
|
|
|
64
|
-
|
|
110
|
+
**Daily trend:**
|
|
111
|
+
dimensions=`date`, metrics=`sessions,totalUsers`
|
|
65
112
|
|
|
66
|
-
|
|
113
|
+
**Campaign performance:**
|
|
114
|
+
dimensions=`sessionCampaignName`, metrics=`sessions,conversions`
|
|
67
115
|
|
|
68
|
-
**
|
|
69
|
-
|
|
116
|
+
**Device breakdown:**
|
|
117
|
+
dimensions=`deviceCategory`, metrics=`sessions,totalUsers`
|
|
70
118
|
|
|
71
|
-
|
|
72
|
-
Your Google account may not have access to the GA4 property. Check your access at
|
|
73
|
-
GA4 > Admin > Account Access Management.
|
|
119
|
+
## Tools
|
|
74
120
|
|
|
75
|
-
|
|
76
|
-
|
|
121
|
+
| Tool | Description |
|
|
122
|
+
|------|-------------|
|
|
123
|
+
| `ga4_get_client_context` | Returns the active GA4 property ID and client name |
|
|
124
|
+
| `ga4_run_report` | Run a standard GA4 report with dimensions, metrics, date range, and filters |
|
|
125
|
+
| `ga4_realtime_report` | Query realtime data (last 30 minutes) |
|
|
126
|
+
| `ga4_list_custom_dimensions` | List all custom dimensions for the property |
|
|
127
|
+
| `ga4_create_custom_dimension` | Create a new custom dimension |
|
|
128
|
+
| `ga4_list_custom_metrics` | List all custom metrics for the property |
|
|
129
|
+
| `ga4_list_data_streams` | List web/app data streams and their measurement IDs |
|
|
130
|
+
| `ga4_send_feedback` | Submit feedback on a query result |
|
|
131
|
+
| `ga4_suggest_improvement` | Suggest a new query pattern or improvement |
|
|
77
132
|
|
|
78
|
-
##
|
|
133
|
+
## Date Formats
|
|
79
134
|
|
|
80
|
-
|
|
135
|
+
Use `YYYY-MM-DD` for absolute dates, or these relative shortcuts:
|
|
81
136
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
137
|
+
- `today`
|
|
138
|
+
- `yesterday`
|
|
139
|
+
- `7daysAgo`
|
|
140
|
+
- `30daysAgo`
|
|
141
|
+
- `90daysAgo`
|
|
142
|
+
|
|
143
|
+
## Common Dimensions and Metrics
|
|
144
|
+
|
|
145
|
+
**Dimensions:** date, dateHour, eventName, pagePath, pageTitle, sessionSource, sessionMedium, sessionCampaignName, country, city, deviceCategory, browser, operatingSystem, landingPage, pageReferrer, newVsReturning, firstUserSource, firstUserMedium, firstUserCampaignName
|
|
146
|
+
|
|
147
|
+
**Metrics:** sessions, totalUsers, newUsers, activeUsers, screenPageViews, eventCount, conversions, engagedSessions, engagementRate, averageSessionDuration, bounceRate, sessionsPerUser, screenPageViewsPerSession, userEngagementDuration
|
|
148
|
+
|
|
149
|
+
## Data Freshness
|
|
150
|
+
|
|
151
|
+
- Standard reports: 24-48 hour delay
|
|
152
|
+
- Realtime reports: last 30 minutes only
|
|
153
|
+
|
|
154
|
+
## Architecture
|
|
155
|
+
|
|
156
|
+
Built on:
|
|
157
|
+
|
|
158
|
+
- `@google-analytics/data` -- GA4 Data API for reports
|
|
159
|
+
- `@google-analytics/admin` -- GA4 Admin API for property management
|
|
160
|
+
- `cockatiel` -- resilience (retry, circuit breaker)
|
|
161
|
+
- `pino` -- structured logging
|
|
99
162
|
|
|
100
163
|
## License
|
|
101
164
|
|
|
102
165
|
MIT
|
|
166
|
+
|
|
167
|
+
## Author
|
|
168
|
+
|
|
169
|
+
Built by Mark Harnett / [drak-marketing](https://github.com/drak-marketing)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"sha":"38406d3","builtAt":"2026-04-03T23:26:52.380Z"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
* Typed errors and classification for GA4 API calls.
|
|
3
|
-
*/
|
|
4
|
-
export declare class GA4AuthError extends Error {
|
|
1
|
+
export declare class Ga4AuthError extends Error {
|
|
5
2
|
readonly cause?: unknown | undefined;
|
|
6
3
|
constructor(message: string, cause?: unknown | undefined);
|
|
7
4
|
}
|
|
8
|
-
export declare class
|
|
5
|
+
export declare class Ga4RateLimitError extends Error {
|
|
9
6
|
readonly retryAfterMs: number;
|
|
10
7
|
constructor(retryAfterMs: number, cause?: unknown);
|
|
11
8
|
}
|
|
12
|
-
export declare class
|
|
9
|
+
export declare class Ga4ServiceError extends Error {
|
|
13
10
|
readonly cause?: unknown | undefined;
|
|
14
11
|
constructor(message: string, cause?: unknown | undefined);
|
|
15
12
|
}
|
package/dist/errors.js
CHANGED
|
@@ -1,59 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
* Typed errors and classification for GA4 API calls.
|
|
3
|
-
*/
|
|
4
|
-
export class GA4AuthError extends Error {
|
|
1
|
+
export class Ga4AuthError extends Error {
|
|
5
2
|
cause;
|
|
6
3
|
constructor(message, cause) {
|
|
7
4
|
super(message);
|
|
8
5
|
this.cause = cause;
|
|
9
|
-
this.name = "
|
|
6
|
+
this.name = "Ga4AuthError";
|
|
10
7
|
}
|
|
11
8
|
}
|
|
12
|
-
export class
|
|
9
|
+
export class Ga4RateLimitError extends Error {
|
|
13
10
|
retryAfterMs;
|
|
14
11
|
constructor(retryAfterMs, cause) {
|
|
15
12
|
super(`Rate limited, retry after ${retryAfterMs}ms`);
|
|
16
|
-
this.name = "GA4RateLimitError";
|
|
17
13
|
this.retryAfterMs = retryAfterMs;
|
|
14
|
+
this.name = "Ga4RateLimitError";
|
|
18
15
|
this.cause = cause;
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
|
-
export class
|
|
18
|
+
export class Ga4ServiceError extends Error {
|
|
22
19
|
cause;
|
|
23
20
|
constructor(message, cause) {
|
|
24
21
|
super(message);
|
|
25
22
|
this.cause = cause;
|
|
26
|
-
this.name = "
|
|
23
|
+
this.name = "Ga4ServiceError";
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
export function classifyError(error) {
|
|
30
|
-
const message =
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
message.
|
|
36
|
-
message.includes("Token has been expired") ||
|
|
37
|
-
message.includes("refresh token") ||
|
|
38
|
-
message.includes("UNAUTHENTICATED") ||
|
|
39
|
-
message.includes("PERMISSION_DENIED")) {
|
|
40
|
-
return new GA4AuthError(`Auth failed: ${message}. Re-run mcp-ga4-setup to refresh credentials.`, error);
|
|
27
|
+
const message = error?.message || String(error);
|
|
28
|
+
const code = error?.code || error?.status;
|
|
29
|
+
if (code === 401 || code === 403 || code === 7 || code === 16 ||
|
|
30
|
+
message.includes("PERMISSION_DENIED") || message.includes("UNAUTHENTICATED") ||
|
|
31
|
+
message.includes("invalid_grant")) {
|
|
32
|
+
return new Ga4AuthError(`Auth failed: ${message}. Check credentials.`, error);
|
|
41
33
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const retryMs = 60_000;
|
|
45
|
-
return new GA4RateLimitError(retryMs, error);
|
|
34
|
+
if (code === 429 || code === 8 || message.includes("RESOURCE_EXHAUSTED") || message.includes("rateLimitExceeded")) {
|
|
35
|
+
return new Ga4RateLimitError(60_000, error);
|
|
46
36
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return new GA4ServiceError(`GA4 API server error: ${message}`, error);
|
|
51
|
-
}
|
|
52
|
-
if (!(error instanceof Error)) {
|
|
53
|
-
const wrapped = new Error(message);
|
|
54
|
-
wrapped.name = "GA4Error";
|
|
55
|
-
wrapped.cause = error;
|
|
56
|
-
return wrapped;
|
|
37
|
+
if (code >= 500 || code === 13 || code === 14 ||
|
|
38
|
+
message.includes("INTERNAL") || message.includes("UNAVAILABLE")) {
|
|
39
|
+
return new Ga4ServiceError(`GA4 API server error: ${message}`, error);
|
|
57
40
|
}
|
|
58
41
|
return error;
|
|
59
42
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* mcp-ga4 -- MCP server for querying Google Analytics 4 via natural language.
|
|
4
|
-
*
|
|
5
|
-
* Supports two config modes:
|
|
6
|
-
* 1. Single-property (env vars): GA4_PROPERTY_ID + GOOGLE_APPLICATION_CREDENTIALS
|
|
7
|
-
* 2. Multi-client (config.json): MCP_GA4_CONFIG or ~/.config/mcp-ga4/config.json
|
|
8
|
-
*/
|
|
9
2
|
export {};
|