@goflag/mcp 0.1.0 → 0.1.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.
|
@@ -16,7 +16,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
16
16
|
import { z } from "zod";
|
|
17
17
|
|
|
18
18
|
// package.json
|
|
19
|
-
var version = "0.1.
|
|
19
|
+
var version = "0.1.1";
|
|
20
20
|
|
|
21
21
|
// src/server.ts
|
|
22
22
|
var RULE_DOCS = "https://goflag.tech/docs/rules/";
|
|
@@ -222,4 +222,4 @@ function createGoflagServer() {
|
|
|
222
222
|
export {
|
|
223
223
|
createGoflagServer
|
|
224
224
|
};
|
|
225
|
-
//# sourceMappingURL=chunk-
|
|
225
|
+
//# sourceMappingURL=chunk-YJOBO34N.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../package.json"],"sourcesContent":["/**\n * The goflag MCP server: four read-only tools over the CLI's own engine.\n *\n * Local on purpose (`docs/integrations-plan.md` D1). An MCP server that runs\n * somewhere else cannot see `localhost:3000`, nor a preview behind a company\n * VPN; this one runs where the agent runs and sees what it sees. It is also\n * thin on purpose: `runAudit`, `summarize`, `diffReports` and the rule\n * catalogue are the CLI's, imported by name, so an answer here is the answer\n * `goflag` would print — never a second opinion that drifts from it.\n *\n * What it deliberately does not offer:\n *\n * - No `start`. Letting an agent pass a command for this process to run is an\n * arbitrary-execution door; the agent starts its own dev server.\n * - No headers. A `--header` value passed as a tool argument would sit in the\n * agent's transcript. A protected preview is audited with the CLI.\n * - No write. A baseline is captured with `goflag --update-baseline`; this\n * server only reads one.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nimport {\n buildRuleCatalog,\n diffExitCode,\n diffReports,\n exitCode,\n runAudit,\n summarize,\n totalFindings,\n type AuditOptions,\n type CatalogRule,\n type GoflagReport,\n type ProgressEvent,\n} from \"@goflag/cli\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolResult,\n ServerNotification,\n ServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { version } from \"../package.json\";\n\n/** Where a rule explains itself, for a human the agent hands a link to. */\nconst RULE_DOCS = \"https://goflag.tech/docs/rules/\";\n\n/**\n * The page budget when the agent sets none. The CLI's is 200, sized for a CI\n * job that has minutes; an agent waits on the answer, and on openfinanceguide\n * the link check alone took 618 s of a 715 s run (0.2.15). An agent can ask for\n * more; a first call should come back fast.\n */\nconst DEFAULT_MAX_PAGES = 50;\n\n/** One progress notification per this many milliseconds at most, phase changes aside. */\nconst PROGRESS_INTERVAL_MS = 500;\n\nconst INSTRUCTIONS = `goflag audits a website's SEO and i18n plumbing: hreflang clusters, translation holes, sitemap and robots.txt agreement, canonical, Open Graph, icons, llms.txt, soft 404s.\n\nUse it on the site you are editing, before committing a change to metadata, the sitemap, robots.txt or hreflang: start the dev server, call audit_site on its URL, fix what is new, and call it again. explain_rule gives a rule's requirement, how authoritative it is, and the documents behind it. compare_to_baseline answers \"did my change make it worse?\" against a report saved with the goflag CLI.\n\nFindings carry a rigor: spec-required and vendor-spec are requirements, guideline and heuristic are advice.`;\n\nconst auditShape = {\n url: z\n .string()\n .describe(\"The site to audit, http or https. A dev server's URL works: http://localhost:3000.\"),\n maxPages: z\n .number()\n .int()\n .min(1)\n .max(500)\n .optional()\n .describe(`Page budget. Default ${DEFAULT_MAX_PAGES}.`),\n locales: z\n .array(z.string())\n .optional()\n .describe('Locales the site serves, e.g. [\"en\", \"fr\"], when its sitemap does not say.'),\n expectIndexing: z\n .enum([\"no\", \"yes\"])\n .optional()\n .describe('\"no\" for a staging or preview host that must stay out of search indexes.'),\n static: z\n .boolean()\n .optional()\n .describe(\"Never launch Chromium; judge the server-rendered HTML only.\"),\n advisories: z\n .boolean()\n .optional()\n .describe(\n \"Also return the questions a linter cannot settle, with their evidence, for you to judge.\",\n ),\n checkExternal: z\n .boolean()\n .optional()\n .describe(\"Also probe links to other sites. Off by default: slow, and not your site's fault.\"),\n};\n\ntype AuditArgs = {\n url: string;\n maxPages?: number;\n locales?: string[];\n expectIndexing?: \"no\" | \"yes\";\n static?: boolean;\n advisories?: boolean;\n checkExternal?: boolean;\n};\n\ntype ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\nfunction auditOptions(args: AuditArgs, extra: ToolExtra): AuditOptions {\n return {\n maxPages: args.maxPages ?? DEFAULT_MAX_PAGES,\n locales: args.locales,\n expectIndexing: args.expectIndexing,\n static: args.static,\n advisories: args.advisories,\n checkExternal: args.checkExternal ?? false,\n signal: extra.signal,\n onProgress: progressReporter(extra),\n };\n}\n\n/**\n * Crawl progress as MCP progress notifications, when the client asked for them\n * (a `progressToken`). Throttled: a crawl reports every page, and a client\n * drawing a spinner needs a heartbeat, not a stream. `progress` only grows, as\n * the protocol requires; the crawl's own total grows as links are found, so it\n * goes in the message rather than in `total`.\n */\nfunction progressReporter(extra: ToolExtra): ((event: ProgressEvent) => void) | undefined {\n const token = extra._meta?.progressToken;\n if (token === undefined) return undefined;\n let progress = 0;\n let lastPhase = \"\";\n let lastSent = 0;\n return (event) => {\n progress += 1;\n const now = Date.now();\n if (event.phase === lastPhase && now - lastSent < PROGRESS_INTERVAL_MS) return;\n lastPhase = event.phase;\n lastSent = now;\n extra\n .sendNotification({\n method: \"notifications/progress\",\n params: {\n progressToken: token,\n progress,\n message: `${event.phase}: ${event.done} of ${event.total}`,\n },\n })\n .catch(() => undefined);\n };\n}\n\nfunction ok(text: string, structured: Record<string, unknown>): CallToolResult {\n return {\n content: [{ type: \"text\", text: `${text}\\n\\n${JSON.stringify(structured)}` }],\n structuredContent: structured,\n };\n}\n\nfunction failure(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/** One line a human can read before the JSON an agent reads. */\nfunction headline(report: GoflagReport): string {\n const total = totalFindings(report);\n const coverage = report.diagnostics.coverage;\n const pages =\n coverage?.selected !== undefined && coverage.considered !== undefined\n ? `${coverage.selected} of ${coverage.considered} sitemap URLs`\n : `${report.diagnostics.pagesCrawled} pages`;\n return `${report.url}: ${report.summary.verdict} flag, ${total} finding${total === 1 ? \"\" : \"s\"}, ${pages} audited.`;\n}\n\n/**\n * Read a report saved by the CLI. Errors say what was wrong with the file and\n * never quote it: an agent can point this at any path, and a parse error that\n * echoes the first bytes of `~/.ssh/id_ed25519` into the transcript would make\n * a read-only tool a way to read files the agent was not shown.\n */\nasync function readBaseline(path: string): Promise<GoflagReport | string> {\n if (extname(path).toLowerCase() !== \".json\") {\n return `${path} is not a .json file. Pass a report saved by goflag --report or --update-baseline.`;\n }\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n return `Could not read ${path}.`;\n }\n try {\n const parsed = JSON.parse(raw) as Partial<GoflagReport>;\n if (typeof parsed.url === \"string\" && parsed.summary && Array.isArray(parsed.seoIssues)) {\n return parsed as GoflagReport;\n }\n } catch {\n // Falls through to the same answer as a JSON file of the wrong shape.\n }\n return `${path} is not a goflag JSON report.`;\n}\n\nfunction ruleView(rule: CatalogRule, sources: ReturnType<typeof buildRuleCatalog>[\"sources\"]) {\n return {\n ...rule,\n sources: rule.sources.map((id) => sources[id] ?? { id }),\n docs: `${RULE_DOCS}${rule.id}`,\n };\n}\n\n/** A server with goflag's four tools, not yet connected to a transport. */\nexport function createGoflagServer(): McpServer {\n const catalog = buildRuleCatalog();\n const server = new McpServer({ name: \"goflag\", version }, { instructions: INSTRUCTIONS });\n\n server.registerTool(\n \"audit_site\",\n {\n title: \"Audit a site\",\n description:\n \"Crawl a site — a local dev server, a preview or production — and judge its SEO and i18n plumbing. Returns the findings rolled up by rule, each with its rigor, plus translation holes, broken links and the pages audited.\",\n inputSchema: auditShape,\n annotations: { readOnlyHint: true, openWorldHint: true, idempotentHint: true },\n },\n async (args, extra) => {\n try {\n const report = await runAudit(args.url, auditOptions(args, extra));\n return ok(headline(report), {\n exitCode: exitCode(report),\n coverage: report.diagnostics.coverage ?? null,\n summary: summarize(report),\n });\n } catch (err) {\n return failure((err as Error).message);\n }\n },\n );\n\n server.registerTool(\n \"compare_to_baseline\",\n {\n title: \"Compare to a baseline\",\n description:\n \"Audit a site and compare it to a report saved earlier with the goflag CLI (--report or --update-baseline). Returns what is new, what was resolved, and how many findings are unchanged: the question a change has to answer is whether it made things worse.\",\n inputSchema: {\n ...auditShape,\n baseline: z\n .string()\n .describe(\"Path to the saved JSON report, relative to where this server runs.\"),\n },\n annotations: { readOnlyHint: true, openWorldHint: true, idempotentHint: true },\n },\n async ({ baseline: path, ...args }, extra) => {\n const baseline = await readBaseline(path);\n if (typeof baseline === \"string\") return failure(baseline);\n try {\n const report = await runAudit(args.url, auditOptions(args, extra));\n const diff = diffReports(baseline, report);\n return ok(\n `${headline(report)} Against the baseline: ${diff.added.length} new, ${diff.resolved.length} resolved, ${diff.unchanged} unchanged.`,\n {\n exitCode: diffExitCode(diff, \"warning\", { total: totalFindings(report) }),\n baseline: diff.baseline,\n added: diff.added,\n resolved: diff.resolved,\n unchanged: diff.unchanged,\n ...(diff.profileMismatch ? { profileMismatch: diff.profileMismatch } : {}),\n ...(diff.indexingMismatch ? { indexingMismatch: diff.indexingMismatch } : {}),\n },\n );\n } catch (err) {\n return failure((err as Error).message);\n }\n },\n );\n\n server.registerTool(\n \"explain_rule\",\n {\n title: \"Explain a rule\",\n description:\n \"What one goflag rule checks, why, how authoritative it is (rigor), the documents that require it, and what a passing page looks like.\",\n inputSchema: {\n id: z.string().describe('A rule id from a finding, e.g. \"hreflang.cluster-incomplete\".'),\n },\n annotations: { readOnlyHint: true, openWorldHint: false, idempotentHint: true },\n },\n async ({ id }) => {\n const rule = catalog.rules.find((candidate) => candidate.id === id);\n if (!rule) {\n const family = id.split(\".\")[0] ?? \"\";\n const near = catalog.rules\n .filter((candidate) => candidate.id.startsWith(`${family}.`))\n .map((candidate) => candidate.id);\n return failure(\n `No rule \"${id}\".${near.length > 0 ? ` Rules in ${family}: ${near.join(\", \")}.` : \"\"} list_rules returns them all.`,\n );\n }\n return ok(`${rule.id}: ${rule.summary}`, ruleView(rule, catalog.sources));\n },\n );\n\n server.registerTool(\n \"list_rules\",\n {\n title: \"List the rules\",\n description:\n \"Every rule goflag runs, with its scope (page, site, prose), severity and rigor. Prose rules are questions for a reviewer, never findings.\",\n inputSchema: {\n scope: z\n .enum([\"page\", \"site\", \"prose\"])\n .optional()\n .describe(\"Only the rules of one scope.\"),\n },\n annotations: { readOnlyHint: true, openWorldHint: false, idempotentHint: true },\n },\n async ({ scope }) => {\n const rules = catalog.rules\n .filter((rule) => scope === undefined || rule.scope === scope)\n .map(({ id, scope: ruleScope, severity, rigor, summary }) => ({\n id,\n scope: ruleScope,\n severity,\n rigor,\n summary,\n }));\n return ok(`${rules.length} rule${rules.length === 1 ? \"\" : \"s\"}.`, { rules });\n },\n );\n\n return server;\n}\n","{\n \"name\": \"@goflag/mcp\",\n \"version\": \"0.1.0\",\n \"type\": \"module\",\n \"description\": \"A local MCP server for goflag: an agent audits the site it is editing — localhost or a protected preview — and reads each rule with its source.\",\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"seo\",\n \"i18n\",\n \"hreflang\",\n \"audit\",\n \"agent\",\n \"claude\",\n \"cursor\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Tancrède Simonin <39413504+tancredesimonin@users.noreply.github.com>\",\n \"homepage\": \"https://github.com/tancredesimonin/goflag#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/tancredesimonin/goflag.git\",\n \"directory\": \"packages/mcp\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/tancredesimonin/goflag/issues\"\n },\n \"mcpName\": \"io.github.tancredesimonin/goflag\",\n \"engines\": {\n \"node\": \">=22\"\n },\n \"bin\": {\n \"goflag-mcp\": \"dist/cli.js\"\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"prepublishOnly\": \"pnpm build\",\n \"clean\": \"rm -rf dist coverage\",\n \"typecheck\": \"pnpm --filter @goflag/cli build && tsc --noEmit\",\n \"test\": \"pnpm --filter @goflag/cli build && vitest run\",\n \"test:unit\": \"pnpm --filter @goflag/cli build && vitest run\",\n \"test:watch\": \"vitest\",\n \"release\": \"commit-and-tag-version --path . --skip.tag --tag-prefix mcp-v\"\n },\n \"dependencies\": {\n \"@goflag/cli\": \"workspace:^\",\n \"@modelcontextprotocol/sdk\": \"^1.32.2\",\n \"zod\": \"^4.3.6\"\n },\n \"devDependencies\": {\n \"@hono/node-server\": \"^2.0.1\",\n \"@types/node\": \"^22.10.5\",\n \"commit-and-tag-version\": \"^12.5.0\",\n \"hono\": \"^4.12.25\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.0\",\n \"vitest\": \"^5.0.5\"\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../package.json"],"sourcesContent":["/**\n * The goflag MCP server: four read-only tools over the CLI's own engine.\n *\n * Local on purpose (`docs/integrations-plan.md` D1). An MCP server that runs\n * somewhere else cannot see `localhost:3000`, nor a preview behind a company\n * VPN; this one runs where the agent runs and sees what it sees. It is also\n * thin on purpose: `runAudit`, `summarize`, `diffReports` and the rule\n * catalogue are the CLI's, imported by name, so an answer here is the answer\n * `goflag` would print — never a second opinion that drifts from it.\n *\n * What it deliberately does not offer:\n *\n * - No `start`. Letting an agent pass a command for this process to run is an\n * arbitrary-execution door; the agent starts its own dev server.\n * - No headers. A `--header` value passed as a tool argument would sit in the\n * agent's transcript. A protected preview is audited with the CLI.\n * - No write. A baseline is captured with `goflag --update-baseline`; this\n * server only reads one.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nimport {\n buildRuleCatalog,\n diffExitCode,\n diffReports,\n exitCode,\n runAudit,\n summarize,\n totalFindings,\n type AuditOptions,\n type CatalogRule,\n type GoflagReport,\n type ProgressEvent,\n} from \"@goflag/cli\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolResult,\n ServerNotification,\n ServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { version } from \"../package.json\";\n\n/** Where a rule explains itself, for a human the agent hands a link to. */\nconst RULE_DOCS = \"https://goflag.tech/docs/rules/\";\n\n/**\n * The page budget when the agent sets none. The CLI's is 200, sized for a CI\n * job that has minutes; an agent waits on the answer, and on openfinanceguide\n * the link check alone took 618 s of a 715 s run (0.2.15). An agent can ask for\n * more; a first call should come back fast.\n */\nconst DEFAULT_MAX_PAGES = 50;\n\n/** One progress notification per this many milliseconds at most, phase changes aside. */\nconst PROGRESS_INTERVAL_MS = 500;\n\nconst INSTRUCTIONS = `goflag audits a website's SEO and i18n plumbing: hreflang clusters, translation holes, sitemap and robots.txt agreement, canonical, Open Graph, icons, llms.txt, soft 404s.\n\nUse it on the site you are editing, before committing a change to metadata, the sitemap, robots.txt or hreflang: start the dev server, call audit_site on its URL, fix what is new, and call it again. explain_rule gives a rule's requirement, how authoritative it is, and the documents behind it. compare_to_baseline answers \"did my change make it worse?\" against a report saved with the goflag CLI.\n\nFindings carry a rigor: spec-required and vendor-spec are requirements, guideline and heuristic are advice.`;\n\nconst auditShape = {\n url: z\n .string()\n .describe(\"The site to audit, http or https. A dev server's URL works: http://localhost:3000.\"),\n maxPages: z\n .number()\n .int()\n .min(1)\n .max(500)\n .optional()\n .describe(`Page budget. Default ${DEFAULT_MAX_PAGES}.`),\n locales: z\n .array(z.string())\n .optional()\n .describe('Locales the site serves, e.g. [\"en\", \"fr\"], when its sitemap does not say.'),\n expectIndexing: z\n .enum([\"no\", \"yes\"])\n .optional()\n .describe('\"no\" for a staging or preview host that must stay out of search indexes.'),\n static: z\n .boolean()\n .optional()\n .describe(\"Never launch Chromium; judge the server-rendered HTML only.\"),\n advisories: z\n .boolean()\n .optional()\n .describe(\n \"Also return the questions a linter cannot settle, with their evidence, for you to judge.\",\n ),\n checkExternal: z\n .boolean()\n .optional()\n .describe(\"Also probe links to other sites. Off by default: slow, and not your site's fault.\"),\n};\n\ntype AuditArgs = {\n url: string;\n maxPages?: number;\n locales?: string[];\n expectIndexing?: \"no\" | \"yes\";\n static?: boolean;\n advisories?: boolean;\n checkExternal?: boolean;\n};\n\ntype ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\nfunction auditOptions(args: AuditArgs, extra: ToolExtra): AuditOptions {\n return {\n maxPages: args.maxPages ?? DEFAULT_MAX_PAGES,\n locales: args.locales,\n expectIndexing: args.expectIndexing,\n static: args.static,\n advisories: args.advisories,\n checkExternal: args.checkExternal ?? false,\n signal: extra.signal,\n onProgress: progressReporter(extra),\n };\n}\n\n/**\n * Crawl progress as MCP progress notifications, when the client asked for them\n * (a `progressToken`). Throttled: a crawl reports every page, and a client\n * drawing a spinner needs a heartbeat, not a stream. `progress` only grows, as\n * the protocol requires; the crawl's own total grows as links are found, so it\n * goes in the message rather than in `total`.\n */\nfunction progressReporter(extra: ToolExtra): ((event: ProgressEvent) => void) | undefined {\n const token = extra._meta?.progressToken;\n if (token === undefined) return undefined;\n let progress = 0;\n let lastPhase = \"\";\n let lastSent = 0;\n return (event) => {\n progress += 1;\n const now = Date.now();\n if (event.phase === lastPhase && now - lastSent < PROGRESS_INTERVAL_MS) return;\n lastPhase = event.phase;\n lastSent = now;\n extra\n .sendNotification({\n method: \"notifications/progress\",\n params: {\n progressToken: token,\n progress,\n message: `${event.phase}: ${event.done} of ${event.total}`,\n },\n })\n .catch(() => undefined);\n };\n}\n\nfunction ok(text: string, structured: Record<string, unknown>): CallToolResult {\n return {\n content: [{ type: \"text\", text: `${text}\\n\\n${JSON.stringify(structured)}` }],\n structuredContent: structured,\n };\n}\n\nfunction failure(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/** One line a human can read before the JSON an agent reads. */\nfunction headline(report: GoflagReport): string {\n const total = totalFindings(report);\n const coverage = report.diagnostics.coverage;\n const pages =\n coverage?.selected !== undefined && coverage.considered !== undefined\n ? `${coverage.selected} of ${coverage.considered} sitemap URLs`\n : `${report.diagnostics.pagesCrawled} pages`;\n return `${report.url}: ${report.summary.verdict} flag, ${total} finding${total === 1 ? \"\" : \"s\"}, ${pages} audited.`;\n}\n\n/**\n * Read a report saved by the CLI. Errors say what was wrong with the file and\n * never quote it: an agent can point this at any path, and a parse error that\n * echoes the first bytes of `~/.ssh/id_ed25519` into the transcript would make\n * a read-only tool a way to read files the agent was not shown.\n */\nasync function readBaseline(path: string): Promise<GoflagReport | string> {\n if (extname(path).toLowerCase() !== \".json\") {\n return `${path} is not a .json file. Pass a report saved by goflag --report or --update-baseline.`;\n }\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n return `Could not read ${path}.`;\n }\n try {\n const parsed = JSON.parse(raw) as Partial<GoflagReport>;\n if (typeof parsed.url === \"string\" && parsed.summary && Array.isArray(parsed.seoIssues)) {\n return parsed as GoflagReport;\n }\n } catch {\n // Falls through to the same answer as a JSON file of the wrong shape.\n }\n return `${path} is not a goflag JSON report.`;\n}\n\nfunction ruleView(rule: CatalogRule, sources: ReturnType<typeof buildRuleCatalog>[\"sources\"]) {\n return {\n ...rule,\n sources: rule.sources.map((id) => sources[id] ?? { id }),\n docs: `${RULE_DOCS}${rule.id}`,\n };\n}\n\n/** A server with goflag's four tools, not yet connected to a transport. */\nexport function createGoflagServer(): McpServer {\n const catalog = buildRuleCatalog();\n const server = new McpServer({ name: \"goflag\", version }, { instructions: INSTRUCTIONS });\n\n server.registerTool(\n \"audit_site\",\n {\n title: \"Audit a site\",\n description:\n \"Crawl a site — a local dev server, a preview or production — and judge its SEO and i18n plumbing. Returns the findings rolled up by rule, each with its rigor, plus translation holes, broken links and the pages audited.\",\n inputSchema: auditShape,\n annotations: { readOnlyHint: true, openWorldHint: true, idempotentHint: true },\n },\n async (args, extra) => {\n try {\n const report = await runAudit(args.url, auditOptions(args, extra));\n return ok(headline(report), {\n exitCode: exitCode(report),\n coverage: report.diagnostics.coverage ?? null,\n summary: summarize(report),\n });\n } catch (err) {\n return failure((err as Error).message);\n }\n },\n );\n\n server.registerTool(\n \"compare_to_baseline\",\n {\n title: \"Compare to a baseline\",\n description:\n \"Audit a site and compare it to a report saved earlier with the goflag CLI (--report or --update-baseline). Returns what is new, what was resolved, and how many findings are unchanged: the question a change has to answer is whether it made things worse.\",\n inputSchema: {\n ...auditShape,\n baseline: z\n .string()\n .describe(\"Path to the saved JSON report, relative to where this server runs.\"),\n },\n annotations: { readOnlyHint: true, openWorldHint: true, idempotentHint: true },\n },\n async ({ baseline: path, ...args }, extra) => {\n const baseline = await readBaseline(path);\n if (typeof baseline === \"string\") return failure(baseline);\n try {\n const report = await runAudit(args.url, auditOptions(args, extra));\n const diff = diffReports(baseline, report);\n return ok(\n `${headline(report)} Against the baseline: ${diff.added.length} new, ${diff.resolved.length} resolved, ${diff.unchanged} unchanged.`,\n {\n exitCode: diffExitCode(diff, \"warning\", { total: totalFindings(report) }),\n baseline: diff.baseline,\n added: diff.added,\n resolved: diff.resolved,\n unchanged: diff.unchanged,\n ...(diff.profileMismatch ? { profileMismatch: diff.profileMismatch } : {}),\n ...(diff.indexingMismatch ? { indexingMismatch: diff.indexingMismatch } : {}),\n },\n );\n } catch (err) {\n return failure((err as Error).message);\n }\n },\n );\n\n server.registerTool(\n \"explain_rule\",\n {\n title: \"Explain a rule\",\n description:\n \"What one goflag rule checks, why, how authoritative it is (rigor), the documents that require it, and what a passing page looks like.\",\n inputSchema: {\n id: z.string().describe('A rule id from a finding, e.g. \"hreflang.cluster-incomplete\".'),\n },\n annotations: { readOnlyHint: true, openWorldHint: false, idempotentHint: true },\n },\n async ({ id }) => {\n const rule = catalog.rules.find((candidate) => candidate.id === id);\n if (!rule) {\n const family = id.split(\".\")[0] ?? \"\";\n const near = catalog.rules\n .filter((candidate) => candidate.id.startsWith(`${family}.`))\n .map((candidate) => candidate.id);\n return failure(\n `No rule \"${id}\".${near.length > 0 ? ` Rules in ${family}: ${near.join(\", \")}.` : \"\"} list_rules returns them all.`,\n );\n }\n return ok(`${rule.id}: ${rule.summary}`, ruleView(rule, catalog.sources));\n },\n );\n\n server.registerTool(\n \"list_rules\",\n {\n title: \"List the rules\",\n description:\n \"Every rule goflag runs, with its scope (page, site, prose), severity and rigor. Prose rules are questions for a reviewer, never findings.\",\n inputSchema: {\n scope: z\n .enum([\"page\", \"site\", \"prose\"])\n .optional()\n .describe(\"Only the rules of one scope.\"),\n },\n annotations: { readOnlyHint: true, openWorldHint: false, idempotentHint: true },\n },\n async ({ scope }) => {\n const rules = catalog.rules\n .filter((rule) => scope === undefined || rule.scope === scope)\n .map(({ id, scope: ruleScope, severity, rigor, summary }) => ({\n id,\n scope: ruleScope,\n severity,\n rigor,\n summary,\n }));\n return ok(`${rules.length} rule${rules.length === 1 ? \"\" : \"s\"}.`, { rules });\n },\n );\n\n return server;\n}\n","{\n \"name\": \"@goflag/mcp\",\n \"version\": \"0.1.1\",\n \"type\": \"module\",\n \"description\": \"A local MCP server for goflag: an agent audits the site it is editing — localhost or a protected preview — and reads each rule with its source.\",\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"seo\",\n \"i18n\",\n \"hreflang\",\n \"audit\",\n \"agent\",\n \"claude\",\n \"cursor\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Tancrède Simonin <39413504+tancredesimonin@users.noreply.github.com>\",\n \"homepage\": \"https://github.com/tancredesimonin/goflag#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/tancredesimonin/goflag.git\",\n \"directory\": \"packages/mcp\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/tancredesimonin/goflag/issues\"\n },\n \"mcpName\": \"io.github.tancredesimonin/goflag\",\n \"engines\": {\n \"node\": \">=22\"\n },\n \"bin\": {\n \"goflag-mcp\": \"dist/cli.js\"\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"prepublishOnly\": \"pnpm build\",\n \"clean\": \"rm -rf dist coverage\",\n \"typecheck\": \"pnpm --filter @goflag/cli build && tsc --noEmit\",\n \"test\": \"pnpm --filter @goflag/cli build && vitest run\",\n \"test:unit\": \"pnpm --filter @goflag/cli build && vitest run\",\n \"test:watch\": \"vitest\",\n \"release\": \"commit-and-tag-version --path . --skip.tag --tag-prefix mcp-v\"\n },\n \"dependencies\": {\n \"@goflag/cli\": \"workspace:^\",\n \"@modelcontextprotocol/sdk\": \"^1.32.2\",\n \"zod\": \"^4.3.6\"\n },\n \"devDependencies\": {\n \"@hono/node-server\": \"^2.0.1\",\n \"@types/node\": \"^22.10.5\",\n \"commit-and-tag-version\": \"^12.5.0\",\n \"hono\": \"^4.12.25\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.0\",\n \"vitest\": \"^5.0.5\"\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goflag/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A local MCP server for goflag: an agent audits the site it is editing — localhost or a protected preview — and reads each rule with its source.",
|
|
6
6
|
"keywords": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@goflag/cli": "^0.2.
|
|
50
|
+
"@goflag/cli": "^0.2.19",
|
|
51
51
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
52
52
|
"zod": "^4.3.6"
|
|
53
53
|
},
|