mcp-ga4 1.0.1 → 1.1.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/README.md +6 -20
- package/dist/ga4.d.ts +1 -0
- package/dist/ga4.js +33 -0
- package/dist/index.js +5 -0
- package/dist/setup.js +64 -29
- package/dist/tools.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,29 +20,15 @@ This will:
|
|
|
20
20
|
Click **Advanced** then **Go to mcp-ga4 (unsafe)** to continue. This is safe --
|
|
21
21
|
the app only requests read-only access to your analytics data.
|
|
22
22
|
|
|
23
|
-
### 2.
|
|
23
|
+
### 2. Restart Claude Code
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The setup wizard automatically registers the GA4 server with Claude Code.
|
|
26
|
+
Just restart Claude Code to pick it up:
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
- **CLI:** Exit and reopen `claude`
|
|
29
|
+
- **Desktop app:** Quit and reopen the app
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
"ga4": {
|
|
32
|
-
"command": "npx",
|
|
33
|
-
"args": ["-y", "mcp-ga4"],
|
|
34
|
-
"env": {
|
|
35
|
-
"GA4_PROPERTY_ID": "YOUR_PROPERTY_ID",
|
|
36
|
-
"GOOGLE_APPLICATION_CREDENTIALS": "~/.config/mcp-ga4/credentials.json"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
- **Claude Desktop (Mac):** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
43
|
-
- **Claude Code CLI:** `.mcp.json` in your project directory
|
|
44
|
-
|
|
45
|
-
That's it. No Python, no pip, no venv. Just `npx`.
|
|
31
|
+
That's it. No config files to edit, no JSON to paste. Just `npx` and restart.
|
|
46
32
|
|
|
47
33
|
## Usage
|
|
48
34
|
|
package/dist/ga4.d.ts
CHANGED
|
@@ -19,5 +19,6 @@ export declare class GA4Manager {
|
|
|
19
19
|
listDataStreams(propertyId: string): Promise<any>;
|
|
20
20
|
listCustomDimensions(propertyId: string): Promise<any>;
|
|
21
21
|
createCustomDimension(propertyId: string, parameterName: string, displayName: string, scope: string, description: string): Promise<any>;
|
|
22
|
+
healthCheck(propertyId: string): Promise<any>;
|
|
22
23
|
listCustomMetrics(propertyId: string): Promise<any>;
|
|
23
24
|
}
|
package/dist/ga4.js
CHANGED
|
@@ -118,6 +118,39 @@ export class GA4Manager {
|
|
|
118
118
|
scope: result.scope,
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
+
async healthCheck(propertyId) {
|
|
122
|
+
const checks = {};
|
|
123
|
+
// Check 1: Credentials file
|
|
124
|
+
try {
|
|
125
|
+
const { readFileSync } = await import("fs");
|
|
126
|
+
readFileSync(this.credentialsFile, "utf-8");
|
|
127
|
+
checks.credentials_file = "ok";
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
checks.credentials_file = `FAIL: ${err.message}`;
|
|
131
|
+
return { status: "unhealthy", checks };
|
|
132
|
+
}
|
|
133
|
+
// Check 2: Token refresh
|
|
134
|
+
try {
|
|
135
|
+
const token = await getAccessToken(this.credentialsFile);
|
|
136
|
+
checks.token_refresh = token ? "ok" : "FAIL: no token returned";
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
checks.token_refresh = `FAIL: ${err.message}`;
|
|
140
|
+
return { status: "unhealthy", checks };
|
|
141
|
+
}
|
|
142
|
+
// Check 3: GA4 API access
|
|
143
|
+
try {
|
|
144
|
+
const data = await this.request(`${ADMIN_API}/properties/${propertyId}/dataStreams`);
|
|
145
|
+
const count = (data.dataStreams || []).length;
|
|
146
|
+
checks.ga4_api = `ok (${count} data stream${count !== 1 ? "s" : ""})`;
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
checks.ga4_api = `FAIL: ${err.message}`;
|
|
150
|
+
return { status: "unhealthy", checks };
|
|
151
|
+
}
|
|
152
|
+
return { status: "healthy", property_id: propertyId, checks };
|
|
153
|
+
}
|
|
121
154
|
async listCustomMetrics(propertyId) {
|
|
122
155
|
const data = await withResilience(() => this.request(`${ADMIN_API}/properties/${propertyId}/customMetrics`), "listCustomMetrics");
|
|
123
156
|
const metrics = (data.customMetrics || []).map((m) => ({
|
package/dist/index.js
CHANGED
|
@@ -205,6 +205,11 @@ async function main() {
|
|
|
205
205
|
const result = await ga4.listCustomMetrics(args?.property_id);
|
|
206
206
|
return text(result);
|
|
207
207
|
}
|
|
208
|
+
// ---- Health Check ----
|
|
209
|
+
case "ga4_health_check": {
|
|
210
|
+
const result = await ga4.healthCheck(args?.property_id);
|
|
211
|
+
return text(result);
|
|
212
|
+
}
|
|
208
213
|
// ---- Feedback ----
|
|
209
214
|
case "ga4_send_feedback": {
|
|
210
215
|
const feedbackType = args?.feedback_type;
|
package/dist/setup.js
CHANGED
|
@@ -14,7 +14,8 @@ import { createInterface } from "readline";
|
|
|
14
14
|
import { createServer } from "http";
|
|
15
15
|
import { mkdirSync, writeFileSync, readFileSync, chmodSync } from "fs";
|
|
16
16
|
import { join } from "path";
|
|
17
|
-
import { homedir
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { execSync } from "child_process";
|
|
18
19
|
import { URL } from "url";
|
|
19
20
|
const CONFIG_DIR = join(homedir(), ".config", "mcp-ga4");
|
|
20
21
|
const CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
@@ -192,35 +193,60 @@ async function verifyConnection(propertyId, credsFile) {
|
|
|
192
193
|
return false;
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
|
-
function
|
|
196
|
-
printHeader("Step 4: Claude Code
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
196
|
+
function registerWithClaude(propertyId, credsFile) {
|
|
197
|
+
printHeader("Step 4: Registering with Claude Code");
|
|
198
|
+
// Try claude mcp add (works if Claude Code CLI is installed)
|
|
199
|
+
try {
|
|
200
|
+
execSync("claude --version", { stdio: "pipe" });
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// claude CLI not found -- fall back to manual instructions
|
|
204
|
+
console.log("Claude Code CLI not detected. Add this to your .mcp.json manually:\n");
|
|
205
|
+
const mcpConfig = {
|
|
206
|
+
ga4: {
|
|
207
|
+
command: "npx",
|
|
208
|
+
args: ["-y", "mcp-ga4"],
|
|
209
|
+
env: {
|
|
210
|
+
GA4_PROPERTY_ID: propertyId,
|
|
211
|
+
GOOGLE_APPLICATION_CREDENTIALS: credsFile,
|
|
212
|
+
},
|
|
204
213
|
},
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (platform() === "darwin") {
|
|
209
|
-
const configPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
210
|
-
console.log(` File: ${configPath}`);
|
|
211
|
-
console.log(" (Or for Claude Code CLI: add to .mcp.json in your project)\n");
|
|
214
|
+
};
|
|
215
|
+
console.log(JSON.stringify({ mcpServers: mcpConfig }, null, 2));
|
|
216
|
+
return false;
|
|
212
217
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
// Remove existing ga4 server if present (ignore errors)
|
|
219
|
+
try {
|
|
220
|
+
execSync("claude mcp remove ga4 -s user", { stdio: "pipe" });
|
|
216
221
|
}
|
|
217
|
-
|
|
218
|
-
|
|
222
|
+
catch {
|
|
223
|
+
// not present, that's fine
|
|
224
|
+
}
|
|
225
|
+
const cmd = `claude mcp add -s user` +
|
|
226
|
+
` -e GA4_PROPERTY_ID=${propertyId}` +
|
|
227
|
+
` -e GOOGLE_APPLICATION_CREDENTIALS=${credsFile}` +
|
|
228
|
+
` ga4 -- npx -y mcp-ga4`;
|
|
229
|
+
try {
|
|
230
|
+
execSync(cmd, { stdio: "inherit" });
|
|
231
|
+
console.log("\nGA4 server registered with Claude Code.");
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.error(`\nFailed to register automatically: ${err.message}`);
|
|
236
|
+
console.log("Add this to your .mcp.json manually:\n");
|
|
237
|
+
const mcpConfig = {
|
|
238
|
+
ga4: {
|
|
239
|
+
command: "npx",
|
|
240
|
+
args: ["-y", "mcp-ga4"],
|
|
241
|
+
env: {
|
|
242
|
+
GA4_PROPERTY_ID: propertyId,
|
|
243
|
+
GOOGLE_APPLICATION_CREDENTIALS: credsFile,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
console.log(JSON.stringify({ mcpServers: mcpConfig }, null, 2));
|
|
248
|
+
return false;
|
|
219
249
|
}
|
|
220
|
-
console.log('Add this inside the "mcpServers" key:\n');
|
|
221
|
-
console.log(JSON.stringify(mcpConfig, null, 2));
|
|
222
|
-
// Offer to write .mcp.json
|
|
223
|
-
console.log(`\n${"=".repeat(50)}`);
|
|
224
250
|
}
|
|
225
251
|
// ============================================
|
|
226
252
|
// MAIN
|
|
@@ -236,10 +262,19 @@ async function main() {
|
|
|
236
262
|
const tokens = await runOAuth(clientId, clientSecret);
|
|
237
263
|
const credsFile = saveCredentials(tokens, clientId, clientSecret);
|
|
238
264
|
if (await verifyConnection(propertyId, credsFile)) {
|
|
239
|
-
|
|
265
|
+
const autoRegistered = registerWithClaude(propertyId, credsFile);
|
|
240
266
|
printHeader("Setup Complete!");
|
|
241
|
-
|
|
242
|
-
|
|
267
|
+
if (autoRegistered) {
|
|
268
|
+
console.log("GA4 is now connected to Claude Code.\n");
|
|
269
|
+
console.log("IMPORTANT: Restart Claude Code to load the new server.");
|
|
270
|
+
console.log(" - CLI: exit and reopen claude");
|
|
271
|
+
console.log(" - Desktop app: quit and reopen the app\n");
|
|
272
|
+
console.log('Then try asking: "What were my top 10 pages last week?"\n');
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
console.log("After adding the config, restart Claude Code to load the server.\n");
|
|
276
|
+
console.log('Then try asking: "What were my top 10 pages last week?"\n');
|
|
277
|
+
}
|
|
243
278
|
console.log("To update or re-authenticate: npx mcp-ga4-setup");
|
|
244
279
|
console.log("To report issues: use the ga4_send_feedback tool in Claude\n");
|
|
245
280
|
}
|
package/dist/tools.js
CHANGED
|
@@ -168,6 +168,20 @@ export const tools = [
|
|
|
168
168
|
required: ["property_id"],
|
|
169
169
|
},
|
|
170
170
|
},
|
|
171
|
+
{
|
|
172
|
+
name: "ga4_health_check",
|
|
173
|
+
description: "Verify that GA4 credentials are valid and the API is accessible. Run this to diagnose connection issues.",
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: {
|
|
177
|
+
property_id: {
|
|
178
|
+
type: "string",
|
|
179
|
+
description: "GA4 property ID (numeric)",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
required: ["property_id"],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
171
185
|
{
|
|
172
186
|
name: "ga4_send_feedback",
|
|
173
187
|
description: "Send feedback about the GA4 tools. Use when a query didn't work as expected, to request a feature, or ask a question.",
|