@rankcli/mcp-server 0.0.1

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,120 @@
1
+ # @rankcli/mcp-server
2
+
3
+ **MCP Server for AI Assistants to perform SEO analysis.**
4
+
5
+ The industry's first SEO tool accessible via Model Context Protocol. Let Claude, GPT, and other AI assistants run comprehensive SEO audits directly.
6
+
7
+ ## What is MCP?
8
+
9
+ [Model Context Protocol](https://modelcontextprotocol.io) (MCP) is Anthropic's standard for connecting AI assistants to external tools. This server exposes RankCLI's full SEO analysis capabilities to AI.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install -g @rankcli/mcp-server
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ ### Claude Desktop
20
+
21
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "rankcli": {
27
+ "command": "npx",
28
+ "args": ["@rankcli/mcp-server"]
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ Restart Claude Desktop. You'll now have SEO tools available.
35
+
36
+ ### Other MCP Clients
37
+
38
+ Run the server directly:
39
+
40
+ ```bash
41
+ npx @rankcli/mcp-server
42
+ ```
43
+
44
+ The server communicates via stdio using JSON-RPC.
45
+
46
+ ## Available Tools
47
+
48
+ ### `seo_analyze`
49
+ **Full comprehensive SEO audit.** Returns scores for GEO, Core Web Vitals, Security, Structured Data, Images, Links, and Mobile.
50
+
51
+ ### `seo_geo_check`
52
+ **GEO (Generative Engine Optimization).** Check AI crawler access, JS rendering, LLM signals, citation readiness.
53
+
54
+ ### `seo_robots_ai`
55
+ **Analyze robots.txt for AI crawlers.** Shows which AI crawlers (GPTBot, ClaudeBot, PerplexityBot) are allowed/blocked.
56
+
57
+ ### `seo_generate_robots`
58
+ **Generate AI-friendly robots.txt.** Creates rules allowing all major AI crawlers.
59
+
60
+ ### `seo_core_web_vitals`
61
+ **Estimate Core Web Vitals.** LCP, CLS, INP, TTFB from HTML analysis.
62
+
63
+ ### `seo_structured_data`
64
+ **Validate JSON-LD schemas.** Check Article, Product, FAQ, HowTo, LocalBusiness, BreadcrumbList.
65
+
66
+ ### `seo_generate_schema`
67
+ **Generate schema templates.** For article, product, faq, local-business, website.
68
+
69
+ ### `seo_security_headers`
70
+ **Analyze security headers.** HTTPS, HSTS, CSP with A+ to F grading.
71
+
72
+ ### `seo_generate_security_headers`
73
+ **Generate recommended security headers.**
74
+
75
+ ### `seo_images`
76
+ **Image optimization analysis.** Alt text, dimensions, formats, lazy loading.
77
+
78
+ ### `seo_internal_links`
79
+ **Internal linking analysis.** Anchor text quality, orphan detection.
80
+
81
+ ### `seo_mobile`
82
+ **Mobile SEO analysis.** Viewport, touch targets, PWA readiness.
83
+
84
+ ### `seo_ai_crawlers`
85
+ **AI crawler reference.** Information about all known AI crawlers.
86
+
87
+ ## Example Usage
88
+
89
+ Once configured, ask Claude:
90
+
91
+ > "Run a comprehensive SEO audit on https://example.com"
92
+
93
+ > "Check if my site is visible to ChatGPT and other AI search engines"
94
+
95
+ > "Generate an AI-friendly robots.txt for my domain"
96
+
97
+ > "What structured data should I add to my blog posts?"
98
+
99
+ Claude will use the appropriate tools to analyze and respond.
100
+
101
+ ## Why This Matters
102
+
103
+ 1. **AI doing SEO**: Your AI assistant can now audit sites, generate fixes, and monitor SEO.
104
+ 2. **Meta-optimization**: Make sure YOUR site is visible to AI crawlers—checked by AI.
105
+ 3. **Developer workflow**: Ask Claude to audit during code review or deployment.
106
+
107
+ ## Requirements
108
+
109
+ - Node.js 18+
110
+ - An MCP-compatible AI assistant (Claude Desktop, etc.)
111
+
112
+ ## Related
113
+
114
+ - [RankCLI CLI](https://www.npmjs.com/package/@rankcli/cli) - Command-line SEO tool
115
+ - [RankCLI Website](https://rankcli.dev) - Full documentation
116
+ - [MCP Protocol](https://modelcontextprotocol.io) - Model Context Protocol
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,725 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+ import { analyzers } from "@rankcli/agent-runtime";
11
+ var TOOLS = [
12
+ {
13
+ name: "seo_analyze",
14
+ description: `Run comprehensive SEO analysis on a webpage. Returns scores and issues for:
15
+ - GEO (AI Search Optimization) - Is the site visible to ChatGPT, Perplexity, Claude?
16
+ - Core Web Vitals (LCP, CLS, INP) - Performance estimates
17
+ - Structured Data - Schema.org validation
18
+ - Security Headers - HTTPS, HSTS, CSP
19
+ - Mobile SEO - Responsive design, touch targets
20
+ - Images - Alt text, formats, dimensions
21
+ - Internal Linking - Anchor text, orphan detection
22
+
23
+ Use this for a complete SEO audit.`,
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ url: {
28
+ type: "string",
29
+ description: "URL of the page to analyze"
30
+ },
31
+ html: {
32
+ type: "string",
33
+ description: "HTML content of the page (optional if URL is provided)"
34
+ },
35
+ robotsTxt: {
36
+ type: "string",
37
+ description: "robots.txt content for AI crawler analysis (optional)"
38
+ }
39
+ },
40
+ required: ["url"]
41
+ }
42
+ },
43
+ {
44
+ name: "seo_geo_check",
45
+ description: `Check if a website is optimized for AI search engines (GEO - Generative Engine Optimization).
46
+
47
+ Analyzes:
48
+ - AI crawler access (GPTBot, ClaudeBot, PerplexityBot, etc.)
49
+ - robots.txt rules for AI crawlers
50
+ - JS rendering requirements (can AI crawlers see content?)
51
+ - Content structure for LLM consumption
52
+ - Citation readiness (trust signals)
53
+ - FAQ/entity extraction capability
54
+
55
+ Critical for visibility in ChatGPT, Perplexity, Claude, and Gemini responses.`,
56
+ inputSchema: {
57
+ type: "object",
58
+ properties: {
59
+ url: {
60
+ type: "string",
61
+ description: "URL of the page to analyze"
62
+ },
63
+ html: {
64
+ type: "string",
65
+ description: "HTML content of the page"
66
+ },
67
+ robotsTxt: {
68
+ type: "string",
69
+ description: "robots.txt content"
70
+ }
71
+ },
72
+ required: ["url"]
73
+ }
74
+ },
75
+ {
76
+ name: "seo_robots_ai",
77
+ description: `Analyze robots.txt for AI crawler permissions. Shows which AI crawlers (GPTBot, ClaudeBot, PerplexityBot, etc.) are allowed or blocked.`,
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ robotsTxt: {
82
+ type: "string",
83
+ description: "Content of robots.txt file"
84
+ }
85
+ },
86
+ required: ["robotsTxt"]
87
+ }
88
+ },
89
+ {
90
+ name: "seo_generate_robots",
91
+ description: `Generate an AI-friendly robots.txt that allows all major AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, etc.)`,
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ siteUrl: {
96
+ type: "string",
97
+ description: "Base URL of the site (e.g., https://example.com)"
98
+ }
99
+ },
100
+ required: ["siteUrl"]
101
+ }
102
+ },
103
+ {
104
+ name: "seo_core_web_vitals",
105
+ description: `Estimate Core Web Vitals (LCP, CLS, INP, TTFB) from HTML analysis. Identifies issues like:
106
+ - Render-blocking resources
107
+ - Images without dimensions
108
+ - Large JavaScript bundles
109
+ - Missing preload hints`,
110
+ inputSchema: {
111
+ type: "object",
112
+ properties: {
113
+ url: {
114
+ type: "string",
115
+ description: "URL of the page"
116
+ },
117
+ html: {
118
+ type: "string",
119
+ description: "HTML content of the page"
120
+ }
121
+ },
122
+ required: ["url", "html"]
123
+ }
124
+ },
125
+ {
126
+ name: "seo_structured_data",
127
+ description: `Validate JSON-LD structured data (Schema.org). Checks for:
128
+ - Required properties per schema type
129
+ - Article, Product, FAQ, HowTo, LocalBusiness schemas
130
+ - Rich result eligibility
131
+ - Common mistakes`,
132
+ inputSchema: {
133
+ type: "object",
134
+ properties: {
135
+ url: {
136
+ type: "string",
137
+ description: "URL of the page"
138
+ },
139
+ html: {
140
+ type: "string",
141
+ description: "HTML content with JSON-LD"
142
+ }
143
+ },
144
+ required: ["url", "html"]
145
+ }
146
+ },
147
+ {
148
+ name: "seo_generate_schema",
149
+ description: `Generate JSON-LD structured data template for a page type (article, product, faq, local-business, website).`,
150
+ inputSchema: {
151
+ type: "object",
152
+ properties: {
153
+ pageType: {
154
+ type: "string",
155
+ enum: ["article", "product", "faq", "local-business", "website"],
156
+ description: "Type of page"
157
+ },
158
+ siteName: {
159
+ type: "string",
160
+ description: "Name of the website"
161
+ },
162
+ siteUrl: {
163
+ type: "string",
164
+ description: "Base URL of the website"
165
+ },
166
+ authorName: {
167
+ type: "string",
168
+ description: "Default author name (for articles)"
169
+ },
170
+ organizationName: {
171
+ type: "string",
172
+ description: "Organization name"
173
+ }
174
+ },
175
+ required: ["pageType", "siteName", "siteUrl"]
176
+ }
177
+ },
178
+ {
179
+ name: "seo_security_headers",
180
+ description: `Analyze security headers (HTTPS, HSTS, CSP, X-Frame-Options, etc.). Returns a security grade A+ through F.`,
181
+ inputSchema: {
182
+ type: "object",
183
+ properties: {
184
+ url: {
185
+ type: "string",
186
+ description: "URL of the page"
187
+ },
188
+ headers: {
189
+ type: "object",
190
+ description: "HTTP response headers",
191
+ additionalProperties: { type: "string" }
192
+ }
193
+ },
194
+ required: ["url", "headers"]
195
+ }
196
+ },
197
+ {
198
+ name: "seo_generate_security_headers",
199
+ description: `Generate recommended security headers configuration for a site.`,
200
+ inputSchema: {
201
+ type: "object",
202
+ properties: {
203
+ siteUrl: {
204
+ type: "string",
205
+ description: "Base URL of the site"
206
+ }
207
+ },
208
+ required: ["siteUrl"]
209
+ }
210
+ },
211
+ {
212
+ name: "seo_images",
213
+ description: `Analyze images for SEO and performance. Checks alt text, dimensions, formats (WebP/AVIF), lazy loading, and responsive images.`,
214
+ inputSchema: {
215
+ type: "object",
216
+ properties: {
217
+ url: {
218
+ type: "string",
219
+ description: "URL of the page"
220
+ },
221
+ html: {
222
+ type: "string",
223
+ description: "HTML content of the page"
224
+ }
225
+ },
226
+ required: ["url", "html"]
227
+ }
228
+ },
229
+ {
230
+ name: "seo_internal_links",
231
+ description: `Analyze internal linking structure. Checks anchor text quality, orphan page risk, link distribution, and suggests linking opportunities.`,
232
+ inputSchema: {
233
+ type: "object",
234
+ properties: {
235
+ url: {
236
+ type: "string",
237
+ description: "URL of the page"
238
+ },
239
+ html: {
240
+ type: "string",
241
+ description: "HTML content of the page"
242
+ }
243
+ },
244
+ required: ["url", "html"]
245
+ }
246
+ },
247
+ {
248
+ name: "seo_mobile",
249
+ description: `Analyze mobile SEO. Checks viewport, touch targets, font sizes, content width, PWA readiness.`,
250
+ inputSchema: {
251
+ type: "object",
252
+ properties: {
253
+ url: {
254
+ type: "string",
255
+ description: "URL of the page"
256
+ },
257
+ html: {
258
+ type: "string",
259
+ description: "HTML content of the page"
260
+ }
261
+ },
262
+ required: ["url", "html"]
263
+ }
264
+ },
265
+ {
266
+ name: "seo_ai_crawlers",
267
+ description: `Get information about all known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, etc.) including their user agents and purposes.`,
268
+ inputSchema: {
269
+ type: "object",
270
+ properties: {}
271
+ }
272
+ }
273
+ ];
274
+ var server = new Server(
275
+ {
276
+ name: "rankcli",
277
+ version: "0.0.1"
278
+ },
279
+ {
280
+ capabilities: {
281
+ tools: {}
282
+ }
283
+ }
284
+ );
285
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
286
+ return { tools: TOOLS };
287
+ });
288
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
289
+ const { name, arguments: args } = request.params;
290
+ try {
291
+ switch (name) {
292
+ case "seo_analyze": {
293
+ const { url, html, robotsTxt } = args;
294
+ if (!html) {
295
+ return {
296
+ content: [
297
+ {
298
+ type: "text",
299
+ text: "HTML content is required for analysis. Please provide the HTML of the page."
300
+ }
301
+ ]
302
+ };
303
+ }
304
+ const result = await analyzers.analyzeComprehensive(html, url, { robotsTxt });
305
+ return {
306
+ content: [
307
+ {
308
+ type: "text",
309
+ text: formatComprehensiveResult(result)
310
+ }
311
+ ]
312
+ };
313
+ }
314
+ case "seo_geo_check": {
315
+ const { url, html, robotsTxt } = args;
316
+ if (!html) {
317
+ return {
318
+ content: [{ type: "text", text: "HTML content required for GEO analysis." }]
319
+ };
320
+ }
321
+ const result = await analyzers.analyzeGEO(html, url, robotsTxt);
322
+ return {
323
+ content: [{ type: "text", text: formatGEOResult(result) }]
324
+ };
325
+ }
326
+ case "seo_robots_ai": {
327
+ const { robotsTxt } = args;
328
+ const result = analyzers.analyzeRobotsTxtForAI(robotsTxt);
329
+ return {
330
+ content: [
331
+ {
332
+ type: "text",
333
+ text: `## AI Crawler Analysis
334
+
335
+ **Allowed:** ${result.allowed.join(", ") || "None"}
336
+
337
+ **Blocked:** ${result.blocked.join(", ") || "None"}
338
+
339
+ **Recommendations:**
340
+ ${result.recommendations.map((r) => `- ${r}`).join("\n") || "- All good!"}`
341
+ }
342
+ ]
343
+ };
344
+ }
345
+ case "seo_generate_robots": {
346
+ const { siteUrl } = args;
347
+ const robotsTxt = analyzers.generateAIFriendlyRobotsTxt(siteUrl);
348
+ return {
349
+ content: [
350
+ {
351
+ type: "text",
352
+ text: `## AI-Friendly robots.txt
353
+
354
+ \`\`\`
355
+ ${robotsTxt}\`\`\``
356
+ }
357
+ ]
358
+ };
359
+ }
360
+ case "seo_core_web_vitals": {
361
+ const { url, html } = args;
362
+ const result = analyzers.analyzeCoreWebVitals(html, url);
363
+ return {
364
+ content: [{ type: "text", text: formatCWVResult(result) }]
365
+ };
366
+ }
367
+ case "seo_structured_data": {
368
+ const { url, html } = args;
369
+ const result = analyzers.analyzeStructuredData(html, url);
370
+ return {
371
+ content: [{ type: "text", text: formatStructuredDataResult(result) }]
372
+ };
373
+ }
374
+ case "seo_generate_schema": {
375
+ const { pageType, siteName, siteUrl, authorName, organizationName } = args;
376
+ const schema = analyzers.generateSchemaTemplate(pageType, { siteName, siteUrl, authorName, organizationName });
377
+ return {
378
+ content: [
379
+ {
380
+ type: "text",
381
+ text: `## ${pageType} Schema Template
382
+
383
+ \`\`\`json
384
+ ${schema}
385
+ \`\`\`
386
+
387
+ Replace {{placeholders}} with actual values.`
388
+ }
389
+ ]
390
+ };
391
+ }
392
+ case "seo_security_headers": {
393
+ const { url, headers } = args;
394
+ const result = analyzers.analyzeSecurityHeaders(headers, url);
395
+ return {
396
+ content: [{ type: "text", text: formatSecurityResult(result) }]
397
+ };
398
+ }
399
+ case "seo_generate_security_headers": {
400
+ const { siteUrl } = args;
401
+ const headers = analyzers.generateSecurityHeaders(siteUrl);
402
+ return {
403
+ content: [
404
+ {
405
+ type: "text",
406
+ text: `## Recommended Security Headers
407
+
408
+ ${Object.entries(headers).map(([k, v]) => `**${k}:**
409
+ \`${v}\``).join("\n\n")}`
410
+ }
411
+ ]
412
+ };
413
+ }
414
+ case "seo_images": {
415
+ const { url, html } = args;
416
+ const result = analyzers.analyzeImages(html, url);
417
+ return {
418
+ content: [{ type: "text", text: formatImagesResult(result) }]
419
+ };
420
+ }
421
+ case "seo_internal_links": {
422
+ const { url, html } = args;
423
+ const result = analyzers.analyzeInternalLinking(html, url);
424
+ return {
425
+ content: [{ type: "text", text: formatInternalLinksResult(result) }]
426
+ };
427
+ }
428
+ case "seo_mobile": {
429
+ const { url, html } = args;
430
+ const result = analyzers.analyzeMobileSEO(html, url);
431
+ return {
432
+ content: [{ type: "text", text: formatMobileResult(result) }]
433
+ };
434
+ }
435
+ case "seo_ai_crawlers": {
436
+ return {
437
+ content: [
438
+ {
439
+ type: "text",
440
+ text: formatAICrawlersInfo()
441
+ }
442
+ ]
443
+ };
444
+ }
445
+ default:
446
+ return {
447
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
448
+ isError: true
449
+ };
450
+ }
451
+ } catch (error) {
452
+ return {
453
+ content: [
454
+ {
455
+ type: "text",
456
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
457
+ }
458
+ ],
459
+ isError: true
460
+ };
461
+ }
462
+ });
463
+ function formatComprehensiveResult(result) {
464
+ const criticalIssues = result.allIssues.filter((i) => i.severity === "critical");
465
+ const warnings = result.allIssues.filter((i) => i.severity === "warning");
466
+ return `# SEO Analysis Report
467
+
468
+ **URL:** ${result.url}
469
+ **Overall Score:** ${result.overallScore}/100
470
+ **Analyzed:** ${result.timestamp}
471
+
472
+ ## Scores
473
+
474
+ | Category | Score |
475
+ |----------|-------|
476
+ | GEO (AI Search) | ${result.grades.geo}/100 |
477
+ | Core Web Vitals | ${result.grades.coreWebVitals}/100 |
478
+ | Security | ${result.grades.security} |
479
+ | Structured Data | ${result.grades.structuredData}/100 |
480
+ | Images | ${result.grades.images}/100 |
481
+ | Internal Links | ${result.grades.internalLinking}/100 |
482
+ | Mobile SEO | ${result.grades.mobile}/100 |
483
+
484
+ ## Critical Issues (${criticalIssues.length})
485
+
486
+ ${criticalIssues.map((i) => `### \u274C ${i.title}
487
+ ${i.description}
488
+
489
+ **Fix:** ${i.howToFix}`).join("\n\n") || "None!"}
490
+
491
+ ## Warnings (${warnings.length})
492
+
493
+ ${warnings.slice(0, 5).map((i) => `- **${i.title}:** ${i.description}`).join("\n") || "None!"}
494
+
495
+ ## Priority Recommendations
496
+
497
+ ${result.prioritizedRecommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}
498
+ `;
499
+ }
500
+ function formatGEOResult(result) {
501
+ return `# GEO Analysis (AI Search Optimization)
502
+
503
+ **Score:** ${result.score}/100
504
+
505
+ ## AI Crawler Access
506
+
507
+ | Status | Crawlers |
508
+ |--------|----------|
509
+ | \u2705 Allowed | ${result.aiCrawlerAccess.allowedCrawlers.join(", ") || "None"} |
510
+ | \u274C Blocked | ${result.aiCrawlerAccess.blockedCrawlers.join(", ") || "None"} |
511
+
512
+ **Server-Side Rendered:** ${result.aiCrawlerAccess.serverSideRendered ? "\u2705 Yes" : "\u274C No"}
513
+ **JS Rendering Required:** ${result.aiCrawlerAccess.jsRenderingRequired ? "\u26A0\uFE0F Yes (AI crawlers may not see content)" : "\u2705 No"}
514
+
515
+ ## LLM Friendliness Scores
516
+
517
+ | Signal | Score |
518
+ |--------|-------|
519
+ | Content Clarity | ${result.llmSignals.contentClarity}/100 |
520
+ | Fact Density | ${result.llmSignals.factDensity}/100 |
521
+ | Structure Quality | ${result.llmSignals.structureQuality}/100 |
522
+ | Citation Quality | ${result.llmSignals.citationQuality}/100 |
523
+
524
+ ## Content Structure
525
+
526
+ - Structured Data: ${result.contentStructure.hasStructuredData ? "\u2705" : "\u274C"}
527
+ - FAQ Schema: ${result.contentStructure.hasFAQSchema ? "\u2705" : "\u274C"}
528
+ - Article Schema: ${result.contentStructure.hasArticleSchema ? "\u2705" : "\u274C"}
529
+ - Heading Hierarchy: ${result.contentStructure.headingHierarchy}
530
+
531
+ ## Citation Readiness
532
+
533
+ Trust Signals: ${result.citationReadiness.trustSignals.join(", ") || "None detected"}
534
+
535
+ ## Recommendations
536
+
537
+ ${result.recommendations.map((r) => `- ${r}`).join("\n")}
538
+ `;
539
+ }
540
+ function formatCWVResult(result) {
541
+ const emoji = (est) => est === "good" ? "\u{1F7E2}" : est === "needs-improvement" ? "\u{1F7E1}" : "\u{1F534}";
542
+ return `# Core Web Vitals Estimate
543
+
544
+ **Overall Score:** ${result.overallScore}/100
545
+
546
+ | Metric | Estimate | Issues |
547
+ |--------|----------|--------|
548
+ | LCP (Largest Contentful Paint) | ${emoji(result.lcp.estimate)} ${result.lcp.estimate} | ${result.lcp.issues.length} |
549
+ | CLS (Cumulative Layout Shift) | ${emoji(result.cls.estimate)} ${result.cls.estimate} | ${result.cls.issues.length} |
550
+ | INP (Interaction to Next Paint) | ${emoji(result.inp.estimate)} ${result.inp.estimate} | ${result.inp.issues.length} |
551
+ | TTFB (Time to First Byte) | ${emoji(result.ttfb.estimate)} ${result.ttfb.estimate} | ${result.ttfb.issues.length} |
552
+
553
+ ## Issues
554
+
555
+ ${result.issues.map((i) => `- **${i.title}:** ${i.howToFix}`).join("\n") || "No critical issues!"}
556
+ `;
557
+ }
558
+ function formatStructuredDataResult(result) {
559
+ return `# Structured Data Analysis
560
+
561
+ **Score:** ${result.score}/100
562
+ **Schemas Found:** ${result.schemas.length}
563
+
564
+ ## Schema Types
565
+
566
+ | Type | Valid | Errors |
567
+ |------|-------|--------|
568
+ ${result.schemas.map((s) => `| ${s.type} | ${s.isValid ? "\u2705" : "\u274C"} | ${s.errors.join("; ") || "-"} |`).join("\n") || "| None found | - | - |"}
569
+
570
+ ## Rich Result Eligibility
571
+
572
+ - Organization: ${result.hasOrganization ? "\u2705" : "\u274C"}
573
+ - WebSite: ${result.hasWebSite ? "\u2705" : "\u274C"}
574
+ - Breadcrumb: ${result.hasBreadcrumb ? "\u2705" : "\u274C"}
575
+ - Article: ${result.hasArticle ? "\u2705" : "\u274C"}
576
+ - Product: ${result.hasProduct ? "\u2705" : "\u274C"}
577
+ - FAQ: ${result.hasFAQ ? "\u2705" : "\u274C"}
578
+ - HowTo: ${result.hasHowTo ? "\u2705" : "\u274C"}
579
+
580
+ ## Recommendations
581
+
582
+ ${result.recommendations.map((r) => `- ${r}`).join("\n")}
583
+ `;
584
+ }
585
+ function formatSecurityResult(result) {
586
+ return `# Security Headers Analysis
587
+
588
+ **Grade:** ${result.grade}
589
+ **Score:** ${result.score}/100
590
+
591
+ ## Headers
592
+
593
+ | Header | Status |
594
+ |--------|--------|
595
+ | HTTPS | ${result.https.enabled ? "\u2705" : "\u274C"} |
596
+ | HSTS | ${result.https.hasHSTS ? "\u2705" : "\u274C"} |
597
+ | CSP | ${result.contentSecurity.hasCSP ? "\u2705" : "\u274C"} |
598
+ | X-Frame-Options | ${result.frameOptions.hasXFrameOptions ? "\u2705" : "\u274C"} |
599
+ | X-Content-Type-Options | ${result.contentTypeOptions.hasXContentTypeOptions ? "\u2705" : "\u274C"} |
600
+ | Referrer-Policy | ${result.referrerPolicy.hasReferrerPolicy ? "\u2705" : "\u274C"} |
601
+ | Permissions-Policy | ${result.permissionsPolicy.hasPermissionsPolicy ? "\u2705" : "\u274C"} |
602
+
603
+ ## Issues
604
+
605
+ ${result.issues.map((i) => `- **${i.title}:** ${i.howToFix}`).join("\n") || "No issues!"}
606
+ `;
607
+ }
608
+ function formatImagesResult(result) {
609
+ return `# Image Analysis
610
+
611
+ **Score:** ${result.score}/100
612
+ **Total Images:** ${result.totalImages}
613
+
614
+ ## Stats
615
+
616
+ | Metric | Count |
617
+ |--------|-------|
618
+ | With Alt Text | ${result.imagesWithAlt} |
619
+ | With Dimensions | ${result.imagesWithDimensions} |
620
+ | Lazy Loading | ${result.imagesWithLazyLoading} |
621
+ | Modern Formats | ${result.modernFormats} |
622
+ | Legacy Formats | ${result.legacyFormats} |
623
+
624
+ ## Issues
625
+
626
+ ${result.issues.map((i) => `- **${i.title}:** ${i.howToFix}`).join("\n") || "No issues!"}
627
+
628
+ ## Recommendations
629
+
630
+ ${result.recommendations.map((r) => `- ${r}`).join("\n")}
631
+ `;
632
+ }
633
+ function formatInternalLinksResult(result) {
634
+ return `# Internal Linking Analysis
635
+
636
+ **Score:** ${result.score}/100
637
+
638
+ ## Stats
639
+
640
+ | Metric | Count |
641
+ |--------|-------|
642
+ | Total Links | ${result.totalLinks} |
643
+ | Internal Links | ${result.internalLinks} |
644
+ | External Links | ${result.externalLinks} |
645
+ | Navigation Links | ${result.navigationLinks} |
646
+ | Content Links | ${result.contentLinks} |
647
+ | Unique Internal Targets | ${result.uniqueInternalTargets} |
648
+
649
+ ## Anchor Text Quality
650
+
651
+ - Descriptive: ${result.anchorTextAnalysis.descriptive}
652
+ - Generic: ${result.anchorTextAnalysis.generic}
653
+ - Empty: ${result.anchorTextAnalysis.empty}
654
+
655
+ ## Issues
656
+
657
+ ${result.issues.map((i) => `- **${i.title}:** ${i.howToFix}`).join("\n") || "No issues!"}
658
+
659
+ ## Recommendations
660
+
661
+ ${result.recommendations.map((r) => `- ${r}`).join("\n")}
662
+ `;
663
+ }
664
+ function formatMobileResult(result) {
665
+ return `# Mobile SEO Analysis
666
+
667
+ **Score:** ${result.score}/100
668
+
669
+ ## Viewport
670
+
671
+ - Has Viewport: ${result.viewport.hasViewport ? "\u2705" : "\u274C"}
672
+ - Responsive: ${result.viewport.isResponsive ? "\u2705" : "\u274C"}
673
+ ${result.viewport.viewportContent ? `- Content: \`${result.viewport.viewportContent}\`` : ""}
674
+
675
+ ## Touch Targets
676
+
677
+ - Small Targets: ${result.touchTargets.smallTargets}
678
+ - Proper Targets: ${result.touchTargets.properTargets}
679
+
680
+ ## Mobile Features
681
+
682
+ - Apple Touch Icon: ${result.mobileSpecific.hasAppleTouchIcon ? "\u2705" : "\u274C"}
683
+ - Theme Color: ${result.mobileSpecific.hasThemeColor ? "\u2705" : "\u274C"}
684
+ - Web App Manifest: ${result.mobileSpecific.hasManifest ? "\u2705" : "\u274C"}
685
+ - Responsive Images: ${result.mobileSpecific.hasMobileOptimizedImages ? "\u2705" : "\u274C"}
686
+
687
+ ## Issues
688
+
689
+ ${result.issues.map((i) => `- **${i.title}:** ${i.howToFix}`).join("\n") || "No issues!"}
690
+
691
+ ## Recommendations
692
+
693
+ ${result.recommendations.map((r) => `- ${r}`).join("\n")}
694
+ `;
695
+ }
696
+ function formatAICrawlersInfo() {
697
+ const crawlers = Object.entries(analyzers.AI_CRAWLERS_INFO);
698
+ return `# Known AI Crawlers
699
+
700
+ ${crawlers.map(([name, info]) => `## ${name}
701
+ - **User-Agent:** ${info.userAgent}
702
+ - **Company:** ${info.company}
703
+ - **Purpose:** ${info.purpose}
704
+ `).join("\n")}
705
+
706
+ ## robots.txt Example
707
+
708
+ \`\`\`
709
+ User-agent: GPTBot
710
+ Allow: /
711
+
712
+ User-agent: Claude-Web
713
+ Allow: /
714
+
715
+ User-agent: PerplexityBot
716
+ Allow: /
717
+ \`\`\`
718
+ `;
719
+ }
720
+ async function main() {
721
+ const transport = new StdioServerTransport();
722
+ await server.connect(transport);
723
+ console.error("RankCLI MCP Server running on stdio");
724
+ }
725
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@rankcli/mcp-server",
3
+ "version": "0.0.1",
4
+ "description": "MCP (Model Context Protocol) server for RankCLI SEO analysis",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "rankcli-mcp": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsup src/index.ts --format esm --dts --clean",
13
+ "dev": "tsup src/index.ts --format esm --watch",
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "echo 'No tests yet'",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "seo",
22
+ "ai",
23
+ "claude",
24
+ "rankcli"
25
+ ],
26
+ "author": "Integrallis",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.0.0",
30
+ "@rankcli/agent-runtime": "^0.0.11"
31
+ },
32
+ "devDependencies": {
33
+ "tsup": "^8.0.0",
34
+ "typescript": "^5.4.0"
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }