claude-plugin-wordpress-manager 2.4.0 → 2.9.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/.claude-plugin/plugin.json +10 -3
- package/CHANGELOG.md +103 -0
- package/agents/wp-content-strategist.md +104 -0
- package/agents/wp-monitoring-agent.md +44 -0
- package/agents/wp-site-manager.md +19 -0
- package/docs/GUIDE.md +183 -23
- package/docs/plans/2026-03-01-tier4-5-implementation.md +1783 -0
- package/docs/plans/2026-03-01-tier4-5-observability-automation-design.md +426 -0
- package/docs/plans/2026-03-01-wcop-reassessment-v2.6.0.md +403 -0
- package/hooks/hooks.json +9 -0
- package/package.json +19 -3
- package/servers/wp-rest-bridge/build/tools/comments.d.ts +6 -6
- package/servers/wp-rest-bridge/build/tools/cwv.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/cwv.js +196 -0
- package/servers/wp-rest-bridge/build/tools/ga4.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/ga4.js +323 -0
- package/servers/wp-rest-bridge/build/tools/gsc.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/gsc.js +354 -0
- package/servers/wp-rest-bridge/build/tools/index.d.ts +38 -38
- package/servers/wp-rest-bridge/build/tools/index.js +18 -0
- package/servers/wp-rest-bridge/build/tools/media.d.ts +2 -2
- package/servers/wp-rest-bridge/build/tools/multisite-sites.d.ts +2 -2
- package/servers/wp-rest-bridge/build/tools/plausible.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/plausible.js +207 -0
- package/servers/wp-rest-bridge/build/tools/plugin-repository.d.ts +1 -1
- package/servers/wp-rest-bridge/build/tools/search.d.ts +2 -2
- package/servers/wp-rest-bridge/build/tools/slack.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/slack.js +129 -0
- package/servers/wp-rest-bridge/build/tools/unified-content.d.ts +8 -8
- package/servers/wp-rest-bridge/build/tools/unified-taxonomies.d.ts +4 -4
- package/servers/wp-rest-bridge/build/tools/users.d.ts +6 -6
- package/servers/wp-rest-bridge/build/tools/wc-coupons.d.ts +1 -1
- package/servers/wp-rest-bridge/build/tools/wc-customers.d.ts +3 -3
- package/servers/wp-rest-bridge/build/tools/wc-orders.d.ts +4 -4
- package/servers/wp-rest-bridge/build/tools/wc-products.d.ts +8 -8
- package/servers/wp-rest-bridge/build/tools/wc-webhooks.d.ts +4 -4
- package/servers/wp-rest-bridge/build/tools/wc-workflows.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/wc-workflows.js +222 -0
- package/servers/wp-rest-bridge/build/wordpress.d.ts +23 -0
- package/servers/wp-rest-bridge/build/wordpress.js +178 -0
- package/servers/wp-rest-bridge/package.json +1 -0
- package/skills/wordpress-router/SKILL.md +1 -1
- package/skills/wordpress-router/references/decision-tree.md +12 -2
- package/skills/wp-alerting/SKILL.md +142 -0
- package/skills/wp-alerting/references/alert-thresholds.md +79 -0
- package/skills/wp-alerting/references/escalation-paths.md +92 -0
- package/skills/wp-alerting/references/report-scheduling.md +142 -0
- package/skills/wp-alerting/references/slack-integration.md +109 -0
- package/skills/wp-alerting/scripts/alerting_inspect.mjs +150 -0
- package/skills/wp-analytics/SKILL.md +158 -0
- package/skills/wp-analytics/references/analytics-dashboards.md +83 -0
- package/skills/wp-analytics/references/cwv-monitoring.md +101 -0
- package/skills/wp-analytics/references/ga4-integration.md +76 -0
- package/skills/wp-analytics/references/plausible-setup.md +92 -0
- package/skills/wp-analytics/references/traffic-attribution.md +92 -0
- package/skills/wp-analytics/scripts/analytics_inspect.mjs +153 -0
- package/skills/wp-content/SKILL.md +1 -0
- package/skills/wp-content-attribution/SKILL.md +3 -0
- package/skills/wp-content-optimization/SKILL.md +173 -0
- package/skills/wp-content-optimization/references/content-freshness.md +234 -0
- package/skills/wp-content-optimization/references/headline-optimization.md +171 -0
- package/skills/wp-content-optimization/references/meta-optimization.md +243 -0
- package/skills/wp-content-optimization/references/readability-analysis.md +201 -0
- package/skills/wp-content-optimization/references/seo-content-scoring.md +245 -0
- package/skills/wp-content-optimization/scripts/content_optimization_inspect.mjs +237 -0
- package/skills/wp-content-workflows/SKILL.md +142 -0
- package/skills/wp-content-workflows/references/content-lifecycle-hooks.md +131 -0
- package/skills/wp-content-workflows/references/multi-channel-actions.md +151 -0
- package/skills/wp-content-workflows/references/schedule-triggers.md +118 -0
- package/skills/wp-content-workflows/references/trigger-management.md +159 -0
- package/skills/wp-content-workflows/references/wp-action-hooks.md +114 -0
- package/skills/wp-content-workflows/scripts/workflow_inspect.mjs +202 -0
- package/skills/wp-monitoring/SKILL.md +3 -0
- package/skills/wp-programmatic-seo/SKILL.md +2 -0
- package/skills/wp-search-console/SKILL.md +122 -0
- package/skills/wp-search-console/references/competitor-gap-analysis.md +226 -0
- package/skills/wp-search-console/references/content-seo-feedback.md +181 -0
- package/skills/wp-search-console/references/gsc-setup.md +110 -0
- package/skills/wp-search-console/references/indexing-management.md +182 -0
- package/skills/wp-search-console/references/keyword-tracking.md +181 -0
- package/skills/wp-search-console/scripts/search_console_inspect.mjs +178 -0
- package/skills/wp-social-email/SKILL.md +1 -0
- package/skills/wp-webhooks/SKILL.md +1 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { hasGoogleApiKey, getGoogleApiKey } from '../wordpress.js';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
// ── Zod Schemas ─────────────────────────────────────────────────
|
|
5
|
+
const cwvAnalyzeUrlSchema = z.object({
|
|
6
|
+
url: z.string().describe('URL to analyze'),
|
|
7
|
+
strategy: z.enum(['mobile', 'desktop']).optional().default('mobile')
|
|
8
|
+
.describe('Analysis strategy (default: mobile)'),
|
|
9
|
+
categories: z.array(z.enum(['performance', 'accessibility', 'best-practices', 'seo'])).optional()
|
|
10
|
+
.describe('Lighthouse categories to include (default: performance only)'),
|
|
11
|
+
}).strict();
|
|
12
|
+
const cwvBatchAnalyzeSchema = z.object({
|
|
13
|
+
urls: z.array(z.string()).describe('Array of URLs to analyze (max 10)'),
|
|
14
|
+
strategy: z.enum(['mobile', 'desktop']).optional().default('mobile'),
|
|
15
|
+
}).strict();
|
|
16
|
+
const cwvGetFieldDataSchema = z.object({
|
|
17
|
+
url: z.string().optional().describe('Specific URL for field data (omit for origin-level data)'),
|
|
18
|
+
origin: z.string().optional().describe('Origin URL (e.g., https://mysite.com)'),
|
|
19
|
+
form_factor: z.enum(['PHONE', 'DESKTOP', 'TABLET', 'ALL_FORM_FACTORS']).optional().default('ALL_FORM_FACTORS'),
|
|
20
|
+
}).strict();
|
|
21
|
+
const cwvComparePagesSchema = z.object({
|
|
22
|
+
urls: z.array(z.string()).describe('URLs to compare (2-5)'),
|
|
23
|
+
strategy: z.enum(['mobile', 'desktop']).optional().default('mobile'),
|
|
24
|
+
}).strict();
|
|
25
|
+
// ── Tool Definitions ────────────────────────────────────────────
|
|
26
|
+
export const cwvTools = [
|
|
27
|
+
{
|
|
28
|
+
name: "cwv_analyze_url",
|
|
29
|
+
description: "Analyzes Core Web Vitals for a URL via PageSpeed Insights (LCP, INP, CLS, FCP, TTFB)",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
url: { type: "string", description: "URL to analyze" },
|
|
34
|
+
strategy: { type: "string", enum: ["mobile", "desktop"], description: "Strategy (default: mobile)" },
|
|
35
|
+
categories: { type: "array", items: { type: "string", enum: ["performance", "accessibility", "best-practices", "seo"] }, description: "Categories to include" },
|
|
36
|
+
},
|
|
37
|
+
required: ["url"],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "cwv_batch_analyze",
|
|
42
|
+
description: "Analyzes Core Web Vitals for multiple URLs (max 10) via PageSpeed Insights",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
urls: { type: "array", items: { type: "string" }, description: "URLs to analyze (max 10)" },
|
|
47
|
+
strategy: { type: "string", enum: ["mobile", "desktop"], description: "Strategy (default: mobile)" },
|
|
48
|
+
},
|
|
49
|
+
required: ["urls"],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "cwv_get_field_data",
|
|
54
|
+
description: "Gets real-user CWV field data from Chrome UX Report (28-day aggregate)",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
url: { type: "string", description: "Specific URL (omit for origin-level)" },
|
|
59
|
+
origin: { type: "string", description: "Origin URL (e.g., https://mysite.com)" },
|
|
60
|
+
form_factor: { type: "string", enum: ["PHONE", "DESKTOP", "TABLET", "ALL_FORM_FACTORS"], description: "Form factor (default: ALL)" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "cwv_compare_pages",
|
|
66
|
+
description: "Compares Core Web Vitals across multiple pages and ranks optimization priority",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
urls: { type: "array", items: { type: "string" }, description: "URLs to compare (2-5)" },
|
|
71
|
+
strategy: { type: "string", enum: ["mobile", "desktop"], description: "Strategy (default: mobile)" },
|
|
72
|
+
},
|
|
73
|
+
required: ["urls"],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
// ── Handlers ────────────────────────────────────────────────────
|
|
78
|
+
const PSI_BASE = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed';
|
|
79
|
+
const CRUX_BASE = 'https://chromeuxreport.googleapis.com/v1/records:queryRecord';
|
|
80
|
+
function extractCWV(lighthouse) {
|
|
81
|
+
const audits = lighthouse?.audits || {};
|
|
82
|
+
return {
|
|
83
|
+
lcp: audits['largest-contentful-paint']?.numericValue,
|
|
84
|
+
fcp: audits['first-contentful-paint']?.numericValue,
|
|
85
|
+
cls: audits['cumulative-layout-shift']?.numericValue,
|
|
86
|
+
inp: audits['interaction-to-next-paint']?.numericValue,
|
|
87
|
+
ttfb: audits['server-response-time']?.numericValue,
|
|
88
|
+
performance_score: lighthouse?.categories?.performance?.score,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export const cwvHandlers = {
|
|
92
|
+
cwv_analyze_url: async (params) => {
|
|
93
|
+
if (!hasGoogleApiKey()) {
|
|
94
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Google API key not configured. Add google_api_key to WP_SITES_CONFIG." }] } };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const { url, strategy, categories } = params;
|
|
98
|
+
const apiKey = getGoogleApiKey();
|
|
99
|
+
const queryParams = { url, key: apiKey, strategy: strategy || 'mobile' };
|
|
100
|
+
queryParams.category = categories && categories.length > 0 ? categories : ['performance'];
|
|
101
|
+
const response = await axios.get(PSI_BASE, { params: queryParams, timeout: 60000 });
|
|
102
|
+
const cwv = extractCWV(response.data.lighthouseResult);
|
|
103
|
+
const result = { url, strategy: strategy || 'mobile', cwv, fieldData: response.data.loadingExperience || null };
|
|
104
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] } };
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
108
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error analyzing URL: ${errorMessage}` }] } };
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
cwv_batch_analyze: async (params) => {
|
|
112
|
+
if (!hasGoogleApiKey()) {
|
|
113
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Google API key not configured. Add google_api_key to WP_SITES_CONFIG." }] } };
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const { urls, strategy } = params;
|
|
117
|
+
const limitedUrls = urls.slice(0, 10);
|
|
118
|
+
const apiKey = getGoogleApiKey();
|
|
119
|
+
const results = [];
|
|
120
|
+
for (const url of limitedUrls) {
|
|
121
|
+
try {
|
|
122
|
+
const response = await axios.get(PSI_BASE, {
|
|
123
|
+
params: { url, key: apiKey, strategy: strategy || 'mobile', category: 'performance' },
|
|
124
|
+
timeout: 60000,
|
|
125
|
+
});
|
|
126
|
+
const cwv = extractCWV(response.data.lighthouseResult);
|
|
127
|
+
results.push({ url, cwv, status: 'ok' });
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
results.push({ url, status: 'error', error: err.response?.data?.error?.message || err.message });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] } };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error in batch analysis: ${error.message}` }] } };
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
cwv_get_field_data: async (params) => {
|
|
140
|
+
if (!hasGoogleApiKey()) {
|
|
141
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Google API key not configured. Add google_api_key to WP_SITES_CONFIG." }] } };
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const { url, origin, form_factor } = params;
|
|
145
|
+
const apiKey = getGoogleApiKey();
|
|
146
|
+
const requestBody = {};
|
|
147
|
+
if (url)
|
|
148
|
+
requestBody.url = url;
|
|
149
|
+
else if (origin)
|
|
150
|
+
requestBody.origin = origin;
|
|
151
|
+
else
|
|
152
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Provide either url or origin parameter." }] } };
|
|
153
|
+
if (form_factor && form_factor !== 'ALL_FORM_FACTORS')
|
|
154
|
+
requestBody.formFactor = form_factor;
|
|
155
|
+
const response = await axios.post(CRUX_BASE, requestBody, { params: { key: apiKey }, timeout: 30000 });
|
|
156
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
160
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting CrUX data: ${errorMessage}` }] } };
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
cwv_compare_pages: async (params) => {
|
|
164
|
+
if (!hasGoogleApiKey()) {
|
|
165
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Google API key not configured. Add google_api_key to WP_SITES_CONFIG." }] } };
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const { urls, strategy } = params;
|
|
169
|
+
const limitedUrls = urls.slice(0, 5);
|
|
170
|
+
const apiKey = getGoogleApiKey();
|
|
171
|
+
const results = [];
|
|
172
|
+
for (const url of limitedUrls) {
|
|
173
|
+
try {
|
|
174
|
+
const response = await axios.get(PSI_BASE, {
|
|
175
|
+
params: { url, key: apiKey, strategy: strategy || 'mobile', category: 'performance' },
|
|
176
|
+
timeout: 60000,
|
|
177
|
+
});
|
|
178
|
+
const cwv = extractCWV(response.data.lighthouseResult);
|
|
179
|
+
results.push({ url, cwv });
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
results.push({ url, cwv: null, error: err.message });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Rank by worst LCP
|
|
186
|
+
const ranked = results
|
|
187
|
+
.filter(r => r.cwv)
|
|
188
|
+
.sort((a, b) => (b.cwv.lcp || 0) - (a.cwv.lcp || 0))
|
|
189
|
+
.map((r, i) => ({ ...r, priority: i + 1 }));
|
|
190
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify({ comparison: ranked, worst_first: true }, null, 2) }] } };
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error comparing pages: ${error.message}` }] } };
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { hasGA4, getGA4Auth, getGA4PropertyId } from '../wordpress.js';
|
|
2
|
+
import { google } from 'googleapis';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
// ── Zod Schemas ─────────────────────────────────────────────────
|
|
5
|
+
const ga4RunReportSchema = z.object({
|
|
6
|
+
dimensions: z.array(z.string()).describe('GA4 dimensions (e.g. pagePath, sessionSource, country)'),
|
|
7
|
+
metrics: z.array(z.string()).describe('GA4 metrics (e.g. screenPageViews, activeUsers, sessions)'),
|
|
8
|
+
start_date: z.string().describe('Start date in YYYY-MM-DD format'),
|
|
9
|
+
end_date: z.string().describe('End date in YYYY-MM-DD format'),
|
|
10
|
+
limit: z.number().optional().default(100)
|
|
11
|
+
.describe('Maximum number of rows to return (default 100)'),
|
|
12
|
+
}).strict();
|
|
13
|
+
const ga4GetRealtimeSchema = z.object({
|
|
14
|
+
metrics: z.array(z.string()).optional().default(['activeUsers'])
|
|
15
|
+
.describe('Real-time metrics (default: activeUsers)'),
|
|
16
|
+
dimensions: z.array(z.string()).optional()
|
|
17
|
+
.describe('Real-time dimensions (optional, e.g. unifiedScreenName)'),
|
|
18
|
+
}).strict();
|
|
19
|
+
const ga4TopPagesSchema = z.object({
|
|
20
|
+
start_date: z.string().describe('Start date in YYYY-MM-DD format'),
|
|
21
|
+
end_date: z.string().describe('End date in YYYY-MM-DD format'),
|
|
22
|
+
limit: z.number().optional().default(25)
|
|
23
|
+
.describe('Number of top pages to return (default 25)'),
|
|
24
|
+
}).strict();
|
|
25
|
+
const ga4TrafficSourcesSchema = z.object({
|
|
26
|
+
start_date: z.string().describe('Start date in YYYY-MM-DD format'),
|
|
27
|
+
end_date: z.string().describe('End date in YYYY-MM-DD format'),
|
|
28
|
+
limit: z.number().optional().default(25)
|
|
29
|
+
.describe('Number of traffic sources to return (default 25)'),
|
|
30
|
+
}).strict();
|
|
31
|
+
const ga4UserDemographicsSchema = z.object({
|
|
32
|
+
start_date: z.string().describe('Start date in YYYY-MM-DD format'),
|
|
33
|
+
end_date: z.string().describe('End date in YYYY-MM-DD format'),
|
|
34
|
+
breakdown: z.enum(['country', 'deviceCategory', 'browser']).optional().default('country')
|
|
35
|
+
.describe('Breakdown dimension (default: country)'),
|
|
36
|
+
limit: z.number().optional().default(25)
|
|
37
|
+
.describe('Number of rows to return (default 25)'),
|
|
38
|
+
}).strict();
|
|
39
|
+
const ga4ConversionEventsSchema = z.object({
|
|
40
|
+
start_date: z.string().describe('Start date in YYYY-MM-DD format'),
|
|
41
|
+
end_date: z.string().describe('End date in YYYY-MM-DD format'),
|
|
42
|
+
limit: z.number().optional().default(25)
|
|
43
|
+
.describe('Number of conversion events to return (default 25)'),
|
|
44
|
+
}).strict();
|
|
45
|
+
// ── Tool Definitions ────────────────────────────────────────────
|
|
46
|
+
export const ga4Tools = [
|
|
47
|
+
{
|
|
48
|
+
name: "ga4_run_report",
|
|
49
|
+
description: "Runs a custom GA4 report with specified dimensions, metrics, and date range",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
dimensions: {
|
|
54
|
+
type: "array",
|
|
55
|
+
items: { type: "string" },
|
|
56
|
+
description: "GA4 dimensions (e.g. pagePath, sessionSource, country)",
|
|
57
|
+
},
|
|
58
|
+
metrics: {
|
|
59
|
+
type: "array",
|
|
60
|
+
items: { type: "string" },
|
|
61
|
+
description: "GA4 metrics (e.g. screenPageViews, activeUsers, sessions)",
|
|
62
|
+
},
|
|
63
|
+
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
64
|
+
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
65
|
+
limit: { type: "number", description: "Maximum number of rows to return (default 100)" },
|
|
66
|
+
},
|
|
67
|
+
required: ["dimensions", "metrics", "start_date", "end_date"],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "ga4_get_realtime",
|
|
72
|
+
description: "Gets real-time active users and metrics from GA4",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
metrics: {
|
|
77
|
+
type: "array",
|
|
78
|
+
items: { type: "string" },
|
|
79
|
+
description: "Real-time metrics (default: activeUsers)",
|
|
80
|
+
},
|
|
81
|
+
dimensions: {
|
|
82
|
+
type: "array",
|
|
83
|
+
items: { type: "string" },
|
|
84
|
+
description: "Real-time dimensions (optional, e.g. unifiedScreenName)",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "ga4_top_pages",
|
|
91
|
+
description: "Gets top pages by pageviews from GA4 (convenience shortcut for ga4_run_report)",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
96
|
+
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
97
|
+
limit: { type: "number", description: "Number of top pages to return (default 25)" },
|
|
98
|
+
},
|
|
99
|
+
required: ["start_date", "end_date"],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "ga4_traffic_sources",
|
|
104
|
+
description: "Gets traffic breakdown by source/medium from GA4 (convenience shortcut for ga4_run_report)",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
109
|
+
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
110
|
+
limit: { type: "number", description: "Number of traffic sources to return (default 25)" },
|
|
111
|
+
},
|
|
112
|
+
required: ["start_date", "end_date"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "ga4_user_demographics",
|
|
117
|
+
description: "Gets user demographics breakdown (country, device, or browser) from GA4 (convenience shortcut)",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
122
|
+
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
123
|
+
breakdown: {
|
|
124
|
+
type: "string",
|
|
125
|
+
enum: ["country", "deviceCategory", "browser"],
|
|
126
|
+
description: "Breakdown dimension (default: country)",
|
|
127
|
+
},
|
|
128
|
+
limit: { type: "number", description: "Number of rows to return (default 25)" },
|
|
129
|
+
},
|
|
130
|
+
required: ["start_date", "end_date"],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "ga4_conversion_events",
|
|
135
|
+
description: "Gets conversion events and rates from GA4 (convenience shortcut for ga4_run_report)",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
140
|
+
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
141
|
+
limit: { type: "number", description: "Number of conversion events to return (default 25)" },
|
|
142
|
+
},
|
|
143
|
+
required: ["start_date", "end_date"],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
// ── Handlers ────────────────────────────────────────────────────
|
|
148
|
+
export const ga4Handlers = {
|
|
149
|
+
ga4_run_report: async (params) => {
|
|
150
|
+
if (!hasGA4()) {
|
|
151
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const auth = await getGA4Auth();
|
|
155
|
+
const propertyId = getGA4PropertyId();
|
|
156
|
+
const { dimensions, metrics, start_date, end_date, limit } = params;
|
|
157
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
158
|
+
const response = await analyticsdata.properties.runReport({
|
|
159
|
+
property: propertyId,
|
|
160
|
+
requestBody: {
|
|
161
|
+
dateRanges: [{ startDate: start_date, endDate: end_date }],
|
|
162
|
+
dimensions: dimensions.map(d => ({ name: d })),
|
|
163
|
+
metrics: metrics.map(m => ({ name: m })),
|
|
164
|
+
limit: String(limit || 100),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
171
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error running GA4 report: ${errorMessage}` }] } };
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
ga4_get_realtime: async (params) => {
|
|
175
|
+
if (!hasGA4()) {
|
|
176
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const auth = await getGA4Auth();
|
|
180
|
+
const propertyId = getGA4PropertyId();
|
|
181
|
+
const { metrics, dimensions } = params;
|
|
182
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
183
|
+
const requestBody = {
|
|
184
|
+
metrics: (metrics || ['activeUsers']).map(m => ({ name: m })),
|
|
185
|
+
};
|
|
186
|
+
if (dimensions && dimensions.length > 0) {
|
|
187
|
+
requestBody.dimensions = dimensions.map(d => ({ name: d }));
|
|
188
|
+
}
|
|
189
|
+
const response = await analyticsdata.properties.runRealtimeReport({
|
|
190
|
+
property: propertyId,
|
|
191
|
+
requestBody,
|
|
192
|
+
});
|
|
193
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
197
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting GA4 realtime data: ${errorMessage}` }] } };
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
ga4_top_pages: async (params) => {
|
|
201
|
+
if (!hasGA4()) {
|
|
202
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const auth = await getGA4Auth();
|
|
206
|
+
const propertyId = getGA4PropertyId();
|
|
207
|
+
const { start_date, end_date, limit } = params;
|
|
208
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
209
|
+
const response = await analyticsdata.properties.runReport({
|
|
210
|
+
property: propertyId,
|
|
211
|
+
requestBody: {
|
|
212
|
+
dateRanges: [{ startDate: start_date, endDate: end_date }],
|
|
213
|
+
dimensions: [{ name: 'pagePath' }],
|
|
214
|
+
metrics: [
|
|
215
|
+
{ name: 'screenPageViews' },
|
|
216
|
+
{ name: 'activeUsers' },
|
|
217
|
+
{ name: 'averageSessionDuration' },
|
|
218
|
+
],
|
|
219
|
+
orderBys: [{ metric: { metricName: 'screenPageViews' }, desc: true }],
|
|
220
|
+
limit: String(limit || 25),
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
227
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting GA4 top pages: ${errorMessage}` }] } };
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
ga4_traffic_sources: async (params) => {
|
|
231
|
+
if (!hasGA4()) {
|
|
232
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const auth = await getGA4Auth();
|
|
236
|
+
const propertyId = getGA4PropertyId();
|
|
237
|
+
const { start_date, end_date, limit } = params;
|
|
238
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
239
|
+
const response = await analyticsdata.properties.runReport({
|
|
240
|
+
property: propertyId,
|
|
241
|
+
requestBody: {
|
|
242
|
+
dateRanges: [{ startDate: start_date, endDate: end_date }],
|
|
243
|
+
dimensions: [
|
|
244
|
+
{ name: 'sessionSource' },
|
|
245
|
+
{ name: 'sessionMedium' },
|
|
246
|
+
],
|
|
247
|
+
metrics: [
|
|
248
|
+
{ name: 'sessions' },
|
|
249
|
+
{ name: 'activeUsers' },
|
|
250
|
+
{ name: 'conversions' },
|
|
251
|
+
],
|
|
252
|
+
orderBys: [{ metric: { metricName: 'sessions' }, desc: true }],
|
|
253
|
+
limit: String(limit || 25),
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
260
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting GA4 traffic sources: ${errorMessage}` }] } };
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
ga4_user_demographics: async (params) => {
|
|
264
|
+
if (!hasGA4()) {
|
|
265
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const auth = await getGA4Auth();
|
|
269
|
+
const propertyId = getGA4PropertyId();
|
|
270
|
+
const { start_date, end_date, breakdown, limit } = params;
|
|
271
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
272
|
+
const response = await analyticsdata.properties.runReport({
|
|
273
|
+
property: propertyId,
|
|
274
|
+
requestBody: {
|
|
275
|
+
dateRanges: [{ startDate: start_date, endDate: end_date }],
|
|
276
|
+
dimensions: [{ name: breakdown || 'country' }],
|
|
277
|
+
metrics: [
|
|
278
|
+
{ name: 'activeUsers' },
|
|
279
|
+
{ name: 'sessions' },
|
|
280
|
+
{ name: 'screenPageViews' },
|
|
281
|
+
],
|
|
282
|
+
orderBys: [{ metric: { metricName: 'activeUsers' }, desc: true }],
|
|
283
|
+
limit: String(limit || 25),
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
290
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting GA4 user demographics: ${errorMessage}` }] } };
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
ga4_conversion_events: async (params) => {
|
|
294
|
+
if (!hasGA4()) {
|
|
295
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "GA4 not configured. Add ga4_property_id and ga4_service_account_key to WP_SITES_CONFIG." }] } };
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const auth = await getGA4Auth();
|
|
299
|
+
const propertyId = getGA4PropertyId();
|
|
300
|
+
const { start_date, end_date, limit } = params;
|
|
301
|
+
const analyticsdata = google.analyticsdata({ version: 'v1beta', auth });
|
|
302
|
+
const response = await analyticsdata.properties.runReport({
|
|
303
|
+
property: propertyId,
|
|
304
|
+
requestBody: {
|
|
305
|
+
dateRanges: [{ startDate: start_date, endDate: end_date }],
|
|
306
|
+
dimensions: [{ name: 'eventName' }],
|
|
307
|
+
metrics: [
|
|
308
|
+
{ name: 'eventCount' },
|
|
309
|
+
{ name: 'conversions' },
|
|
310
|
+
{ name: 'totalRevenue' },
|
|
311
|
+
],
|
|
312
|
+
orderBys: [{ metric: { metricName: 'conversions' }, desc: true }],
|
|
313
|
+
limit: String(limit || 25),
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] } };
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
const errorMessage = error.response?.data?.error?.message || error.message;
|
|
320
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error getting GA4 conversion events: ${errorMessage}` }] } };
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
};
|