@wwhat/mcp-stdio-client 1.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/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # @wwhat/mcp-stdio-client
2
+
3
+ Stdio wrapper for the wwhat MCP server - enables Cursor and Claude Desktop integration.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ cd packages/mcp-stdio-client
9
+ bun install
10
+ bun run build
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Environment Variables
16
+
17
+ - `WWHAT_API_KEY` (required) - Your wwhat API key
18
+ - `WWHAT_MCP_URL` (optional) - MCP server URL (defaults to `https://app.wwhat.ai/mcp`)
19
+
20
+ ### Cursor Configuration
21
+
22
+ Add to your Cursor MCP settings (`~/.cursor/mcp.json`):
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "wwhat": {
28
+ "command": "node",
29
+ "args": ["/path/to/wwhat/packages/mcp-stdio-client/dist/index.js"],
30
+ "env": {
31
+ "WWHAT_API_KEY": "your-api-key-here",
32
+ "WWHAT_MCP_URL": "https://app.wwhat.ai/mcp"
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ For local development:
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "wwhat-local": {
45
+ "command": "node",
46
+ "args": ["/path/to/wwhat/packages/mcp-stdio-client/dist/index.js"],
47
+ "env": {
48
+ "WWHAT_API_KEY": "your-api-key-here",
49
+ "WWHAT_MCP_URL": "http://localhost:8787/mcp"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### Claude Desktop Configuration
57
+
58
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac):
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "wwhat": {
64
+ "command": "node",
65
+ "args": ["/path/to/wwhat/packages/mcp-stdio-client/dist/index.js"],
66
+ "env": {
67
+ "WWHAT_API_KEY": "your-api-key-here",
68
+ "WWHAT_MCP_URL": "https://app.wwhat.ai/mcp"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Available Tools
76
+
77
+ | Tool | Description |
78
+ |------|-------------|
79
+ | `get_weekly_insights` | Get weekly analytics insights including traffic changes and recommendations |
80
+ | `query_analytics_metrics` | Query metrics with trend comparisons (W1, L4WAVG, LY4WAVG) |
81
+ | `list_sources` | List available GA4 properties |
82
+ | `trigger_analytics_workflow` | Trigger analytics processing for a week |
83
+ | `get_workflow_status` | Check workflow status |
84
+ | `get_ga4_data` | Get GA4 data for specific dimensions |
85
+
86
+ ## Testing
87
+
88
+ Run directly:
89
+
90
+ ```bash
91
+ WWHAT_API_KEY=your-key WWHAT_MCP_URL=http://localhost:8787/mcp node dist/index.js
92
+ ```
93
+
94
+ ## Development
95
+
96
+ ```bash
97
+ bun run dev # Watch mode
98
+ bun run build # Build for production
99
+ ```
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * wwhat MCP Stdio Client
4
+ *
5
+ * Bridges Cursor/Claude Desktop (stdio) to the wwhat HTTP MCP server.
6
+ *
7
+ * Usage:
8
+ * WWHAT_API_KEY=your-key WWHAT_MCP_URL=https://app.wwhat.ai/mcp npx @wwhat/mcp-stdio-client
9
+ *
10
+ * Or in Cursor/Claude Desktop config:
11
+ * {
12
+ * "mcpServers": {
13
+ * "wwhat": {
14
+ * "command": "npx",
15
+ * "args": ["@wwhat/mcp-stdio-client"],
16
+ * "env": {
17
+ * "WWHAT_API_KEY": "your-api-key",
18
+ * "WWHAT_MCP_URL": "https://app.wwhat.ai/mcp"
19
+ * }
20
+ * }
21
+ * }
22
+ * }
23
+ */
24
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * wwhat MCP Stdio Client
4
+ *
5
+ * Bridges Cursor/Claude Desktop (stdio) to the wwhat HTTP MCP server.
6
+ *
7
+ * Usage:
8
+ * WWHAT_API_KEY=your-key WWHAT_MCP_URL=https://app.wwhat.ai/mcp npx @wwhat/mcp-stdio-client
9
+ *
10
+ * Or in Cursor/Claude Desktop config:
11
+ * {
12
+ * "mcpServers": {
13
+ * "wwhat": {
14
+ * "command": "npx",
15
+ * "args": ["@wwhat/mcp-stdio-client"],
16
+ * "env": {
17
+ * "WWHAT_API_KEY": "your-api-key",
18
+ * "WWHAT_MCP_URL": "https://app.wwhat.ai/mcp"
19
+ * }
20
+ * }
21
+ * }
22
+ * }
23
+ */
24
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
25
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
26
+ import { z } from 'zod';
27
+ const MCP_URL = process.env.WWHAT_MCP_URL || 'https://app.wwhat.ai/mcp';
28
+ const API_KEY = process.env.WWHAT_API_KEY;
29
+ if (!API_KEY) {
30
+ console.error('Error: WWHAT_API_KEY environment variable is required');
31
+ process.exit(1);
32
+ }
33
+ /**
34
+ * Make an MCP JSON-RPC request to the HTTP server
35
+ */
36
+ async function mcpRequest(method, params) {
37
+ const response = await fetch(MCP_URL, {
38
+ method: 'POST',
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ 'X-API-Key': API_KEY,
42
+ },
43
+ body: JSON.stringify({
44
+ jsonrpc: '2.0',
45
+ id: crypto.randomUUID(),
46
+ method,
47
+ params,
48
+ }),
49
+ });
50
+ if (!response.ok) {
51
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
52
+ }
53
+ const result = await response.json();
54
+ if (result.error) {
55
+ throw new Error(result.error.message);
56
+ }
57
+ return result.result;
58
+ }
59
+ /**
60
+ * Create and start the stdio MCP server
61
+ */
62
+ async function main() {
63
+ const server = new McpServer({
64
+ name: 'wwhat-analytics',
65
+ version: '1.0.0',
66
+ });
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'),
72
+ significance_filter: z
73
+ .enum(['all', 'critical', 'significant', 'moderate', 'minor'])
74
+ .default('all')
75
+ .describe('Filter insights by significance level'),
76
+ }, async (params) => {
77
+ const result = await mcpRequest('tools/call', {
78
+ name: 'get_weekly_insights',
79
+ arguments: params,
80
+ });
81
+ return {
82
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
83
+ };
84
+ });
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'),
114
+ }, async (params) => {
115
+ const result = await mcpRequest('tools/call', {
116
+ name: 'query_analytics_metrics',
117
+ arguments: params,
118
+ });
119
+ return {
120
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
121
+ };
122
+ });
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'),
129
+ }, async (params) => {
130
+ const result = await mcpRequest('tools/call', {
131
+ name: 'list_sources',
132
+ arguments: params,
133
+ });
134
+ return {
135
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
136
+ };
137
+ });
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.', {
140
+ 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'),
145
+ }, async (params) => {
146
+ const result = await mcpRequest('tools/call', {
147
+ name: 'trigger_analytics_workflow',
148
+ arguments: params,
149
+ });
150
+ return {
151
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
152
+ };
153
+ });
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.', {
156
+ 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)'),
158
+ }, async (params) => {
159
+ const result = await mcpRequest('tools/call', {
160
+ name: 'get_workflow_status',
161
+ arguments: params,
162
+ });
163
+ return {
164
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
165
+ };
166
+ });
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.', {
169
+ source_id: z.string().describe('The analytics source/property ID'),
170
+ date_range: z
171
+ .object({
172
+ start_date: z.string().describe('Start date (ISO format)'),
173
+ end_date: z.string().describe('End date (ISO format)'),
174
+ })
175
+ .describe('Date range to fetch data for'),
176
+ dimensions: z
177
+ .array(z.enum([
178
+ 'landingPage',
179
+ 'deviceCategory',
180
+ 'country',
181
+ 'sessionDefaultChannelGrouping',
182
+ 'sessionSource',
183
+ 'newVsReturning',
184
+ 'browser',
185
+ 'city',
186
+ 'region',
187
+ ]))
188
+ .optional()
189
+ .describe('Dimensions to break down the data by'),
190
+ filters: z.record(z.string()).optional().describe('Key-value pairs to filter data'),
191
+ metrics: z
192
+ .array(z.enum([
193
+ 'sessions',
194
+ 'totalUsers',
195
+ 'newUsers',
196
+ 'bounceRate',
197
+ 'avgSessionDuration',
198
+ 'screenPageViews',
199
+ 'engagedSessions',
200
+ 'conversions',
201
+ 'totalRevenue',
202
+ ]))
203
+ .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'),
206
+ }, async (params) => {
207
+ const result = await mcpRequest('tools/call', {
208
+ name: 'get_ga4_data',
209
+ arguments: params,
210
+ });
211
+ return {
212
+ content: result.content.map((c) => ({ type: 'text', text: c.text })),
213
+ };
214
+ });
215
+ // Connect to stdio transport
216
+ const transport = new StdioServerTransport();
217
+ await server.connect(transport);
218
+ // Log to stderr (stdout is used for MCP protocol)
219
+ console.error(`wwhat MCP client connected to ${MCP_URL}`);
220
+ }
221
+ main().catch((error) => {
222
+ console.error('Fatal error:', error);
223
+ process.exit(1);
224
+ });
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@wwhat/mcp-stdio-client",
3
+ "version": "1.0.0",
4
+ "description": "Stdio wrapper for wwhat MCP server - enables Cursor/Claude Desktop integration",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "wwhat-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/wwhat-ai/wwhat"
19
+ },
20
+ "author": "wwhat.ai",
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "cursor",
26
+ "claude-desktop",
27
+ "analytics",
28
+ "ga4"
29
+ ],
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.0.0",
32
+ "zod": "^3.23.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0",
36
+ "typescript": "^5.0.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ]
44
+ }