pagesight 0.14.0 → 0.14.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 CHANGED
@@ -8,34 +8,40 @@ See your site the way search engines and AI see it.
8
8
  npm install pagesight
9
9
  ```
10
10
 
11
- Your AI assistant can write your code. Now it can see your site. Index status, performance, real-user metrics, search traffic, meta tags, structured data, AI crawler access — one package, one call.
11
+ Your AI assistant can write your code. Now it can see your site. Index status, performance, real-user metrics, search traffic, meta tags, structured data, AI crawler access, link health — one package, one call.
12
12
 
13
13
  ```
14
14
  === Site Audit: https://example.com ===
15
15
 
16
+ 2 checks failed — results below are partial:
17
+
18
+ FAIL PageSpeed: quota exceeded
19
+ FAIL Sitemaps: permission denied
20
+
21
+ 5 findings:
22
+
16
23
  HIGH Missing canonical URL
17
- HIGH 7,772 sitemap URLs submitted, 0 indexed
24
+ HIGH 22 sitemap URLs submitted, 0 indexed
25
+ Auto-inspected 5 URLs:
26
+ - 2/5 indexed
27
+ - 3/5 Discovered - currently not indexed: /docs/, /pricing/, /about/
18
28
  MEDIUM Missing og:image — no social preview image
19
- MEDIUM Accessibility score: 89/100
20
29
  LOW Missing Twitter Card tags
21
- LOW No structured data (JSON-LD) found
30
+ LOW 6/139 AI crawlers blocked
22
31
  ```
23
32
 
24
33
  ## Tools
25
34
 
26
- | Tool | What it does |
27
- |------|-------------|
28
- | `audit` | One-call site audit. Runs all checks in parallel. Returns prioritized findings. |
29
- | `pagespeed` | Lighthouse scores, Core Web Vitals, opportunities, failing audits with fix links. |
30
- | `metatags` | OG, Twitter Card, canonical, JSON-LD with schema validation, redirect chain, image validation. |
31
- | `inspect` | Google index status, canonical choice, crawl state, rich results. |
32
- | `sample_inspect` | Sample URLs from a sitemap and batch-inspect. Diagnoses indexing patterns. |
33
- | `performance` | Search analytics — clicks, impressions, CTR, position. `compare: true` for period-over-period. |
34
- | `crux` | Real-user Core Web Vitals (p75, histograms). |
35
- | `crux_history` | CWV trends over time — up to 40 weekly data points. |
36
- | `robots` | robots.txt validation (RFC 9309) + AI crawler audit (139+ bots). |
37
- | `sitemaps` | Search Console properties and sitemaps with submitted/indexed counts. |
38
- | `setup` | Auth status and OAuth setup. |
35
+ 6 tools organized by intent:
36
+
37
+ | Tool | Intent | What it does |
38
+ |------|--------|-------------|
39
+ | `audit` | How's my site? | One-call site audit. Runs all checks in parallel. Prioritized findings with auto-drill-down on indexing issues. |
40
+ | `page` | What's on this URL? | Meta tags, OG, Twitter Card, JSON-LD validation (19 schema types), internal link health, redirect chains, WCAG contrast checker. Batch mode for multiple URLs. |
41
+ | `speed` | How fast is it? | PageSpeed single/batch/compare with Lighthouse scores and opportunities. CrUX real-user metrics (snapshot + history trends). |
42
+ | `search` | How's Google seeing me? | URL inspection, sample-inspect from sitemaps, sitemap management, search analytics with period-over-period comparison. |
43
+ | `ai` | How's AI seeing me? | AI crawler audit (139+ bots by category), robots.txt validation (RFC 9309), llms.txt detection, path access checks. |
44
+ | `setup` | Auth config | Auth status check and OAuth setup flow. |
39
45
 
40
46
  ## Setup
41
47
 
@@ -58,7 +64,7 @@ Add to your MCP config:
58
64
  }
59
65
  ```
60
66
 
61
- `robots`, `metatags`, and `pagespeed` work without credentials.
67
+ `page`, `speed`, and `ai` work without credentials. `search` and `audit` (for GSC checks) require OAuth or a service account.
62
68
 
63
69
  ### Full setup
64
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pagesight",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "See your site the way search engines and AI see it.",
5
5
  "keywords": [
6
6
  "seo",
package/src/tools/ai.ts CHANGED
@@ -12,12 +12,24 @@ interface LlmsTxtResult {
12
12
 
13
13
  async function checkLlmsTxt(origin: string, path: string): Promise<LlmsTxtResult> {
14
14
  try {
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), 10_000);
15
17
  const res = await fetch(`${origin}${path}`, {
16
18
  headers: { "User-Agent": "Pagesight/1.0" },
17
19
  redirect: "follow",
20
+ signal: controller.signal,
18
21
  });
22
+ clearTimeout(timeout);
19
23
  if (!res.ok) return { exists: false, size: null, firstLine: null };
24
+ // Cap at 1MB to avoid OOM on large responses
25
+ const contentLength = Number(res.headers.get("content-length") ?? 0);
26
+ if (contentLength > 1_048_576) {
27
+ return { exists: true, size: contentLength, firstLine: "(file too large to preview)" };
28
+ }
20
29
  const text = await res.text();
30
+ if (text.length > 1_048_576) {
31
+ return { exists: true, size: text.length, firstLine: "(file too large to preview)" };
32
+ }
21
33
  const firstLine =
22
34
  text
23
35
  .split("\n")
@@ -215,6 +227,7 @@ export function registerAiTool(server: McpServer): void {
215
227
  {
216
228
  url: z
217
229
  .string()
230
+ .url()
218
231
  .describe("Site URL or origin (e.g., 'https://example.com'). Fetches /robots.txt from this origin."),
219
232
  check_path: z
220
233
  .string()
package/src/tools/page.ts CHANGED
@@ -615,7 +615,7 @@ async function checkLink(href: string): Promise<LinkResult> {
615
615
  try {
616
616
  for (let i = 0; i < 10; i++) {
617
617
  const res = await fetch(current, {
618
- method: "GET",
618
+ method: "HEAD",
619
619
  headers: { "User-Agent": "Mozilla/5.0 (compatible; Googlebot/2.1)", Accept: "text/html" },
620
620
  redirect: "manual",
621
621
  });
@@ -430,10 +430,16 @@ function formatComparison(
430
430
  }
431
431
 
432
432
  function daysAgo(n: number): string {
433
- // GSC dates are in PT (Pacific Time). Use UTC-8 as a stable approximation.
434
- const now = new Date(Date.now() - 8 * 60 * 60 * 1000);
435
- now.setDate(now.getDate() - n);
436
- return now.toISOString().split("T")[0];
433
+ // GSC dates are in Pacific Time. Use Intl to handle DST correctly.
434
+ const d = new Date();
435
+ d.setDate(d.getDate() - n);
436
+ const parts = new Intl.DateTimeFormat("en-CA", {
437
+ timeZone: "America/Los_Angeles",
438
+ year: "numeric",
439
+ month: "2-digit",
440
+ day: "2-digit",
441
+ }).format(d);
442
+ return parts; // en-CA formats as YYYY-MM-DD
437
443
  }
438
444
 
439
445
  // ── Tool registration ──
@@ -813,10 +813,10 @@ export function registerSpeedTool(server: McpServer): void {
813
813
  const cruxOrigin = origin;
814
814
 
815
815
  if (!cruxUrl && !cruxOrigin) {
816
- return { content: [{ type: "text" as const, text: "Error: provide either url or origin, not both." }] };
816
+ return { content: [{ type: "text" as const, text: "Error: provide url or origin for CrUX data." }] };
817
817
  }
818
818
  if (cruxUrl && cruxOrigin) {
819
- return { content: [{ type: "text" as const, text: "Error: provide either url or origin, not both." }] };
819
+ return { content: [{ type: "text" as const, text: "Error: provide url or origin, not both." }] };
820
820
  }
821
821
 
822
822
  try {
@@ -860,10 +860,10 @@ export function registerSpeedTool(server: McpServer): void {
860
860
  const histOrigin = origin;
861
861
 
862
862
  if (!histUrl && !histOrigin) {
863
- return { content: [{ type: "text" as const, text: "Error: provide either url or origin, not both." }] };
863
+ return { content: [{ type: "text" as const, text: "Error: provide url or origin for CrUX history." }] };
864
864
  }
865
865
  if (histUrl && histOrigin) {
866
- return { content: [{ type: "text" as const, text: "Error: provide either url or origin, not both." }] };
866
+ return { content: [{ type: "text" as const, text: "Error: provide url or origin, not both." }] };
867
867
  }
868
868
 
869
869
  try {