recker 1.0.29-next.3524ab6 → 1.0.29-next.7cc1d8b
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/dist/cli/tui/scroll-buffer.js +4 -4
- package/dist/cli/tui/shell.js +13 -36
- package/dist/mcp/server.js +0 -5
- package/package.json +1 -1
- package/dist/mcp/tools/seo.d.ts +0 -3
- package/dist/mcp/tools/seo.js +0 -427
|
@@ -119,17 +119,17 @@ export class ScrollBuffer extends EventEmitter {
|
|
|
119
119
|
}
|
|
120
120
|
export function parseScrollKey(data) {
|
|
121
121
|
const str = data.toString();
|
|
122
|
-
if (str === '\x1b[5~' || str === '\x1bOy'
|
|
122
|
+
if (str === '\x1b[5~' || str === '\x1bOy')
|
|
123
123
|
return 'pageUp';
|
|
124
|
-
if (str === '\x1b[6~' || str === '\x1bOs'
|
|
124
|
+
if (str === '\x1b[6~' || str === '\x1bOs')
|
|
125
125
|
return 'pageDown';
|
|
126
126
|
if (str === '\x1b[1;2A')
|
|
127
127
|
return 'scrollUp';
|
|
128
128
|
if (str === '\x1b[1;2B')
|
|
129
129
|
return 'scrollDown';
|
|
130
|
-
if (str === '\x1b[H' || str === '\x1b[1~' || str === '\x1bOH'
|
|
130
|
+
if (str === '\x1b[H' || str === '\x1b[1~' || str === '\x1bOH')
|
|
131
131
|
return 'home';
|
|
132
|
-
if (str === '\x1b[F' || str === '\x1b[4~' || str === '\x1bOF'
|
|
132
|
+
if (str === '\x1b[F' || str === '\x1b[4~' || str === '\x1bOF')
|
|
133
133
|
return 'end';
|
|
134
134
|
if (str === 'q' || str === 'Q')
|
|
135
135
|
return 'quit';
|
package/dist/cli/tui/shell.js
CHANGED
|
@@ -173,36 +173,20 @@ export class RekShell {
|
|
|
173
173
|
}
|
|
174
174
|
return true;
|
|
175
175
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (scrollKey) {
|
|
179
|
-
if (
|
|
180
|
-
if (self.inScrollMode) {
|
|
181
|
-
self.exitScrollMode();
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
return originalEmit(event, ...args);
|
|
185
|
-
}
|
|
186
|
-
self.handleScrollKey(scrollKey);
|
|
187
|
-
return true;
|
|
188
|
-
}
|
|
189
|
-
if (self.inScrollMode) {
|
|
190
|
-
if (str === '\x1b[A') {
|
|
191
|
-
self.handleScrollKey('scrollUp');
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
if (str === '\x1b[B') {
|
|
195
|
-
self.handleScrollKey('scrollDown');
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
if (str === '\x1b' || str === '\x1b\x1b') {
|
|
176
|
+
const scrollKey = parseScrollKey(data);
|
|
177
|
+
if (scrollKey) {
|
|
178
|
+
if (scrollKey === 'quit') {
|
|
179
|
+
if (self.inScrollMode) {
|
|
199
180
|
self.exitScrollMode();
|
|
200
181
|
return true;
|
|
201
182
|
}
|
|
202
|
-
return
|
|
183
|
+
return originalEmit(event, ...args);
|
|
203
184
|
}
|
|
185
|
+
self.handleScrollKey(scrollKey);
|
|
186
|
+
return true;
|
|
204
187
|
}
|
|
205
|
-
|
|
188
|
+
if (self.inScrollMode) {
|
|
189
|
+
return true;
|
|
206
190
|
}
|
|
207
191
|
}
|
|
208
192
|
return originalEmit(event, ...args);
|
|
@@ -210,9 +194,6 @@ export class RekShell {
|
|
|
210
194
|
}
|
|
211
195
|
}
|
|
212
196
|
handleScrollKey(key) {
|
|
213
|
-
if (!this.originalStdoutWrite) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
197
|
let needsRedraw = false;
|
|
217
198
|
switch (key) {
|
|
218
199
|
case 'pageUp':
|
|
@@ -269,15 +250,11 @@ export class RekShell {
|
|
|
269
250
|
enterScrollMode() {
|
|
270
251
|
if (this.inScrollMode)
|
|
271
252
|
return;
|
|
272
|
-
if (!this.originalStdoutWrite)
|
|
273
|
-
return;
|
|
274
253
|
this.inScrollMode = true;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
catch {
|
|
254
|
+
this.rl.pause();
|
|
255
|
+
if (this.originalStdoutWrite) {
|
|
256
|
+
this.originalStdoutWrite('\x1b[?25l');
|
|
279
257
|
}
|
|
280
|
-
this.originalStdoutWrite('\x1b[?25l');
|
|
281
258
|
this.renderScrollView();
|
|
282
259
|
}
|
|
283
260
|
exitScrollMode() {
|
|
@@ -311,7 +288,7 @@ export class RekShell {
|
|
|
311
288
|
const scrollInfo = this.scrollBuffer.isScrolledUp
|
|
312
289
|
? colors.yellow(`↑ ${this.scrollBuffer.position} lines | ${info.percent}% | `)
|
|
313
290
|
: '';
|
|
314
|
-
const helpText = colors.gray('
|
|
291
|
+
const helpText = colors.gray('Page Up/Down • Home/End • Q to exit');
|
|
315
292
|
const statusBar = `\x1b[${rows};1H\x1b[7m ${scrollInfo}${helpText} \x1b[0m`;
|
|
316
293
|
this.originalStdoutWrite(statusBar);
|
|
317
294
|
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -8,7 +8,6 @@ import { createHybridSearch } from './search/index.js';
|
|
|
8
8
|
import { UnsupportedError } from '../core/errors.js';
|
|
9
9
|
import { getIpInfo, isValidIP, isGeoIPAvailable, isBogon, isIPv6 } from './ip-intel.js';
|
|
10
10
|
import { networkTools, networkToolHandlers } from './tools/network.js';
|
|
11
|
-
import { seoTools, seoToolHandlers } from './tools/seo.js';
|
|
12
11
|
import { ToolRegistry } from './tools/registry.js';
|
|
13
12
|
import { loadToolModules } from './tools/loader.js';
|
|
14
13
|
export class MCPServer {
|
|
@@ -46,10 +45,6 @@ export class MCPServer {
|
|
|
46
45
|
tools: networkTools,
|
|
47
46
|
handlers: networkToolHandlers
|
|
48
47
|
});
|
|
49
|
-
this.toolRegistry.registerModule({
|
|
50
|
-
tools: seoTools,
|
|
51
|
-
handlers: seoToolHandlers
|
|
52
|
-
});
|
|
53
48
|
}
|
|
54
49
|
indexReady = null;
|
|
55
50
|
async ensureIndexReady() {
|
package/package.json
CHANGED
package/dist/mcp/tools/seo.d.ts
DELETED
package/dist/mcp/tools/seo.js
DELETED
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
import { createClient } from '../../core/client.js';
|
|
2
|
-
import { analyzeSeo } from '../../seo/analyzer.js';
|
|
3
|
-
import { seoSpider } from '../../seo/seo-spider.js';
|
|
4
|
-
function formatCheck(check) {
|
|
5
|
-
const icon = check.status === 'pass' ? '✓' : check.status === 'fail' ? '✗' : '⚠';
|
|
6
|
-
let line = `${icon} [${check.status.toUpperCase()}] ${check.name}: ${check.message}`;
|
|
7
|
-
if (check.recommendation) {
|
|
8
|
-
line += `\n → ${check.recommendation}`;
|
|
9
|
-
}
|
|
10
|
-
return line;
|
|
11
|
-
}
|
|
12
|
-
function createIssueSummary(checks) {
|
|
13
|
-
const critical = checks.filter(c => c.status === 'fail');
|
|
14
|
-
const warnings = checks.filter(c => c.status === 'warn');
|
|
15
|
-
const passed = checks.filter(c => c.status === 'pass').length;
|
|
16
|
-
return { critical, warnings, passed, total: checks.length };
|
|
17
|
-
}
|
|
18
|
-
function extractCategory(name) {
|
|
19
|
-
const lowerName = name.toLowerCase();
|
|
20
|
-
const categories = [
|
|
21
|
-
'meta', 'content', 'links', 'images', 'technical', 'security',
|
|
22
|
-
'performance', 'mobile', 'accessibility', 'schema', 'structural',
|
|
23
|
-
'i18n', 'pwa', 'social', 'ecommerce', 'local', 'cwv',
|
|
24
|
-
'readability', 'crawl', 'internal-linking', 'best-practices'
|
|
25
|
-
];
|
|
26
|
-
for (const cat of categories) {
|
|
27
|
-
if (lowerName.startsWith(cat) || lowerName.includes(cat)) {
|
|
28
|
-
return cat;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return 'general';
|
|
32
|
-
}
|
|
33
|
-
function generateQuickWins(report) {
|
|
34
|
-
const quickWins = [];
|
|
35
|
-
for (const check of report.checks) {
|
|
36
|
-
if (check.status === 'pass')
|
|
37
|
-
continue;
|
|
38
|
-
const category = extractCategory(check.name);
|
|
39
|
-
let priority = 'medium';
|
|
40
|
-
if (check.status === 'fail') {
|
|
41
|
-
priority = 'high';
|
|
42
|
-
}
|
|
43
|
-
else if (['meta', 'title', 'content'].includes(category)) {
|
|
44
|
-
priority = 'medium';
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
priority = 'low';
|
|
48
|
-
}
|
|
49
|
-
if (check.name.toLowerCase().includes('title') && check.status === 'fail') {
|
|
50
|
-
priority = 'high';
|
|
51
|
-
}
|
|
52
|
-
if (check.name.toLowerCase().includes('meta description') && check.status === 'fail') {
|
|
53
|
-
priority = 'high';
|
|
54
|
-
}
|
|
55
|
-
if (check.name.toLowerCase().includes('h1') && check.status === 'fail') {
|
|
56
|
-
priority = 'high';
|
|
57
|
-
}
|
|
58
|
-
if (check.name.toLowerCase().includes('canonical') && check.status === 'fail') {
|
|
59
|
-
priority = 'high';
|
|
60
|
-
}
|
|
61
|
-
if (check.name.toLowerCase().includes('https') && check.status === 'fail') {
|
|
62
|
-
priority = 'high';
|
|
63
|
-
}
|
|
64
|
-
quickWins.push({
|
|
65
|
-
priority,
|
|
66
|
-
category,
|
|
67
|
-
issue: check.name,
|
|
68
|
-
action: check.recommendation || check.message,
|
|
69
|
-
impact: check.evidence?.impact || 'Improves SEO ranking and user experience',
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
73
|
-
quickWins.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
74
|
-
return quickWins;
|
|
75
|
-
}
|
|
76
|
-
async function seoAnalyze(args) {
|
|
77
|
-
const url = String(args.url || '');
|
|
78
|
-
const categories = args.categories;
|
|
79
|
-
const verbose = Boolean(args.verbose);
|
|
80
|
-
if (!url) {
|
|
81
|
-
return {
|
|
82
|
-
content: [{ type: 'text', text: 'Error: url is required' }],
|
|
83
|
-
isError: true,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
const client = createClient({ timeout: 30000 });
|
|
88
|
-
const response = await client.get(url);
|
|
89
|
-
const html = await response.text();
|
|
90
|
-
const responseHeaders = {};
|
|
91
|
-
response.headers.forEach((val, key) => {
|
|
92
|
-
responseHeaders[key] = val;
|
|
93
|
-
});
|
|
94
|
-
const report = await analyzeSeo(html, {
|
|
95
|
-
baseUrl: url,
|
|
96
|
-
responseHeaders,
|
|
97
|
-
rules: categories ? { categories: categories } : undefined,
|
|
98
|
-
});
|
|
99
|
-
const summary = createIssueSummary(report.checks);
|
|
100
|
-
const output = {
|
|
101
|
-
url,
|
|
102
|
-
score: report.score,
|
|
103
|
-
grade: report.grade,
|
|
104
|
-
summary: {
|
|
105
|
-
critical: summary.critical.length,
|
|
106
|
-
warnings: summary.warnings.length,
|
|
107
|
-
passed: summary.passed,
|
|
108
|
-
total: summary.total,
|
|
109
|
-
},
|
|
110
|
-
timing: report.timing,
|
|
111
|
-
};
|
|
112
|
-
if (report.openGraph) {
|
|
113
|
-
output.openGraph = report.openGraph;
|
|
114
|
-
}
|
|
115
|
-
if (summary.critical.length > 0) {
|
|
116
|
-
output.criticalIssues = summary.critical.map(c => ({
|
|
117
|
-
name: c.name,
|
|
118
|
-
message: c.message,
|
|
119
|
-
recommendation: c.recommendation,
|
|
120
|
-
evidence: c.evidence,
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
123
|
-
if (summary.warnings.length > 0) {
|
|
124
|
-
output.warnings = summary.warnings.slice(0, verbose ? undefined : 10).map(c => ({
|
|
125
|
-
name: c.name,
|
|
126
|
-
message: c.message,
|
|
127
|
-
recommendation: c.recommendation,
|
|
128
|
-
}));
|
|
129
|
-
if (!verbose && summary.warnings.length > 10) {
|
|
130
|
-
output.warningsNote = `Showing 10 of ${summary.warnings.length} warnings. Use verbose=true to see all.`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (verbose) {
|
|
134
|
-
output.detailedAnalysis = {
|
|
135
|
-
title: report.title,
|
|
136
|
-
metaDescription: report.metaDescription,
|
|
137
|
-
headings: report.headings,
|
|
138
|
-
content: report.content,
|
|
139
|
-
links: report.links,
|
|
140
|
-
images: report.images,
|
|
141
|
-
technical: report.technical,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
content: [{
|
|
146
|
-
type: 'text',
|
|
147
|
-
text: JSON.stringify(output, null, 2),
|
|
148
|
-
}],
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
return {
|
|
153
|
-
content: [{
|
|
154
|
-
type: 'text',
|
|
155
|
-
text: `SEO analysis failed: ${error.message}`,
|
|
156
|
-
}],
|
|
157
|
-
isError: true,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
async function seoSpiderCrawl(args) {
|
|
162
|
-
const url = String(args.url || '');
|
|
163
|
-
const maxPages = Number(args.maxPages) || 20;
|
|
164
|
-
const maxDepth = Number(args.maxDepth) || 3;
|
|
165
|
-
const concurrency = Number(args.concurrency) || 3;
|
|
166
|
-
if (!url) {
|
|
167
|
-
return {
|
|
168
|
-
content: [{ type: 'text', text: 'Error: url is required' }],
|
|
169
|
-
isError: true,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
try {
|
|
173
|
-
const result = await seoSpider(url, {
|
|
174
|
-
seo: true,
|
|
175
|
-
maxPages,
|
|
176
|
-
maxDepth,
|
|
177
|
-
concurrency,
|
|
178
|
-
delay: 200,
|
|
179
|
-
});
|
|
180
|
-
const output = {
|
|
181
|
-
url,
|
|
182
|
-
crawlDuration: result.duration,
|
|
183
|
-
summary: result.summary,
|
|
184
|
-
};
|
|
185
|
-
if (result.siteWideIssues.length > 0) {
|
|
186
|
-
output.siteWideIssues = result.siteWideIssues.map(issue => ({
|
|
187
|
-
type: issue.type,
|
|
188
|
-
severity: issue.severity,
|
|
189
|
-
message: issue.message,
|
|
190
|
-
affectedUrls: issue.affectedUrls.slice(0, 5),
|
|
191
|
-
affectedCount: issue.affectedUrls.length,
|
|
192
|
-
value: issue.value,
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
const pageResults = result.pages
|
|
196
|
-
.filter(p => p.seoReport)
|
|
197
|
-
.map(p => ({
|
|
198
|
-
url: p.url,
|
|
199
|
-
status: p.status,
|
|
200
|
-
score: p.seoReport?.score,
|
|
201
|
-
grade: p.seoReport?.grade,
|
|
202
|
-
criticalIssues: p.seoReport?.checks.filter(c => c.status === 'fail').length || 0,
|
|
203
|
-
warnings: p.seoReport?.checks.filter(c => c.status === 'warn').length || 0,
|
|
204
|
-
}))
|
|
205
|
-
.sort((a, b) => (a.score || 0) - (b.score || 0));
|
|
206
|
-
output.pages = pageResults;
|
|
207
|
-
const errorPages = result.pages.filter(p => p.error);
|
|
208
|
-
if (errorPages.length > 0) {
|
|
209
|
-
output.crawlErrors = errorPages.map(p => ({
|
|
210
|
-
url: p.url,
|
|
211
|
-
error: p.error,
|
|
212
|
-
}));
|
|
213
|
-
}
|
|
214
|
-
const recommendations = [];
|
|
215
|
-
if (result.summary.duplicateTitles > 0) {
|
|
216
|
-
recommendations.push(`Fix ${result.summary.duplicateTitles} pages with duplicate titles - each page should have a unique title`);
|
|
217
|
-
}
|
|
218
|
-
if (result.summary.duplicateDescriptions > 0) {
|
|
219
|
-
recommendations.push(`Fix ${result.summary.duplicateDescriptions} pages with duplicate meta descriptions`);
|
|
220
|
-
}
|
|
221
|
-
if (result.summary.duplicateH1s > 0) {
|
|
222
|
-
recommendations.push(`Fix ${result.summary.duplicateH1s} pages with duplicate H1 headings`);
|
|
223
|
-
}
|
|
224
|
-
if (result.summary.orphanPages > 0) {
|
|
225
|
-
recommendations.push(`Add internal links to ${result.summary.orphanPages} orphan pages that have no incoming links`);
|
|
226
|
-
}
|
|
227
|
-
if (result.summary.avgScore < 70) {
|
|
228
|
-
recommendations.push(`Overall site SEO score is ${result.summary.avgScore}/100 - focus on pages with lowest scores first`);
|
|
229
|
-
}
|
|
230
|
-
if (recommendations.length > 0) {
|
|
231
|
-
output.recommendations = recommendations;
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
content: [{
|
|
235
|
-
type: 'text',
|
|
236
|
-
text: JSON.stringify(output, null, 2),
|
|
237
|
-
}],
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
return {
|
|
242
|
-
content: [{
|
|
243
|
-
type: 'text',
|
|
244
|
-
text: `SEO spider failed: ${error.message}`,
|
|
245
|
-
}],
|
|
246
|
-
isError: true,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
async function seoQuickWins(args) {
|
|
251
|
-
const url = String(args.url || '');
|
|
252
|
-
const limit = Number(args.limit) || 10;
|
|
253
|
-
if (!url) {
|
|
254
|
-
return {
|
|
255
|
-
content: [{ type: 'text', text: 'Error: url is required' }],
|
|
256
|
-
isError: true,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
const client = createClient({ timeout: 30000 });
|
|
261
|
-
const response = await client.get(url);
|
|
262
|
-
const html = await response.text();
|
|
263
|
-
const responseHeaders = {};
|
|
264
|
-
response.headers.forEach((val, key) => {
|
|
265
|
-
responseHeaders[key] = val;
|
|
266
|
-
});
|
|
267
|
-
const report = await analyzeSeo(html, {
|
|
268
|
-
baseUrl: url,
|
|
269
|
-
responseHeaders,
|
|
270
|
-
});
|
|
271
|
-
const quickWins = generateQuickWins(report).slice(0, limit);
|
|
272
|
-
const high = quickWins.filter(w => w.priority === 'high');
|
|
273
|
-
const medium = quickWins.filter(w => w.priority === 'medium');
|
|
274
|
-
const low = quickWins.filter(w => w.priority === 'low');
|
|
275
|
-
const output = {
|
|
276
|
-
url,
|
|
277
|
-
score: report.score,
|
|
278
|
-
grade: report.grade,
|
|
279
|
-
quickWins: {
|
|
280
|
-
high: high.map(w => ({
|
|
281
|
-
issue: w.issue,
|
|
282
|
-
action: w.action,
|
|
283
|
-
impact: w.impact,
|
|
284
|
-
category: w.category,
|
|
285
|
-
})),
|
|
286
|
-
medium: medium.map(w => ({
|
|
287
|
-
issue: w.issue,
|
|
288
|
-
action: w.action,
|
|
289
|
-
category: w.category,
|
|
290
|
-
})),
|
|
291
|
-
low: low.map(w => ({
|
|
292
|
-
issue: w.issue,
|
|
293
|
-
action: w.action,
|
|
294
|
-
category: w.category,
|
|
295
|
-
})),
|
|
296
|
-
},
|
|
297
|
-
summary: {
|
|
298
|
-
totalIssues: quickWins.length,
|
|
299
|
-
highPriority: high.length,
|
|
300
|
-
mediumPriority: medium.length,
|
|
301
|
-
lowPriority: low.length,
|
|
302
|
-
},
|
|
303
|
-
advice: high.length > 0
|
|
304
|
-
? `Start with the ${high.length} high-priority issues first. These have the biggest impact on SEO.`
|
|
305
|
-
: medium.length > 0
|
|
306
|
-
? `Good job! No critical issues. Focus on the ${medium.length} medium-priority improvements.`
|
|
307
|
-
: 'Excellent! Your page is well-optimized. Consider the minor improvements listed.',
|
|
308
|
-
};
|
|
309
|
-
return {
|
|
310
|
-
content: [{
|
|
311
|
-
type: 'text',
|
|
312
|
-
text: JSON.stringify(output, null, 2),
|
|
313
|
-
}],
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
return {
|
|
318
|
-
content: [{
|
|
319
|
-
type: 'text',
|
|
320
|
-
text: `Quick wins analysis failed: ${error.message}`,
|
|
321
|
-
}],
|
|
322
|
-
isError: true,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
export const seoTools = [
|
|
327
|
-
{
|
|
328
|
-
name: 'rek_seo_analyze',
|
|
329
|
-
description: `Analyze a single web page for SEO issues using 250+ rules across 21 categories.
|
|
330
|
-
|
|
331
|
-
Returns:
|
|
332
|
-
- SEO score (0-100) and grade (A-F)
|
|
333
|
-
- Critical issues that must be fixed
|
|
334
|
-
- Warnings and recommendations
|
|
335
|
-
- OpenGraph/social meta analysis
|
|
336
|
-
- Request timing breakdown
|
|
337
|
-
|
|
338
|
-
Perfect for analyzing your localhost dev server or any public URL. Categories include: meta, content, links, images, technical, security, performance, mobile, accessibility, schema, structural, i18n, PWA, social, e-commerce, local SEO, Core Web Vitals, readability, crawlability, internal linking, and best practices.`,
|
|
339
|
-
inputSchema: {
|
|
340
|
-
type: 'object',
|
|
341
|
-
properties: {
|
|
342
|
-
url: {
|
|
343
|
-
type: 'string',
|
|
344
|
-
description: 'URL to analyze (e.g., http://localhost:3000 or https://example.com)',
|
|
345
|
-
},
|
|
346
|
-
categories: {
|
|
347
|
-
type: 'array',
|
|
348
|
-
items: { type: 'string' },
|
|
349
|
-
description: 'Filter by specific categories (e.g., ["meta", "security", "performance"]). Leave empty for all.',
|
|
350
|
-
},
|
|
351
|
-
verbose: {
|
|
352
|
-
type: 'boolean',
|
|
353
|
-
description: 'Include detailed analysis (headings, links, images breakdown)',
|
|
354
|
-
default: false,
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
required: ['url'],
|
|
358
|
-
},
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
name: 'rek_seo_spider',
|
|
362
|
-
description: `Crawl an entire website and analyze SEO across all pages.
|
|
363
|
-
|
|
364
|
-
Detects site-wide issues:
|
|
365
|
-
- Duplicate titles, descriptions, and H1s
|
|
366
|
-
- Orphan pages (no internal links pointing to them)
|
|
367
|
-
- Pages with low SEO scores
|
|
368
|
-
|
|
369
|
-
Returns per-page scores and prioritized recommendations for improving overall site SEO. Great for auditing a full site before launch or finding issues across your dev environment.`,
|
|
370
|
-
inputSchema: {
|
|
371
|
-
type: 'object',
|
|
372
|
-
properties: {
|
|
373
|
-
url: {
|
|
374
|
-
type: 'string',
|
|
375
|
-
description: 'Starting URL to crawl (e.g., http://localhost:3000)',
|
|
376
|
-
},
|
|
377
|
-
maxPages: {
|
|
378
|
-
type: 'number',
|
|
379
|
-
description: 'Maximum pages to crawl',
|
|
380
|
-
default: 20,
|
|
381
|
-
},
|
|
382
|
-
maxDepth: {
|
|
383
|
-
type: 'number',
|
|
384
|
-
description: 'Maximum link depth to follow',
|
|
385
|
-
default: 3,
|
|
386
|
-
},
|
|
387
|
-
concurrency: {
|
|
388
|
-
type: 'number',
|
|
389
|
-
description: 'Parallel requests (be respectful to servers)',
|
|
390
|
-
default: 3,
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
required: ['url'],
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
name: 'rek_seo_quick_wins',
|
|
398
|
-
description: `Get prioritized, actionable SEO improvements for a page.
|
|
399
|
-
|
|
400
|
-
Returns issues sorted by priority (high/medium/low) with:
|
|
401
|
-
- What to fix
|
|
402
|
-
- How to fix it
|
|
403
|
-
- Expected impact
|
|
404
|
-
|
|
405
|
-
Use this when you want a focused list of what to work on next, rather than a full audit.`,
|
|
406
|
-
inputSchema: {
|
|
407
|
-
type: 'object',
|
|
408
|
-
properties: {
|
|
409
|
-
url: {
|
|
410
|
-
type: 'string',
|
|
411
|
-
description: 'URL to analyze',
|
|
412
|
-
},
|
|
413
|
-
limit: {
|
|
414
|
-
type: 'number',
|
|
415
|
-
description: 'Maximum number of quick wins to return',
|
|
416
|
-
default: 10,
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
required: ['url'],
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
];
|
|
423
|
-
export const seoToolHandlers = {
|
|
424
|
-
rek_seo_analyze: seoAnalyze,
|
|
425
|
-
rek_seo_spider: seoSpiderCrawl,
|
|
426
|
-
rek_seo_quick_wins: seoQuickWins,
|
|
427
|
-
};
|