@wwhat/mcp-stdio-client 1.0.0 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/index.js +359 -61
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ async function mcpRequest(method, params) {
38
38
  method: 'POST',
39
39
  headers: {
40
40
  'Content-Type': 'application/json',
41
+ 'Accept': 'application/json, text/event-stream',
41
42
  'X-API-Key': API_KEY,
42
43
  },
43
44
  body: JSON.stringify({
@@ -64,15 +65,77 @@ async function main() {
64
65
  name: 'wwhat-analytics',
65
66
  version: '1.0.0',
66
67
  });
67
- // Tool 1: get_weekly_insights
68
- server.tool('get_weekly_insights', 'Get weekly analytics insights including traffic changes, landing page drops, and actionable recommendations for a specific week.', {
69
- source_id: z.string().describe('The analytics source/property ID'),
70
- week_start_date: z.string().describe('Start date of the week (ISO format, e.g., 2025-01-13)'),
71
- include_lp_explainers: z.boolean().default(true).describe('Include landing page drop explainers'),
68
+ // ============================================================================
69
+ // Tool 1: list_sources
70
+ // ============================================================================
71
+ server.tool('list_sources', `List all analytics sources (GA4 properties) connected to your account.
72
+
73
+ PURPOSE:
74
+ Returns the source_id values needed for all other analytics tools. Call this first to discover available data sources.
75
+
76
+ OUTPUT INCLUDES:
77
+ - source_id: Unique identifier to use with other tools
78
+ - name: Human-readable property name
79
+ - property_id: GA4 property ID
80
+ - status: Connection status (active, pending, error)
81
+ - last_processed: When data was last synced (if include_details=true)
82
+ - data_range: Available date range (if include_details=true)
83
+
84
+ TYPICAL WORKFLOW:
85
+ 1. Call list_sources to get available source_ids
86
+ 2. Use a source_id with get_weekly_insights or query_analytics_data`, {
87
+ include_details: z
88
+ .boolean()
89
+ .default(false)
90
+ .describe('Include last_processed date, data_range, and sync status for each source'),
91
+ }, async (params) => {
92
+ const result = await mcpRequest('tools/call', {
93
+ name: 'list_sources',
94
+ arguments: params,
95
+ });
96
+ return {
97
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
98
+ };
99
+ });
100
+ // ============================================================================
101
+ // Tool 2: get_weekly_insights
102
+ // ============================================================================
103
+ server.tool('get_weekly_insights', `Get AI-generated weekly analytics insights for a specific week.
104
+
105
+ PURPOSE:
106
+ Returns pre-analyzed insight cards highlighting the most important traffic changes, anomalies, and opportunities for a given week. This is higher-level than raw data queries.
107
+
108
+ WHAT YOU GET:
109
+ - Insight cards with titles, descriptions, and severity ratings
110
+ - Traffic change summaries (site-wide and per-segment)
111
+ - Landing page drop explainers (root cause analysis)
112
+ - Actionable recommendations
113
+
114
+ INSIGHT TYPES:
115
+ - traffic_drop: Significant decrease in sessions/users
116
+ - traffic_spike: Unusual increase in traffic
117
+ - conversion_change: Conversion rate shifts
118
+ - channel_shift: Traffic source changes
119
+ - device_shift: Mobile/desktop mix changes
120
+ - geo_shift: Geographic traffic changes
121
+
122
+ SEVERITY LEVELS:
123
+ - critical: >30% change, requires immediate attention
124
+ - significant: 20-30% change, should investigate
125
+ - moderate: 10-20% change, worth noting
126
+ - minor: <10% change, informational
127
+
128
+ TYPICAL WORKFLOW:
129
+ 1. Call get_weekly_insights for overview
130
+ 2. Use query_analytics_data to drill into specific insights
131
+ 3. Use analyze_url for page-specific deep dives`, {
132
+ source_id: z.string().describe('The analytics source/property ID (get from list_sources)'),
133
+ week_start_date: z.string().describe('Monday of the week to analyze (ISO format: YYYY-MM-DD, e.g., "2025-01-13")'),
134
+ include_lp_explainers: z.boolean().default(true).describe('Include AI-generated root cause analysis for landing page drops'),
72
135
  significance_filter: z
73
136
  .enum(['all', 'critical', 'significant', 'moderate', 'minor'])
74
137
  .default('all')
75
- .describe('Filter insights by significance level'),
138
+ .describe('Filter to only show insights at or above this severity level'),
76
139
  }, async (params) => {
77
140
  const result = await mcpRequest('tools/call', {
78
141
  name: 'get_weekly_insights',
@@ -82,66 +145,287 @@ async function main() {
82
145
  content: result.content.map((c) => ({ type: 'text', text: c.text })),
83
146
  };
84
147
  });
85
- // Tool 2: query_analytics_metrics
86
- server.tool('query_analytics_metrics', 'Query processed analytics metrics with trend comparisons (W1, L4WAVG, LY4WAVG). Supports filtering, sorting, and aggregation.', {
87
- source_id: z.string().describe('The analytics source/property ID'),
88
- period_start: z.string().describe('Period start date (ISO format)'),
89
- period_end: z.string().optional().describe('Period end date (defaults to 7 days after start)'),
90
- metric: z
91
- .enum(['sessions', 'totalUsers', 'newUsers', 'bounceRate', 'conversions', 'revenue'])
92
- .default('sessions')
93
- .describe('Primary metric to analyze'),
94
- comparison_type: z
95
- .enum(['W1', 'L4WAVG', 'LY4WAVG', 'all'])
96
- .default('all')
97
- .describe('Type of comparison to include'),
98
- filter: z
99
- .object({
100
- dimension_values: z.record(z.string()).optional().describe('Filter by dimension values'),
101
- min_value: z.number().optional().describe('Minimum metric value to include'),
102
- min_change_percent: z.number().optional().describe('Minimum absolute change percentage'),
103
- only_drops: z.boolean().optional().describe('Only include negative changes'),
104
- only_gains: z.boolean().optional().describe('Only include positive changes'),
105
- })
106
- .optional()
107
- .describe('Filtering options'),
108
- sort_by: z
109
- .enum(['value', 'change_w1', 'change_l4wavg', 'change_ly4wavg', 'z_score'])
110
- .default('value')
111
- .describe('Sort results by this field'),
112
- sort_order: z.enum(['asc', 'desc']).default('desc'),
113
- limit: z.number().min(1).max(100).default(20).describe('Maximum number of results'),
148
+ // ============================================================================
149
+ // Tool 3: query_analytics_data (Universal Query Interface)
150
+ // ============================================================================
151
+ server.tool('query_analytics_data', `Universal query interface for analytics data. Query any combination of dimensions and metrics with flexible filtering, sorting, and comparisons.
152
+
153
+ AVAILABLE DIMENSIONS (use in filters, group_by, breakdown_by):
154
+ ┌─────────────────────────────────┬────────────────────────────────────────────────────┐
155
+ │ Dimension │ Description & Example Values │
156
+ ├─────────────────────────────────┼────────────────────────────────────────────────────┤
157
+ │ landingPage │ URL path: "/", "/blog/guide", "/pricing" │
158
+ deviceCategory │ "desktop", "mobile", "tablet" │
159
+ │ sessionDefaultChannelGrouping │ "Organic Search", "Direct", "Paid Search", │
160
+ │ │ "Social", "Email", "Referral", "Display" │
161
+ │ country │ "United States", "United Kingdom", "Germany"
162
+ │ region │ State/province: "California", "Ontario" │
163
+ city │ "New York", "London", "San Francisco" │
164
+ newVsReturning │ "new" or "returning" │
165
+ browser │ "Chrome", "Safari", "Firefox", "Edge" │
166
+ sessionSource │ "google", "facebook", "newsletter", "(direct)" │
167
+ sessionMedium │ "organic", "cpc", "referral", "email", "(none)" │
168
+ └─────────────────────────────────┴────────────────────────────────────────────────────┘
169
+
170
+ AVAILABLE METRICS (use in metric parameter):
171
+ ┌──────────────┬────────────────────────────────────────────────────────────────┐
172
+ Metric │ Description │
173
+ ├──────────────┼────────────────────────────────────────────────────────────────┤
174
+ │ sessions │ Number of sessions (default) - most common for traffic analysis│
175
+ visitors │ Unique visitors (totalUsers)
176
+ conversions │ Total conversions - mapped per source config │
177
+ │ leads │ Lead-type conversions (form fills, signups) │
178
+ │ purchases │ Purchase/transaction conversions │
179
+ │ revenue │ Revenue in dollars │
180
+ │ bounceRate │ Bounce rate percentage (0-100) │
181
+ └──────────────┴────────────────────────────────────────────────────────────────┘
182
+
183
+ COMPARISON TYPES (returned in each row's comparisons object):
184
+ - W1: Week-over-week change (vs previous week) - best for recent trends
185
+ - L4WAVG: Change vs last 4-week average - smooths out weekly variance
186
+ - LY4WAVG: Change vs same period last year - accounts for seasonality
187
+
188
+ Each comparison includes:
189
+ - previous: Previous period value
190
+ - current: Current period value
191
+ - change: Absolute change
192
+ - changePercent: Percentage change
193
+
194
+ OUTPUT STRUCTURE:
195
+ {
196
+ "status": "data_found",
197
+ "summary": {
198
+ "totalRows": 25,
199
+ "primaryMetric": "sessions",
200
+ "totalValue": 15420,
201
+ "dropsCount": 8, // Rows with >10% drop
202
+ "gainsCount": 5, // Rows with >10% gain
203
+ "topByChange": [...], // Top 3 gainers
204
+ "bottomByChange": [...] // Top 3 losers
205
+ },
206
+ "rows": [{
207
+ "dimensions": { "landingPage": "/blog/guide" },
208
+ "metrics": { "sessions": 1250, "conversions": 45 },
209
+ "comparisons": {
210
+ "w1": { "sessions": { "previous": 1400, "changePercent": -10.7 } }
211
+ },
212
+ "shareOfTotal": 8.1
213
+ }],
214
+ "insights": [{ "type": "drop", "severity": "significant", "message": "..." }]
215
+ }
216
+
217
+ COMMON USE CASES:
218
+
219
+ 1. Find all traffic drops this week:
220
+ { only_drops: true, sort_by: "change_w1", sort_order: "asc" }
221
+
222
+ 2. Analyze blog performance:
223
+ { filters: [{ dimension: "landingPage", operator: "startsWith", value: "/blog/" }], group_by: ["landingPage"] }
224
+
225
+ 3. Channel performance breakdown:
226
+ { group_by: ["sessionDefaultChannelGrouping"], metric: "conversions" }
227
+
228
+ 4. Mobile vs Desktop comparison:
229
+ { group_by: ["deviceCategory"] }
230
+
231
+ 5. Top converting pages:
232
+ { group_by: ["landingPage"], metric: "conversions", sort_by: "value", min_value: 5 }
233
+
234
+ 6. Pages losing traffic from Google:
235
+ { filters: [{ dimension: "sessionSource", operator: "equals", value: "google" }], only_drops: true, group_by: ["landingPage"] }`, {
236
+ source_id: z.string().describe('The analytics source/property ID (get from list_sources)'),
237
+ week_start_date: z.string().describe('Monday of the week to analyze (ISO format: YYYY-MM-DD, e.g., "2025-01-13")'),
238
+ filters: z.array(z.object({
239
+ dimension: z.string().describe('Dimension name (see AVAILABLE DIMENSIONS above)'),
240
+ operator: z.enum(['equals', 'contains', 'startsWith', 'endsWith', 'in']).describe('equals: exact match | contains: substring | startsWith/endsWith: prefix/suffix | in: match any value in array'),
241
+ value: z.union([z.string(), z.array(z.string())]).describe('Value to match. For "in" operator, provide array like ["value1", "value2"]'),
242
+ })).optional().describe('Filter rows by dimension values. Multiple filters are AND-ed together'),
243
+ group_by: z.array(z.string()).optional().describe('Aggregate and group results by these dimensions. Example: ["landingPage"] returns one row per unique page with summed metrics'),
244
+ breakdown_by: z.array(z.string()).optional().describe('For each result row, include a nested breakdown. Example: breakdown_by=["deviceCategory"] adds desktop/mobile/tablet split to each row'),
245
+ metric: z.string().optional().default('sessions').describe('Primary metric for sorting/filtering: sessions, visitors, conversions, leads, purchases, revenue, bounceRate'),
246
+ min_value: z.number().optional().describe('Exclude rows where primary metric < this value. Example: min_value=100 for pages with 100+ sessions'),
247
+ min_change_percent: z.number().optional().describe('Only rows with |W/W change| >= this %. Example: 20 returns rows with ≥20% increase OR ≥20% decrease'),
248
+ only_drops: z.boolean().optional().describe('Only show rows with negative week-over-week change (find traffic/conversion losses)'),
249
+ only_gains: z.boolean().optional().describe('Only show rows with positive week-over-week change (find growth)'),
250
+ sort_by: z.enum(['value', 'change_w1', 'change_l4wavg', 'change_ly4wavg', 'z_score']).optional().default('value').describe('Sort by: value (metric amount), change_w1 (W/W %), change_l4wavg (vs 4-week avg %), change_ly4wavg (vs last year %)'),
251
+ sort_order: z.enum(['asc', 'desc']).optional().default('desc').describe('desc: highest/best first | asc: lowest/worst first'),
252
+ comparison_type: z.enum(['W1', 'L4WAVG', 'LY4WAVG', 'all']).optional().default('all').describe('Which comparisons to include in response: W1, L4WAVG, LY4WAVG, or all'),
253
+ limit: z.number().optional().default(100).describe('Maximum rows to return (default: 100, max: 500)'),
254
+ include_insights: z.boolean().optional().default(true).describe('Auto-detect notable changes (>15%) and include as insights array'),
255
+ fetch_if_missing: z.boolean().optional().default(false).describe('If no data found, return instructions for fetching from GA4 API'),
114
256
  }, async (params) => {
115
257
  const result = await mcpRequest('tools/call', {
116
- name: 'query_analytics_metrics',
258
+ name: 'query_analytics_data',
117
259
  arguments: params,
118
260
  });
119
261
  return {
120
262
  content: result.content.map((c) => ({ type: 'text', text: c.text })),
121
263
  };
122
264
  });
123
- // Tool 3: list_sources
124
- server.tool('list_sources', 'List all analytics sources (GA4 properties) available to the authenticated user.', {
125
- include_details: z
126
- .boolean()
127
- .default(false)
128
- .describe('Include additional details like last processed date and data availability'),
265
+ // ============================================================================
266
+ // Tool 4: analyze_url
267
+ // ============================================================================
268
+ server.tool('analyze_url', `Deep-dive analysis for a specific URL or landing page.
269
+
270
+ PURPOSE:
271
+ Get comprehensive performance data for a single page, including traffic metrics, comparisons, and breakdowns by device/channel/country. More focused than query_analytics_data when you know the exact page.
272
+
273
+ WHAT YOU GET:
274
+ - Core metrics: sessions, visitors, bounce rate, conversions, revenue
275
+ - Week-over-week and 4-week average comparisons
276
+ - Share of total site traffic
277
+ - Breakdowns by:
278
+ - deviceCategory (desktop/mobile/tablet split)
279
+ - sessionDefaultChannelGrouping (organic/direct/paid/social)
280
+ - country (geographic distribution)
281
+ - newVsReturning (new vs returning visitors)
282
+ - Auto-detected insights (drops, growth, anomalies)
283
+
284
+ URL MATCHING:
285
+ - Partial match: "/blog" matches "/blog/post-1", "/blog/post-2"
286
+ - Exact match: Use full path for specific page
287
+ - Domain optional: Both "example.com/page" and "/page" work
288
+
289
+ OUTPUT STRUCTURE:
290
+ {
291
+ "status": "data_found",
292
+ "url": "/blog/guide",
293
+ "metrics": { "sessions": 1250, "visitors": 980, "bounceRate": 45.2, "conversions": 32 },
294
+ "comparisons": {
295
+ "w1": { "sessions": { "previous": 1400, "changePercent": -10.7 } },
296
+ "l4wavg": { "sessions": { "previous": 1180, "changePercent": 5.9 } }
297
+ },
298
+ "shareOfSiteTraffic": 8.1,
299
+ "breakdowns": [
300
+ { "dimension": "deviceCategory", "values": [{ "value": "mobile", "sessions": 650, "share": 52 }] }
301
+ ],
302
+ "insights": [{ "type": "drop", "message": "Mobile traffic down 18% W/W" }]
303
+ }
304
+
305
+ WHEN DATA IS MISSING:
306
+ Returns status="needs_fetch" with instructions to fetch from GA4 API.
307
+
308
+ TYPICAL WORKFLOW:
309
+ 1. Use get_weekly_insights to identify problematic pages
310
+ 2. Call analyze_url for deep dive on specific page
311
+ 3. Use query_analytics_data for cross-page comparisons`, {
312
+ source_id: z.string().describe('The analytics source/property ID (get from list_sources)'),
313
+ url: z.string().describe('URL path to analyze. Examples: "/blog/guide", "/pricing", "example.com/page". Partial paths match multiple pages.'),
314
+ week_start_date: z.string().describe('Monday of the week to analyze (ISO format: YYYY-MM-DD)'),
315
+ include_breakdowns: z.boolean().optional().default(true).describe('Include device, channel, country, and new/returning breakdowns'),
316
+ fetch_if_missing: z.boolean().optional().default(false).describe('If data not found, return fetch instructions instead of empty result'),
129
317
  }, async (params) => {
130
318
  const result = await mcpRequest('tools/call', {
131
- name: 'list_sources',
319
+ name: 'analyze_url',
320
+ arguments: params,
321
+ });
322
+ return {
323
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
324
+ };
325
+ });
326
+ // ============================================================================
327
+ // Tool 5: get_source_config
328
+ // ============================================================================
329
+ server.tool('get_source_config', `Get the configuration and metric mappings for an analytics source.
330
+
331
+ PURPOSE:
332
+ Understand how GA4 events and metrics are mapped to internal metrics (conversions, leads, purchases, revenue). Essential for interpreting metric values correctly.
333
+
334
+ WHAT YOU GET:
335
+ - Metric mappings: Which GA4 events count as conversions, leads, purchases
336
+ - Revenue configuration: Currency, event mapping
337
+ - Funnel stage assignments: How metrics map to acquisition/engagement/conversion/monetization
338
+ - Custom dimension mappings: Any custom dimensions configured
339
+
340
+ EXAMPLE OUTPUT:
341
+ {
342
+ "sourceId": "src_abc123",
343
+ "name": "My Website",
344
+ "metricMappings": {
345
+ "conversions": { "events": ["form_submit", "signup", "purchase"], "aggregation": "sum" },
346
+ "leads": { "events": ["form_submit", "signup"], "aggregation": "sum" },
347
+ "purchases": { "events": ["purchase"], "aggregation": "sum" },
348
+ "revenue": { "events": ["purchase"], "property": "value", "currency": "USD" }
349
+ },
350
+ "funnelConfig": {
351
+ "acquisition": ["sessions", "visitors"],
352
+ "engagement": ["pageviews", "time_on_site"],
353
+ "conversion": ["conversions", "leads"],
354
+ "monetization": ["purchases", "revenue"]
355
+ }
356
+ }
357
+
358
+ USE CASES:
359
+ - Verify what counts as a "conversion" before analyzing conversion data
360
+ - Understand revenue calculation methodology
361
+ - Debug metric discrepancies between GA4 and insights`, {
362
+ source_id: z.string().describe('The analytics source/property ID (get from list_sources)'),
363
+ }, async (params) => {
364
+ const result = await mcpRequest('tools/call', {
365
+ name: 'get_source_config',
132
366
  arguments: params,
133
367
  });
134
368
  return {
135
369
  content: result.content.map((c) => ({ type: 'text', text: c.text })),
136
370
  };
137
371
  });
138
- // Tool 4: trigger_analytics_workflow
139
- server.tool('trigger_analytics_workflow', 'Trigger the analytics agent workflow to process a specific week. Returns immediately with a workflow ID that can be polled for status.', {
372
+ // ============================================================================
373
+ // Tool 6: get_funnel_definition
374
+ // ============================================================================
375
+ server.tool('get_funnel_definition', `Get the standard marketing funnel structure and stage definitions.
376
+
377
+ PURPOSE:
378
+ Returns the funnel framework used to categorize and analyze metrics. Helps understand how different metrics relate across the customer journey.
379
+
380
+ FUNNEL STAGES:
381
+ ┌─────────────────┬────────────────────────────────────────────────────────────┐
382
+ │ Stage │ Metrics & Purpose │
383
+ ├─────────────────┼────────────────────────────────────────────────────────────┤
384
+ │ ACQUISITION │ sessions, visitors, newUsers │
385
+ │ │ How people find and arrive at your site │
386
+ ├─────────────────┼────────────────────────────────────────────────────────────┤
387
+ │ ENGAGEMENT │ pageviews, avgSessionDuration, bounceRate, pagesPerSession │
388
+ │ │ How people interact with your content │
389
+ ├─────────────────┼────────────────────────────────────────────────────────────┤
390
+ │ CONVERSION │ conversions, leads, signups, formSubmits │
391
+ │ │ Actions that indicate interest/intent │
392
+ ├─────────────────┼────────────────────────────────────────────────────────────┤
393
+ │ MONETIZATION │ purchases, revenue, transactions, avgOrderValue │
394
+ │ │ Revenue-generating actions │
395
+ └─────────────────┴────────────────────────────────────────────────────────────┘
396
+
397
+ OUTPUT INCLUDES:
398
+ - Stage definitions with associated metrics
399
+ - Stage-to-stage conversion rate calculations
400
+ - Benchmark ranges for each metric
401
+ - Metric interdependencies
402
+
403
+ USE CASES:
404
+ - Understand which metrics to focus on at each funnel stage
405
+ - Identify where in the funnel problems are occurring
406
+ - Plan analysis strategy based on business goals`, {}, async () => {
407
+ const result = await mcpRequest('tools/call', {
408
+ name: 'get_funnel_definition',
409
+ arguments: {},
410
+ });
411
+ return {
412
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
413
+ };
414
+ });
415
+ // ============================================================================
416
+ // Tool 7: trigger_analytics_workflow (Future Implementation)
417
+ // ============================================================================
418
+ server.tool('trigger_analytics_workflow', `[COMING SOON] Trigger data processing workflow for a specific week.
419
+
420
+ PURPOSE:
421
+ Initiates the analytics processing pipeline to fetch fresh data from GA4 and generate insights. Use when data is missing or stale.
422
+
423
+ NOTE: This tool is planned for a future release. Currently, data processing is handled automatically on a schedule.`, {
140
424
  source_id: z.string().describe('The analytics source/property ID'),
141
- week_start_date: z.string().describe('Start date of the week to process (ISO format)'),
142
- force_refresh: z.boolean().default(false).describe('Force re-processing even if data already exists'),
143
- wait_for_completion: z.boolean().default(false).describe('If true, poll until workflow completes'),
144
- timeout_seconds: z.number().max(600).default(300).describe('Maximum time to wait if wait_for_completion is true'),
425
+ week_start_date: z.string().describe('Monday of the week to process (ISO format: YYYY-MM-DD)'),
426
+ force_refresh: z.boolean().default(false).describe('Re-process even if data already exists'),
427
+ wait_for_completion: z.boolean().default(false).describe('Wait for workflow to complete before returning'),
428
+ timeout_seconds: z.number().max(600).default(300).describe('Max wait time if wait_for_completion=true'),
145
429
  }, async (params) => {
146
430
  const result = await mcpRequest('tools/call', {
147
431
  name: 'trigger_analytics_workflow',
@@ -151,10 +435,17 @@ async function main() {
151
435
  content: result.content.map((c) => ({ type: 'text', text: c.text })),
152
436
  };
153
437
  });
154
- // Tool 5: get_workflow_status
155
- server.tool('get_workflow_status', 'Check the status of a running analytics workflow triggered by trigger_analytics_workflow.', {
438
+ // ============================================================================
439
+ // Tool 8: get_workflow_status (Future Implementation)
440
+ // ============================================================================
441
+ server.tool('get_workflow_status', `[COMING SOON] Check status of a running analytics workflow.
442
+
443
+ PURPOSE:
444
+ Poll for completion status after triggering a workflow with trigger_analytics_workflow.
445
+
446
+ NOTE: This tool is planned for a future release alongside trigger_analytics_workflow.`, {
156
447
  invocation_id: z.string().describe('The invocation ID returned by trigger_analytics_workflow'),
157
- source_id: z.string().describe('The analytics source/property ID (for validation)'),
448
+ source_id: z.string().describe('The analytics source/property ID'),
158
449
  }, async (params) => {
159
450
  const result = await mcpRequest('tools/call', {
160
451
  name: 'get_workflow_status',
@@ -164,15 +455,22 @@ async function main() {
164
455
  content: result.content.map((c) => ({ type: 'text', text: c.text })),
165
456
  };
166
457
  });
167
- // Tool 6: get_ga4_data
168
- server.tool('get_ga4_data', 'Get Google Analytics 4 data for specific pages, segments, or dimensions. Fetches from database if available, otherwise retrieves from GA4 API.', {
458
+ // ============================================================================
459
+ // Tool 9: get_ga4_data (Future Implementation)
460
+ // ============================================================================
461
+ server.tool('get_ga4_data', `[COMING SOON] Direct GA4 API data fetch for custom queries.
462
+
463
+ PURPOSE:
464
+ Fetch raw data directly from Google Analytics 4 API for custom analysis not covered by standard tools.
465
+
466
+ NOTE: This tool is planned for a future release. Currently, use query_analytics_data for pre-processed data with comparisons and insights.`, {
169
467
  source_id: z.string().describe('The analytics source/property ID'),
170
468
  date_range: z
171
469
  .object({
172
- start_date: z.string().describe('Start date (ISO format)'),
173
- end_date: z.string().describe('End date (ISO format)'),
470
+ start_date: z.string().describe('Start date (ISO format: YYYY-MM-DD)'),
471
+ end_date: z.string().describe('End date (ISO format: YYYY-MM-DD)'),
174
472
  })
175
- .describe('Date range to fetch data for'),
473
+ .describe('Date range to fetch'),
176
474
  dimensions: z
177
475
  .array(z.enum([
178
476
  'landingPage',
@@ -186,8 +484,8 @@ async function main() {
186
484
  'region',
187
485
  ]))
188
486
  .optional()
189
- .describe('Dimensions to break down the data by'),
190
- filters: z.record(z.string()).optional().describe('Key-value pairs to filter data'),
487
+ .describe('Dimensions to include'),
488
+ filters: z.record(z.string()).optional().describe('Dimension filters as key-value pairs'),
191
489
  metrics: z
192
490
  .array(z.enum([
193
491
  'sessions',
@@ -201,8 +499,8 @@ async function main() {
201
499
  'totalRevenue',
202
500
  ]))
203
501
  .default(['sessions', 'totalUsers'])
204
- .describe('Metrics to retrieve'),
205
- fetch_if_missing: z.boolean().default(true).describe('If true, fetch from GA4 API when data not in database'),
502
+ .describe('Metrics to fetch'),
503
+ fetch_if_missing: z.boolean().default(true).describe('Fetch from GA4 API if not in cache'),
206
504
  }, async (params) => {
207
505
  const result = await mcpRequest('tools/call', {
208
506
  name: 'get_ga4_data',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwhat/mcp-stdio-client",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Stdio wrapper for wwhat MCP server - enables Cursor/Claude Desktop integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "ga4"
29
29
  ],
30
30
  "dependencies": {
31
- "@modelcontextprotocol/sdk": "^1.0.0",
31
+ "@modelcontextprotocol/sdk": "^1.25.0",
32
32
  "zod": "^3.23.0"
33
33
  },
34
34
  "devDependencies": {