@heyseo/mcp-server 0.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.
Files changed (46) hide show
  1. package/README.md +284 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +258 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/prompts/index.d.ts +22 -0
  7. package/dist/prompts/index.d.ts.map +1 -0
  8. package/dist/prompts/index.js +267 -0
  9. package/dist/prompts/index.js.map +1 -0
  10. package/dist/resources/index.d.ts +38 -0
  11. package/dist/resources/index.d.ts.map +1 -0
  12. package/dist/resources/index.js +247 -0
  13. package/dist/resources/index.js.map +1 -0
  14. package/dist/tools/comparison.d.ts +159 -0
  15. package/dist/tools/comparison.d.ts.map +1 -0
  16. package/dist/tools/comparison.js +482 -0
  17. package/dist/tools/comparison.js.map +1 -0
  18. package/dist/tools/ga4.d.ts +182 -0
  19. package/dist/tools/ga4.d.ts.map +1 -0
  20. package/dist/tools/ga4.js +429 -0
  21. package/dist/tools/ga4.js.map +1 -0
  22. package/dist/tools/gsc.d.ts +194 -0
  23. package/dist/tools/gsc.d.ts.map +1 -0
  24. package/dist/tools/gsc.js +348 -0
  25. package/dist/tools/gsc.js.map +1 -0
  26. package/dist/tools/index.d.ts +392 -0
  27. package/dist/tools/index.d.ts.map +1 -0
  28. package/dist/tools/index.js +59 -0
  29. package/dist/tools/index.js.map +1 -0
  30. package/dist/tools/pagespeed.d.ts +88 -0
  31. package/dist/tools/pagespeed.d.ts.map +1 -0
  32. package/dist/tools/pagespeed.js +285 -0
  33. package/dist/tools/pagespeed.js.map +1 -0
  34. package/dist/tools/tasks.d.ts +71 -0
  35. package/dist/tools/tasks.d.ts.map +1 -0
  36. package/dist/tools/tasks.js +116 -0
  37. package/dist/tools/tasks.js.map +1 -0
  38. package/dist/types.d.ts +151 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +5 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/utils/api-client.d.ts +69 -0
  43. package/dist/utils/api-client.d.ts.map +1 -0
  44. package/dist/utils/api-client.js +202 -0
  45. package/dist/utils/api-client.js.map +1 -0
  46. package/package.json +42 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Comparison and Opportunities Tools
3
+ * Tools for comparing time periods and finding SEO opportunities
4
+ */
5
+ import { z } from 'zod';
6
+ import type { HeySeoApiClient } from '../utils/api-client.js';
7
+ export declare const comparePeriodSchema: z.ZodObject<{
8
+ siteId: z.ZodString;
9
+ period1Start: z.ZodString;
10
+ period1End: z.ZodString;
11
+ period2Start: z.ZodString;
12
+ period2End: z.ZodString;
13
+ includeGA4: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ siteId: string;
16
+ period1Start: string;
17
+ period1End: string;
18
+ period2Start: string;
19
+ period2End: string;
20
+ includeGA4: boolean;
21
+ }, {
22
+ siteId: string;
23
+ period1Start: string;
24
+ period1End: string;
25
+ period2Start: string;
26
+ period2End: string;
27
+ includeGA4?: boolean | undefined;
28
+ }>;
29
+ export type ComparePeriodInput = z.infer<typeof comparePeriodSchema>;
30
+ export declare const findOpportunitiesSchema: z.ZodObject<{
31
+ siteId: z.ZodString;
32
+ type: z.ZodDefault<z.ZodOptional<z.ZodEnum<["keyword", "page", "technical", "content", "all"]>>>;
33
+ }, "strip", z.ZodTypeAny, {
34
+ siteId: string;
35
+ type: "page" | "keyword" | "technical" | "content" | "all";
36
+ }, {
37
+ siteId: string;
38
+ type?: "page" | "keyword" | "technical" | "content" | "all" | undefined;
39
+ }>;
40
+ export type FindOpportunitiesInput = z.infer<typeof findOpportunitiesSchema>;
41
+ export declare const weekOverWeekSchema: z.ZodObject<{
42
+ siteId: z.ZodString;
43
+ weeksBack: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ siteId: string;
46
+ weeksBack: number;
47
+ }, {
48
+ siteId: string;
49
+ weeksBack?: number | undefined;
50
+ }>;
51
+ export type WeekOverWeekInput = z.infer<typeof weekOverWeekSchema>;
52
+ /**
53
+ * Execute period comparison tool
54
+ */
55
+ export declare function executeComparePeriod(client: HeySeoApiClient, input: ComparePeriodInput): Promise<string>;
56
+ /**
57
+ * Execute find opportunities tool
58
+ */
59
+ export declare function executeFindOpportunities(client: HeySeoApiClient, input: FindOpportunitiesInput): Promise<string>;
60
+ /**
61
+ * Execute week-over-week comparison
62
+ */
63
+ export declare function executeWeekOverWeek(client: HeySeoApiClient, input: WeekOverWeekInput): Promise<string>;
64
+ /**
65
+ * Comparison tool definitions for MCP
66
+ */
67
+ export declare const comparisonTools: ({
68
+ name: string;
69
+ description: string;
70
+ inputSchema: {
71
+ type: string;
72
+ properties: {
73
+ siteId: {
74
+ type: string;
75
+ description: string;
76
+ };
77
+ period1Start: {
78
+ type: string;
79
+ pattern: string;
80
+ description: string;
81
+ };
82
+ period1End: {
83
+ type: string;
84
+ pattern: string;
85
+ description: string;
86
+ };
87
+ period2Start: {
88
+ type: string;
89
+ pattern: string;
90
+ description: string;
91
+ };
92
+ period2End: {
93
+ type: string;
94
+ pattern: string;
95
+ description: string;
96
+ };
97
+ includeGA4: {
98
+ type: string;
99
+ default: boolean;
100
+ description: string;
101
+ };
102
+ weeksBack?: undefined;
103
+ type?: undefined;
104
+ };
105
+ required: string[];
106
+ };
107
+ } | {
108
+ name: string;
109
+ description: string;
110
+ inputSchema: {
111
+ type: string;
112
+ properties: {
113
+ siteId: {
114
+ type: string;
115
+ description: string;
116
+ };
117
+ weeksBack: {
118
+ type: string;
119
+ minimum: number;
120
+ maximum: number;
121
+ default: number;
122
+ description: string;
123
+ };
124
+ period1Start?: undefined;
125
+ period1End?: undefined;
126
+ period2Start?: undefined;
127
+ period2End?: undefined;
128
+ includeGA4?: undefined;
129
+ type?: undefined;
130
+ };
131
+ required: string[];
132
+ };
133
+ } | {
134
+ name: string;
135
+ description: string;
136
+ inputSchema: {
137
+ type: string;
138
+ properties: {
139
+ siteId: {
140
+ type: string;
141
+ description: string;
142
+ };
143
+ type: {
144
+ type: string;
145
+ enum: string[];
146
+ default: string;
147
+ description: string;
148
+ };
149
+ period1Start?: undefined;
150
+ period1End?: undefined;
151
+ period2Start?: undefined;
152
+ period2End?: undefined;
153
+ includeGA4?: undefined;
154
+ weeksBack?: undefined;
155
+ };
156
+ required: string[];
157
+ };
158
+ })[];
159
+ //# sourceMappingURL=comparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparison.d.ts","sourceRoot":"","sources":["../../src/tools/comparison.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAI9D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;EAuB9B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAGrE,eAAO,MAAM,uBAAuB;;;;;;;;;EAOlC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAG7E,eAAO,MAAM,kBAAkB;;;;;;;;;EAS7B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAyCnE;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,MAAM,CAAC,CA6IjB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAwKjB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoG3B,CAAC"}
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Comparison and Opportunities Tools
3
+ * Tools for comparing time periods and finding SEO opportunities
4
+ */
5
+ import { z } from 'zod';
6
+ // Schema for period comparison tool
7
+ export const comparePeriodSchema = z.object({
8
+ siteId: z.string().describe('The ID of the site to compare'),
9
+ period1Start: z
10
+ .string()
11
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
12
+ .describe('First period start date (YYYY-MM-DD)'),
13
+ period1End: z
14
+ .string()
15
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
16
+ .describe('First period end date (YYYY-MM-DD)'),
17
+ period2Start: z
18
+ .string()
19
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
20
+ .describe('Second period start date (YYYY-MM-DD)'),
21
+ period2End: z
22
+ .string()
23
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
24
+ .describe('Second period end date (YYYY-MM-DD)'),
25
+ includeGA4: z
26
+ .boolean()
27
+ .optional()
28
+ .default(true)
29
+ .describe('Include GA4 metrics in comparison'),
30
+ });
31
+ // Schema for opportunities tool
32
+ export const findOpportunitiesSchema = z.object({
33
+ siteId: z.string().describe('The ID of the site to analyze'),
34
+ type: z
35
+ .enum(['keyword', 'page', 'technical', 'content', 'all'])
36
+ .optional()
37
+ .default('all')
38
+ .describe('Type of opportunities to find'),
39
+ });
40
+ // Schema for week-over-week comparison
41
+ export const weekOverWeekSchema = z.object({
42
+ siteId: z.string().describe('The ID of the site to compare'),
43
+ weeksBack: z
44
+ .number()
45
+ .min(1)
46
+ .max(12)
47
+ .optional()
48
+ .default(1)
49
+ .describe('Number of weeks to compare against (default: last week)'),
50
+ });
51
+ /**
52
+ * Calculate percentage change
53
+ */
54
+ function calculateChange(current, previous) {
55
+ const absolute = current - previous;
56
+ const percentage = previous !== 0 ? ((current - previous) / previous) * 100 : 0;
57
+ let trend;
58
+ if (Math.abs(percentage) < 1) {
59
+ trend = 'stable';
60
+ }
61
+ else if (percentage > 0) {
62
+ trend = 'up';
63
+ }
64
+ else {
65
+ trend = 'down';
66
+ }
67
+ return { absolute, percentage, trend };
68
+ }
69
+ /**
70
+ * Get trend emoji
71
+ */
72
+ function getTrendEmoji(trend, metric) {
73
+ // For position, down is good (lower number = better rank)
74
+ if (metric === 'position') {
75
+ if (trend === 'down')
76
+ return '✅ ↓';
77
+ if (trend === 'up')
78
+ return '⚠️ ↑';
79
+ return '➡️';
80
+ }
81
+ // For other metrics, up is good
82
+ if (trend === 'up')
83
+ return '✅ ↑';
84
+ if (trend === 'down')
85
+ return '⚠️ ↓';
86
+ return '➡️';
87
+ }
88
+ /**
89
+ * Execute period comparison tool
90
+ */
91
+ export async function executeComparePeriod(client, input) {
92
+ try {
93
+ // Fetch GSC data for both periods
94
+ const [gsc1, gsc2] = await Promise.all([
95
+ client.queryGSC({
96
+ siteId: input.siteId,
97
+ startDate: input.period1Start,
98
+ endDate: input.period1End,
99
+ dimensions: [],
100
+ rowLimit: 1,
101
+ }),
102
+ client.queryGSC({
103
+ siteId: input.siteId,
104
+ startDate: input.period2Start,
105
+ endDate: input.period2End,
106
+ dimensions: [],
107
+ rowLimit: 1,
108
+ }),
109
+ ]);
110
+ // Extract metrics
111
+ const metrics1 = gsc1.rows[0] || { clicks: 0, impressions: 0, ctr: 0, position: 0 };
112
+ const metrics2 = gsc2.rows[0] || { clicks: 0, impressions: 0, ctr: 0, position: 0 };
113
+ // Calculate changes
114
+ const changes = {
115
+ clicks: calculateChange(metrics2.clicks, metrics1.clicks),
116
+ impressions: calculateChange(metrics2.impressions, metrics1.impressions),
117
+ ctr: calculateChange(metrics2.ctr * 100, metrics1.ctr * 100),
118
+ position: calculateChange(metrics2.position, metrics1.position),
119
+ };
120
+ // Format comparison
121
+ const comparison = {
122
+ clicks: {
123
+ period1: metrics1.clicks,
124
+ period2: metrics2.clicks,
125
+ change: `${changes.clicks.percentage >= 0 ? '+' : ''}${changes.clicks.percentage.toFixed(1)}%`,
126
+ trend: getTrendEmoji(changes.clicks.trend, 'clicks'),
127
+ },
128
+ impressions: {
129
+ period1: metrics1.impressions,
130
+ period2: metrics2.impressions,
131
+ change: `${changes.impressions.percentage >= 0 ? '+' : ''}${changes.impressions.percentage.toFixed(1)}%`,
132
+ trend: getTrendEmoji(changes.impressions.trend, 'impressions'),
133
+ },
134
+ ctr: {
135
+ period1: `${(metrics1.ctr * 100).toFixed(2)}%`,
136
+ period2: `${(metrics2.ctr * 100).toFixed(2)}%`,
137
+ change: `${changes.ctr.absolute >= 0 ? '+' : ''}${changes.ctr.absolute.toFixed(2)}pp`,
138
+ trend: getTrendEmoji(changes.ctr.trend, 'ctr'),
139
+ },
140
+ position: {
141
+ period1: metrics1.position.toFixed(1),
142
+ period2: metrics2.position.toFixed(1),
143
+ change: `${changes.position.absolute >= 0 ? '+' : ''}${changes.position.absolute.toFixed(1)}`,
144
+ trend: getTrendEmoji(changes.position.trend, 'position'),
145
+ },
146
+ };
147
+ // Determine overall health
148
+ const positiveChanges = [
149
+ changes.clicks.trend === 'up',
150
+ changes.impressions.trend === 'up',
151
+ changes.ctr.trend === 'up',
152
+ changes.position.trend === 'down', // Lower position is better
153
+ ].filter(Boolean).length;
154
+ let overallHealth;
155
+ if (positiveChanges >= 3) {
156
+ overallHealth = '🟢 Strong improvement';
157
+ }
158
+ else if (positiveChanges >= 2) {
159
+ overallHealth = '🟡 Mixed results';
160
+ }
161
+ else {
162
+ overallHealth = '🔴 Needs attention';
163
+ }
164
+ // Find top gainers and losers (by query)
165
+ const [queries1, queries2] = await Promise.all([
166
+ client.queryGSC({
167
+ siteId: input.siteId,
168
+ startDate: input.period1Start,
169
+ endDate: input.period1End,
170
+ dimensions: ['query'],
171
+ rowLimit: 100,
172
+ }),
173
+ client.queryGSC({
174
+ siteId: input.siteId,
175
+ startDate: input.period2Start,
176
+ endDate: input.period2End,
177
+ dimensions: ['query'],
178
+ rowLimit: 100,
179
+ }),
180
+ ]);
181
+ // Create maps for comparison
182
+ const queries1Map = new Map(queries1.rows.map((r) => [r.keys[0], r]));
183
+ const queries2Map = new Map(queries2.rows.map((r) => [r.keys[0], r]));
184
+ // Find changes
185
+ const queryChanges = [];
186
+ queries2Map.forEach((row, query) => {
187
+ const prev = queries1Map.get(query);
188
+ const prevClicks = prev?.clicks || 0;
189
+ const clickChange = row.clicks - prevClicks;
190
+ const clickChangePct = prevClicks > 0 ? (clickChange / prevClicks) * 100 : 100;
191
+ queryChanges.push({ query, clickChange, clickChangePct });
192
+ });
193
+ // Sort to find top gainers and losers
194
+ const sortedByChange = [...queryChanges].sort((a, b) => b.clickChange - a.clickChange);
195
+ const topGainers = sortedByChange.slice(0, 5).filter((q) => q.clickChange > 0);
196
+ const topLosers = sortedByChange.slice(-5).reverse().filter((q) => q.clickChange < 0);
197
+ return JSON.stringify({
198
+ success: true,
199
+ periods: {
200
+ period1: { start: input.period1Start, end: input.period1End },
201
+ period2: { start: input.period2Start, end: input.period2End },
202
+ },
203
+ overallHealth,
204
+ comparison,
205
+ topGainers: topGainers.map((q) => ({
206
+ query: q.query,
207
+ change: `+${q.clickChange} clicks (+${q.clickChangePct.toFixed(0)}%)`,
208
+ })),
209
+ topLosers: topLosers.map((q) => ({
210
+ query: q.query,
211
+ change: `${q.clickChange} clicks (${q.clickChangePct.toFixed(0)}%)`,
212
+ })),
213
+ }, null, 2);
214
+ }
215
+ catch (error) {
216
+ return JSON.stringify({
217
+ success: false,
218
+ error: error instanceof Error ? error.message : 'Unknown error',
219
+ });
220
+ }
221
+ }
222
+ /**
223
+ * Execute find opportunities tool
224
+ */
225
+ export async function executeFindOpportunities(client, input) {
226
+ try {
227
+ // If API supports opportunities endpoint
228
+ if (input.type !== 'all') {
229
+ const opportunities = await client.getOpportunities(input.siteId, input.type);
230
+ return JSON.stringify({ success: true, opportunities }, null, 2);
231
+ }
232
+ // Otherwise, analyze data to find opportunities
233
+ const endDate = new Date().toISOString().split('T')[0];
234
+ const startDate = new Date(Date.now() - 28 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
235
+ // Get keyword and page data
236
+ const [keywords, pages] = await Promise.all([
237
+ client.queryGSC({
238
+ siteId: input.siteId,
239
+ startDate,
240
+ endDate,
241
+ dimensions: ['query'],
242
+ rowLimit: 500,
243
+ }),
244
+ client.queryGSC({
245
+ siteId: input.siteId,
246
+ startDate,
247
+ endDate,
248
+ dimensions: ['page'],
249
+ rowLimit: 200,
250
+ }),
251
+ ]);
252
+ const opportunities = [];
253
+ // Keyword opportunities: High impressions, low CTR, positions 5-20
254
+ const lowHangingKeywords = keywords.rows.filter((row) => row.impressions >= 100 &&
255
+ row.ctr < 0.03 && // Less than 3% CTR
256
+ row.position >= 5 &&
257
+ row.position <= 20);
258
+ if (lowHangingKeywords.length > 0) {
259
+ const topLowHanging = lowHangingKeywords
260
+ .sort((a, b) => b.impressions - a.impressions)
261
+ .slice(0, 5);
262
+ opportunities.push({
263
+ type: 'keyword',
264
+ priority: 'high',
265
+ title: 'Low-hanging fruit keywords',
266
+ description: `Found ${lowHangingKeywords.length} keywords ranking on page 1-2 with low CTR. Improving titles/descriptions could boost clicks.`,
267
+ impact: `Potential ${Math.round(lowHangingKeywords.reduce((sum, k) => sum + k.impressions * 0.03, 0))} additional monthly clicks`,
268
+ effort: 'low',
269
+ data: {
270
+ keywords: topLowHanging.map((k) => ({
271
+ keyword: k.keys[0],
272
+ position: k.position.toFixed(1),
273
+ impressions: k.impressions,
274
+ currentCtr: `${(k.ctr * 100).toFixed(2)}%`,
275
+ })),
276
+ },
277
+ });
278
+ }
279
+ // Keyword opportunities: Position 11-20 (page 2)
280
+ const almostPage1 = keywords.rows.filter((row) => row.position >= 11 && row.position <= 20 && row.impressions >= 50);
281
+ if (almostPage1.length > 0) {
282
+ const topAlmost = almostPage1.sort((a, b) => b.impressions - a.impressions).slice(0, 5);
283
+ opportunities.push({
284
+ type: 'keyword',
285
+ priority: 'medium',
286
+ title: 'Keywords almost on page 1',
287
+ description: `Found ${almostPage1.length} keywords on page 2 that could reach page 1 with optimization.`,
288
+ impact: 'Page 1 rankings typically see 10x more clicks',
289
+ effort: 'medium',
290
+ data: {
291
+ keywords: topAlmost.map((k) => ({
292
+ keyword: k.keys[0],
293
+ position: k.position.toFixed(1),
294
+ impressions: k.impressions,
295
+ })),
296
+ },
297
+ });
298
+ }
299
+ // Page opportunities: High impressions but low clicks
300
+ const underperformingPages = pages.rows.filter((row) => row.impressions >= 500 && row.ctr < 0.02);
301
+ if (underperformingPages.length > 0) {
302
+ const topUnderperforming = underperformingPages
303
+ .sort((a, b) => b.impressions - a.impressions)
304
+ .slice(0, 5);
305
+ opportunities.push({
306
+ type: 'page',
307
+ priority: 'high',
308
+ title: 'Underperforming pages with high visibility',
309
+ description: `Found ${underperformingPages.length} pages with good impressions but poor CTR. Review titles and meta descriptions.`,
310
+ impact: 'Doubling CTR would significantly increase organic traffic',
311
+ effort: 'low',
312
+ data: {
313
+ pages: topUnderperforming.map((p) => ({
314
+ page: p.keys[0],
315
+ impressions: p.impressions,
316
+ clicks: p.clicks,
317
+ ctr: `${(p.ctr * 100).toFixed(2)}%`,
318
+ })),
319
+ },
320
+ });
321
+ }
322
+ // Content opportunities: Pages with declining impressions potential
323
+ const thinContent = pages.rows.filter((row) => row.impressions >= 100 && row.impressions < 1000 && row.position > 10);
324
+ if (thinContent.length > 0) {
325
+ opportunities.push({
326
+ type: 'content',
327
+ priority: 'medium',
328
+ title: 'Content expansion opportunities',
329
+ description: `Found ${thinContent.length} pages with moderate visibility that could benefit from content expansion.`,
330
+ impact: 'Longer, comprehensive content often ranks better',
331
+ effort: 'high',
332
+ data: {
333
+ pagesCount: thinContent.length,
334
+ samplePages: thinContent.slice(0, 3).map((p) => p.keys[0]),
335
+ },
336
+ });
337
+ }
338
+ // Sort by priority
339
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
340
+ opportunities.sort((a, b) => (priorityOrder[a.priority] || 2) -
341
+ (priorityOrder[b.priority] || 2));
342
+ return JSON.stringify({
343
+ success: true,
344
+ totalOpportunities: opportunities.length,
345
+ opportunities,
346
+ }, null, 2);
347
+ }
348
+ catch (error) {
349
+ return JSON.stringify({
350
+ success: false,
351
+ error: error instanceof Error ? error.message : 'Unknown error',
352
+ });
353
+ }
354
+ }
355
+ /**
356
+ * Execute week-over-week comparison
357
+ */
358
+ export async function executeWeekOverWeek(client, input) {
359
+ // Calculate date ranges
360
+ const now = new Date();
361
+ const thisWeekEnd = new Date(now);
362
+ thisWeekEnd.setDate(now.getDate() - now.getDay()); // Last Sunday
363
+ const thisWeekStart = new Date(thisWeekEnd);
364
+ thisWeekStart.setDate(thisWeekEnd.getDate() - 6); // Monday before
365
+ const prevWeekEnd = new Date(thisWeekStart);
366
+ prevWeekEnd.setDate(thisWeekStart.getDate() - 1 - (input.weeksBack - 1) * 7);
367
+ const prevWeekStart = new Date(prevWeekEnd);
368
+ prevWeekStart.setDate(prevWeekEnd.getDate() - 6);
369
+ return executeComparePeriod(client, {
370
+ siteId: input.siteId,
371
+ period1Start: prevWeekStart.toISOString().split('T')[0],
372
+ period1End: prevWeekEnd.toISOString().split('T')[0],
373
+ period2Start: thisWeekStart.toISOString().split('T')[0],
374
+ period2End: thisWeekEnd.toISOString().split('T')[0],
375
+ includeGA4: true,
376
+ });
377
+ }
378
+ /**
379
+ * Comparison tool definitions for MCP
380
+ */
381
+ export const comparisonTools = [
382
+ {
383
+ name: 'heyseo_compare_periods',
384
+ description: `Compare SEO metrics between two time periods.
385
+
386
+ Returns:
387
+ - Side-by-side comparison of clicks, impressions, CTR, and position
388
+ - Percentage changes with trend indicators
389
+ - Overall health assessment
390
+ - Top gaining and losing keywords
391
+
392
+ Useful for tracking progress, identifying trends, and measuring campaign impact.`,
393
+ inputSchema: {
394
+ type: 'object',
395
+ properties: {
396
+ siteId: {
397
+ type: 'string',
398
+ description: 'The ID of the site to compare',
399
+ },
400
+ period1Start: {
401
+ type: 'string',
402
+ pattern: '^\\d{4}-\\d{2}-\\d{2}$',
403
+ description: 'First period start date (baseline)',
404
+ },
405
+ period1End: {
406
+ type: 'string',
407
+ pattern: '^\\d{4}-\\d{2}-\\d{2}$',
408
+ description: 'First period end date',
409
+ },
410
+ period2Start: {
411
+ type: 'string',
412
+ pattern: '^\\d{4}-\\d{2}-\\d{2}$',
413
+ description: 'Second period start date (comparison)',
414
+ },
415
+ period2End: {
416
+ type: 'string',
417
+ pattern: '^\\d{4}-\\d{2}-\\d{2}$',
418
+ description: 'Second period end date',
419
+ },
420
+ includeGA4: {
421
+ type: 'boolean',
422
+ default: true,
423
+ description: 'Include GA4 metrics in comparison',
424
+ },
425
+ },
426
+ required: ['siteId', 'period1Start', 'period1End', 'period2Start', 'period2End'],
427
+ },
428
+ },
429
+ {
430
+ name: 'heyseo_week_over_week',
431
+ description: `Quick week-over-week performance comparison.
432
+
433
+ Compares the most recent complete week against a previous week.
434
+ Returns the same detailed comparison as heyseo_compare_periods.`,
435
+ inputSchema: {
436
+ type: 'object',
437
+ properties: {
438
+ siteId: {
439
+ type: 'string',
440
+ description: 'The ID of the site to compare',
441
+ },
442
+ weeksBack: {
443
+ type: 'number',
444
+ minimum: 1,
445
+ maximum: 12,
446
+ default: 1,
447
+ description: 'Number of weeks back to compare (1 = last week)',
448
+ },
449
+ },
450
+ required: ['siteId'],
451
+ },
452
+ },
453
+ {
454
+ name: 'heyseo_find_opportunities',
455
+ description: `Find SEO optimization opportunities for a site.
456
+
457
+ Analyzes your data to identify actionable opportunities:
458
+ - Keyword opportunities: Low-hanging fruit, almost page 1
459
+ - Page opportunities: High impressions with low CTR
460
+ - Content opportunities: Pages that could benefit from expansion
461
+ - Technical opportunities: Performance issues
462
+
463
+ Each opportunity includes priority, estimated impact, and required effort.`,
464
+ inputSchema: {
465
+ type: 'object',
466
+ properties: {
467
+ siteId: {
468
+ type: 'string',
469
+ description: 'The ID of the site to analyze',
470
+ },
471
+ type: {
472
+ type: 'string',
473
+ enum: ['keyword', 'page', 'technical', 'content', 'all'],
474
+ default: 'all',
475
+ description: 'Type of opportunities to find',
476
+ },
477
+ },
478
+ required: ['siteId'],
479
+ },
480
+ },
481
+ ];
482
+ //# sourceMappingURL=comparison.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparison.js","sourceRoot":"","sources":["../../src/tools/comparison.ts"],"names":[],"mappings":"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"}