@pseolint/mcp 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.
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +228 -0
- package/dist/server.js.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ouranos-labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @pseolint/mcp
|
|
2
|
+
|
|
3
|
+
> MCP server for pseolint — audit programmatic SEO sites from AI coding assistants.
|
|
4
|
+
|
|
5
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that exposes [pseolint](https://www.npmjs.com/package/pseolint) auditing tools to AI coding assistants like Claude Code, Claude Desktop, Cursor, and Windsurf.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
### `audit_site`
|
|
10
|
+
|
|
11
|
+
Run a full pseolint audit on a URL or directory path. Returns the SpamBrain Risk Score (0-100) and all findings with actionable fix suggestions.
|
|
12
|
+
|
|
13
|
+
**Parameters:**
|
|
14
|
+
- `source` (required) — URL or directory path to audit
|
|
15
|
+
- `threshold` — Score threshold for pass/fail (default: 40)
|
|
16
|
+
- `sampleSize` — Audit a random subset of N pages (0 = all)
|
|
17
|
+
- `format` — Output format: `console` or `json` (default: console)
|
|
18
|
+
|
|
19
|
+
**Example prompt:** "Audit my site at http://localhost:3000 for SpamBrain risk"
|
|
20
|
+
|
|
21
|
+
### `explain_score`
|
|
22
|
+
|
|
23
|
+
Run an audit and get a human-readable explanation of what's driving the SpamBrain Risk Score, including category breakdowns, top issues, and prioritized fix suggestions.
|
|
24
|
+
|
|
25
|
+
**Parameters:**
|
|
26
|
+
- `source` (required) — URL or directory path to audit
|
|
27
|
+
|
|
28
|
+
**Example prompt:** "Explain why my site's SpamBrain score is high"
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
### Claude Code
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
claude mcp add pseolint -- npx @pseolint/mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Claude Desktop
|
|
39
|
+
|
|
40
|
+
Add to `claude_desktop_config.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"pseolint": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["@pseolint/mcp"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Cursor
|
|
54
|
+
|
|
55
|
+
Add to `.cursor/mcp.json`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"pseolint": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["@pseolint/mcp"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Windsurf
|
|
69
|
+
|
|
70
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"pseolint": {
|
|
76
|
+
"command": "npx",
|
|
77
|
+
"args": ["@pseolint/mcp"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### VS Code (GitHub Copilot)
|
|
84
|
+
|
|
85
|
+
Add to `.vscode/mcp.json`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"servers": {
|
|
90
|
+
"pseolint": {
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["@pseolint/mcp"]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## What It Checks
|
|
99
|
+
|
|
100
|
+
34 rules across 6 categories:
|
|
101
|
+
|
|
102
|
+
- **SpamBrain Risk** — near-duplicate detection, entity-swap doorway pages, thin content, boilerplate ratio
|
|
103
|
+
- **Content Quality** — unique value per page, heading/meta uniqueness, E-E-A-T signals
|
|
104
|
+
- **Internal Linking** — orphan pages, dead ends, cluster connectivity, link depth
|
|
105
|
+
- **Technical SEO** — canonical consistency, sitemap completeness, robots.txt conflicts
|
|
106
|
+
- **Structured Data** — JSON-LD validation, required fields, schema consistency
|
|
107
|
+
- **Cannibalization** — title overlap, keyword collision, URL pattern conflicts
|
|
108
|
+
|
|
109
|
+
## Links
|
|
110
|
+
|
|
111
|
+
- [GitHub](https://github.com/ouranos-labs/pseolint)
|
|
112
|
+
- [npm: pseolint](https://www.npmjs.com/package/pseolint)
|
|
113
|
+
- [npm: @pseolint/core](https://www.npmjs.com/package/@pseolint/core)
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7B,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuGpE,wBAAgB,YAAY,IAAI,SAAS,CAqJxC;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAIpD"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { auditSource, formatConsole, formatJson } from "@pseolint/core";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { version } = require("../package.json");
|
|
8
|
+
const MCP_SAMPLE_CAP = 50;
|
|
9
|
+
function friendlyError(err, source) {
|
|
10
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11
|
+
if (msg.includes("fetch") || msg.includes("ECONNREFUSED")) {
|
|
12
|
+
return `Could not reach ${source}. If this is a local dev server, make sure it is running. Original error: ${msg}`;
|
|
13
|
+
}
|
|
14
|
+
if (msg.includes("Unable to access source")) {
|
|
15
|
+
return `Directory not found: ${source}. Check the path exists and is readable.`;
|
|
16
|
+
}
|
|
17
|
+
if (msg.includes("ETIMEDOUT") || msg.includes("timeout")) {
|
|
18
|
+
return `Request to ${source} timed out. The site may be slow or unreachable. Try increasing the timeout or using --sample-size to audit fewer pages.`;
|
|
19
|
+
}
|
|
20
|
+
return `Audit failed for ${source}: ${msg}`;
|
|
21
|
+
}
|
|
22
|
+
function scoreLabel(score) {
|
|
23
|
+
if (score <= 20)
|
|
24
|
+
return "Safe";
|
|
25
|
+
if (score <= 40)
|
|
26
|
+
return "Caution";
|
|
27
|
+
if (score <= 60)
|
|
28
|
+
return "Risky";
|
|
29
|
+
if (score <= 80)
|
|
30
|
+
return "Dangerous";
|
|
31
|
+
return "Critical";
|
|
32
|
+
}
|
|
33
|
+
function buildExplanation(summary, threshold) {
|
|
34
|
+
const lines = [];
|
|
35
|
+
const label = scoreLabel(summary.score);
|
|
36
|
+
const passFail = summary.score >= threshold ? "FAIL" : "PASS";
|
|
37
|
+
lines.push(`SpamBrain Risk Score: ${summary.score}/100 (${label}) — ${passFail} at threshold ${threshold}`);
|
|
38
|
+
lines.push(`Pages analysed: ${summary.pageCount}`);
|
|
39
|
+
if (summary.templateDetected) {
|
|
40
|
+
lines.push("");
|
|
41
|
+
lines.push("Template-generated content detected. Fix suggestions are tailored for template authors.");
|
|
42
|
+
}
|
|
43
|
+
lines.push("");
|
|
44
|
+
lines.push("Category breakdown:");
|
|
45
|
+
for (const [cat, score] of Object.entries(summary.categoryScores)) {
|
|
46
|
+
const catLabel = cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
47
|
+
lines.push(` ${catLabel}: ${score}/100`);
|
|
48
|
+
}
|
|
49
|
+
const ruleCounts = new Map();
|
|
50
|
+
for (const f of summary.findings) {
|
|
51
|
+
ruleCounts.set(f.ruleId, (ruleCounts.get(f.ruleId) ?? 0) + 1);
|
|
52
|
+
}
|
|
53
|
+
if (ruleCounts.size > 0) {
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push("What's driving the score (fix in this order):");
|
|
56
|
+
const effortOrder = ["quick", "moderate", "structural"];
|
|
57
|
+
const withEffort = Array.from(ruleCounts.entries()).map(([ruleId, count]) => {
|
|
58
|
+
const finding = summary.findings.find((f) => f.ruleId === ruleId);
|
|
59
|
+
return { ruleId, count, finding };
|
|
60
|
+
}).sort((a, b) => {
|
|
61
|
+
const ea = effortOrder.indexOf(a.finding?.effort ?? "moderate");
|
|
62
|
+
const eb = effortOrder.indexOf(b.finding?.effort ?? "moderate");
|
|
63
|
+
if (ea !== eb)
|
|
64
|
+
return ea - eb;
|
|
65
|
+
return b.count - a.count;
|
|
66
|
+
});
|
|
67
|
+
for (const { ruleId, count, finding } of withEffort.slice(0, 10)) {
|
|
68
|
+
const effortTag = finding?.effort ? `[${finding.effort}] ` : "";
|
|
69
|
+
lines.push(` ${effortTag}${ruleId}: ${count} finding${count !== 1 ? "s" : ""}`);
|
|
70
|
+
if (finding?.fix) {
|
|
71
|
+
lines.push(` → ${finding.fix}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (summary.score >= threshold) {
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push(`Your score of ${summary.score} exceeds the threshold of ${threshold}. Focus on the quick fixes first to bring the score down, then tackle structural issues.`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
lines.push("");
|
|
81
|
+
lines.push(`Your score of ${summary.score} is below the threshold of ${threshold}. Site passes CI checks.`);
|
|
82
|
+
}
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
}
|
|
85
|
+
const CROSS_PAGE_RULES = new Set([
|
|
86
|
+
"spam/near-duplicate", "spam/entity-swap", "spam/boilerplate-ratio",
|
|
87
|
+
"spam/template-diversity", "spam/publication-velocity", "spam/doorway-pattern",
|
|
88
|
+
"spam/template-coverage", "content/unique-value", "content/heading-uniqueness",
|
|
89
|
+
"content/meta-uniqueness", "cannibal/title-overlap", "cannibal/keyword-collision",
|
|
90
|
+
"cannibal/url-pattern", "links/orphan-pages", "links/dead-ends",
|
|
91
|
+
"links/cluster-connectivity", "links/hub-pages",
|
|
92
|
+
]);
|
|
93
|
+
export function createServer() {
|
|
94
|
+
const server = new McpServer({
|
|
95
|
+
name: "pseolint",
|
|
96
|
+
version,
|
|
97
|
+
});
|
|
98
|
+
server.registerTool("audit_site", {
|
|
99
|
+
title: "Audit Site for SpamBrain Risk",
|
|
100
|
+
description: "Use when a user asks to check their website for SEO issues, SpamBrain risk, duplicate content, thin pages, or before deploying a programmatic SEO site. Crawls the site, runs 35 rules across 6 categories, and returns a SpamBrain Risk Score (0-100) with actionable findings. For sites with many pages, results are automatically capped to keep response times reasonable.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
source: z.string().describe("URL (e.g. http://localhost:3000) or local directory path (e.g. ./out) to audit"),
|
|
103
|
+
threshold: z.number().optional().default(40).describe("Score threshold — audit fails if score >= this value (default: 40)"),
|
|
104
|
+
sampleSize: z.number().optional().default(0).describe("Audit a random subset of N pages. 0 = all pages up to internal cap of 50 for MCP. Set explicitly to override."),
|
|
105
|
+
format: z.enum(["console", "json"]).optional().default("console").describe("Output format. Use 'json' for structured data, 'console' for human-readable summary."),
|
|
106
|
+
},
|
|
107
|
+
annotations: {
|
|
108
|
+
readOnlyHint: true,
|
|
109
|
+
destructiveHint: false,
|
|
110
|
+
openWorldHint: true,
|
|
111
|
+
},
|
|
112
|
+
}, async ({ source, threshold, sampleSize, format }) => {
|
|
113
|
+
try {
|
|
114
|
+
const options = {};
|
|
115
|
+
if (sampleSize > 0) {
|
|
116
|
+
options.sampleSize = sampleSize;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
options.sampleSize = MCP_SAMPLE_CAP;
|
|
120
|
+
}
|
|
121
|
+
const summary = await auditSource(source, options);
|
|
122
|
+
let text;
|
|
123
|
+
if (format === "json") {
|
|
124
|
+
text = formatJson(summary);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
text = formatConsole(summary, { noColor: true });
|
|
128
|
+
}
|
|
129
|
+
if (summary.pageCount >= MCP_SAMPLE_CAP && sampleSize === 0) {
|
|
130
|
+
text += `\n\nNote: Results capped to ${MCP_SAMPLE_CAP} pages for performance. Run the CLI directly for a full audit: npx pseolint ${source}`;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
content: [{ type: "text", text }],
|
|
134
|
+
isError: summary.score >= threshold,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: "text", text: friendlyError(err, source) }],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
server.registerTool("explain_score", {
|
|
145
|
+
title: "Explain SpamBrain Risk Score",
|
|
146
|
+
description: "Use when a user wants to understand WHY their SpamBrain score is high, what categories are failing, and what to fix first. Returns a prioritized breakdown with quick wins listed before structural fixes, and a pass/fail verdict against the threshold.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
source: z.string().describe("URL (e.g. http://localhost:3000) or local directory path (e.g. ./out) to audit"),
|
|
149
|
+
threshold: z.number().optional().default(40).describe("Score threshold for pass/fail verdict (default: 40)"),
|
|
150
|
+
},
|
|
151
|
+
annotations: {
|
|
152
|
+
readOnlyHint: true,
|
|
153
|
+
destructiveHint: false,
|
|
154
|
+
openWorldHint: true,
|
|
155
|
+
},
|
|
156
|
+
}, async ({ source, threshold }) => {
|
|
157
|
+
try {
|
|
158
|
+
const options = { sampleSize: MCP_SAMPLE_CAP };
|
|
159
|
+
const summary = await auditSource(source, options);
|
|
160
|
+
const text = buildExplanation(summary, threshold);
|
|
161
|
+
return {
|
|
162
|
+
content: [{ type: "text", text }],
|
|
163
|
+
isError: summary.score >= threshold,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: friendlyError(err, source) }],
|
|
169
|
+
isError: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
server.registerTool("check_page_technical", {
|
|
174
|
+
title: "Check Single Page Technical SEO",
|
|
175
|
+
description: "Use when a user asks to check a specific page URL for technical SEO issues. Checks per-page rules only: canonical tags, Open Graph tags, JSON-LD schema, robots directives, meta tags, thin content, and author signals. Does NOT check cross-page rules (duplicates, cannibalization, linking) — use audit_site for those.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
url: z.string().describe("Full URL of the page to check (e.g. https://yoursite.com/templates/california-llc)"),
|
|
178
|
+
},
|
|
179
|
+
annotations: {
|
|
180
|
+
readOnlyHint: true,
|
|
181
|
+
destructiveHint: false,
|
|
182
|
+
openWorldHint: false,
|
|
183
|
+
},
|
|
184
|
+
}, async ({ url }) => {
|
|
185
|
+
try {
|
|
186
|
+
const options = {
|
|
187
|
+
crawlDiscovery: false,
|
|
188
|
+
};
|
|
189
|
+
const summary = await auditSource(url, options);
|
|
190
|
+
const perPageFindings = summary.findings.filter((f) => !CROSS_PAGE_RULES.has(f.ruleId));
|
|
191
|
+
const lines = [];
|
|
192
|
+
lines.push(`Technical SEO check for ${url}`);
|
|
193
|
+
lines.push("");
|
|
194
|
+
if (perPageFindings.length === 0) {
|
|
195
|
+
lines.push("No technical SEO issues found on this page.");
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
lines.push(`Found ${perPageFindings.length} issue${perPageFindings.length !== 1 ? "s" : ""}:`);
|
|
199
|
+
lines.push("");
|
|
200
|
+
for (const f of perPageFindings) {
|
|
201
|
+
const effortTag = f.effort ? ` [${f.effort}]` : "";
|
|
202
|
+
lines.push(` ${f.severity.toUpperCase()}${effortTag}: ${f.message}`);
|
|
203
|
+
if (f.fix) {
|
|
204
|
+
lines.push(` → ${f.fix}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lines.push("");
|
|
209
|
+
lines.push("Note: This checks per-page technical rules only. For cross-page analysis (duplicates, cannibalization, linking), use audit_site.");
|
|
210
|
+
return {
|
|
211
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
return {
|
|
216
|
+
content: [{ type: "text", text: friendlyError(err, url) }],
|
|
217
|
+
isError: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
return server;
|
|
222
|
+
}
|
|
223
|
+
export async function startMcpServer() {
|
|
224
|
+
const server = createServer();
|
|
225
|
+
const transport = new StdioServerTransport();
|
|
226
|
+
await server.connect(transport);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pseolint/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for pseolint — audit programmatic SEO sites from AI coding assistants",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Ouranos Labs <contact@ouranos-labs.dev>",
|
|
7
|
+
"homepage": "https://pseolint.dev",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/ouranos-labs/pseolint.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["mcp", "model-context-protocol", "seo", "pseo", "spambrain", "lint", "ai"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"pseolint-mcp": "dist/bin.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json",
|
|
34
|
+
"lint": "tsc --noEmit -p tsconfig.json"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
|
+
"@pseolint/core": "workspace:*",
|
|
39
|
+
"zod": "^4.3.6"
|
|
40
|
+
}
|
|
41
|
+
}
|