npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jordanmgsoftware/seo-cli

v0.4.7

Published

SOTA SEO CLI for MG Software projects: crawl, audit, link graph, anti-spam (March 2026), GEO/AEO, programmatic SEO checks, plus dedicated security & privacy audits (HTTP headers, TLS, cookies, dependency vulns, takeover, GDPR/PII). Runs on production URLs

Readme

@jordanmgsoftware/seo-cli

SOTA SEO CLI by MG Software — runs on production URLs, localhost, or build output.

# add to a Next.js project
bun add -d @jordanmgsoftware/seo-cli
bun run mg-seo init
bun run seo-audit

# global / direct usage
bun add -g @jordanmgsoftware/seo-cli   # or: npm i -g @jordanmgsoftware/seo-cli
mg-seo init                      # generate mg-seo.config.ts
mg-seo crawl https://example.com
mg-seo crawl http://localhost:3000
mg-seo audit https://example.com
mg-seo audit http://localhost:3000 --auto-start
seo-audit                        # defaults to http://localhost:3000 + auto-start
mg-seo links https://example.com
mg-seo security https://example.com  # dedicated security audit
mg-seo privacy https://example.com   # dedicated privacy audit
mg-seo report

Why

A pragmatic, code-first alternative to Screaming Frog / Sitebulb that:

  • Lives in your dev loop — works on localhost and on built static output (e.g. .next, dist/)
  • Integrates with CI — exit codes, JUnit XML, GitHub Action templates
  • 2026-current — checks for Google's March 2026 Spam Update, INP-as-primary-signal, GEO/AEO readiness aligned with Google's AI optimization guide (AI-bot access, snippet eligibility for AI Overviews / AI Mode; llms.txt as a non-scoring optional)
  • First-class programmatic SEO — MinHash-based template uniqueness detection (the exact thing Google now penalises)
  • Code-first configmg-seo.config.ts, type-safe, per project
  • Zero-config Next.js loop — add the package, run bun run seo-audit, and the CLI starts localhost when needed

Commands (v0.2)

| Command | Purpose | |---|---| | mg-seo init | Scaffold mg-seo.config.ts | | mg-seo crawl <source> | Spider crawl with sitemap + robots discovery | | mg-seo audit <source> | Run all rules (on-page, links, schema, content, technical) | | seo-audit [source] | Local-first audit that auto-starts a dev server when needed | | mg-seo links <source> | Internal link graph (orphans, broken, redirect chains, depth) | | mg-seo grade <file-or-url> | Grade ONE document (local HTML draft or URL) against the content / anti-spam / GEO / schema rules | | mg-seo security <source> | Dedicated security audit (HTTP headers, TLS, cookies, exposed paths, dependency vulns, takeover detection, project-mode static analysis) | | mg-seo privacy <source> | Dedicated privacy audit (trackers without consent, GA misconfig, plaintext PII, service-worker scope) | | mg-seo report | Aggregate latest results into a markdown / HTML report | | mg-seo-mcp | Local MCP server (stdio) exposing audit/keywords/content tools to AI agents |

MCP server (local)

mg-seo ships a local Model Context Protocol server so AI agents (Cursor, Claude) can run audits and build content briefs directly — no need to remember CLI flags. Because it runs locally it keeps the two capabilities a hosted endpoint never could: it can reach http://localhost:3000 and read your project source tree (project mode: source-mapping, deps-audit, supply-chain, vibe-check).

It speaks stdio (stdout is the JSON-RPC channel); all status output is routed to stderr so the protocol stays clean.

Tools

| Tool | What it does | |---|---| | seo_audit | Crawl + audit a URL, localhost, or project dir. scope, project, active, autoStart, maxPages, format (summary|json). Returns the LLM-optimised report or a structured payload. | | seo_keywords | Keyword opportunities (GSC striking-distance/CTR/cannibalisation, discovery, competitor gaps via enrichment JSON). | | content_brief | Deterministic blog/landing brief: keyword clusters → intent → new-vs-improve mapped against the crawled site. | | grade_content | Validate ONE document (url, local file, or raw html) against the content / anti-spam / GEO / schema rules — the validation half of the content loop. file grades a draft in place so the HTML never travels through agent context. | | seo_diff | Diff the latest cached audit vs. the previous snapshot (run seo_audit with writeFiles:true first). | | list_scopes | List the valid scope values for seo_audit. |

Cursor config

Add to .cursor/mcp.json (or your global Cursor MCP config).

With bunbunx resolves the default mg-seo bin and the mcp subcommand starts the server:

{
  "mcpServers": {
    "mg-seo": {
      "command": "bunx",
      "args": ["--bun", "@jordanmgsoftware/seo-cli", "mcp"]
    }
  }
}

With npm/npx — select the dedicated bin explicitly with -p (plain npx <pkg> mcp is ambiguous because the package ships multiple bins):

{
  "mcpServers": {
    "mg-seo": {
      "command": "npx",
      "args": ["-y", "-p", "@jordanmgsoftware/seo-cli", "mg-seo-mcp"]
    }
  }
}

Installed in the project (offline / pinned version) — point straight at the built file:

{
  "mcpServers": {
    "mg-seo": {
      "command": "node",
      "args": ["node_modules/@jordanmgsoftware/seo-cli/dist/mcp.mjs"]
    }
  }
}

Requires v0.4.0+ (v0.4.2+ for directory→domain inference in content_brief / seo_keywords). Use @latest in bunx/npx so you don't get a cached older release.

Content loop (recommended agent workflow)

mg-seo does not auto-write blog posts — the calling agent is the LLM. The loop is:

  1. content_brief → pick a topic cluster (intent, internal-link sources, competitor evidence).
  2. The agent writes the page.
  3. grade_content → validate the draft against the anti-spam (spam/entity-swap, spam/publication-velocity, keyword-stuffing) and GEO rules before publishing. Render the draft to disk and pass file (or run mg-seo grade <draft.html> in the terminal) so the HTML never has to travel through agent context.
  4. Fix what it flags, repeat step 3 until clean.

Rule categories

| Category | Coverage | |---|---| | on-page | titles, meta description, H1, headings, image alt/dimensions, canonical, hreflang, OG, viewport, lang, noindex, status codes, redirect chains | | links | orphans, broken internal links, click depth, generic anchor text | | schema | JSON-LD presence, valid JSON, required properties for Article/Product/FAQ/Organization/Breadcrumb/Recipe/Event/Person/JobPosting and more; video SEO (VideoObject absolute URLs, missing schema for playable videos, thumbnail/content crawlability + CORP, watch-page placement, url/page match) | | content | thin content (word count), text-to-HTML ratio, identical body content, title keyword cannibalisation (Jaccard ≥ 0.8) | | technical | robots.txt present, XML sitemap discoverable, sitemap coverage of crawled pages | | geo | Issue tier: AI-bot access (robots.txt blocking GPTBot/ClaudeBot/PerplexityBot — Google's AI Overviews are unaffected; they use Googlebot), snippet eligibility (geo/snippet-eligibility: nosnippet / max-snippet:0 / near-total data-nosnippet on an indexable page silently opts it out of AI Overviews / AI Mode, which require a page to be indexed and snippet-eligible), and CSR visibility (content invisible to non-JS AI crawlers). See Opportunities below for everything else GEO. | | security | Only emitted by mg-seo security. HTTP security headers (HSTS, CSP, COOP/COEP, CORP, Permissions-Policy, …), TLS hygiene (cert expiry, weak protocol/cipher, hostname mismatch, OCSP stapling), cookie flags (Secure, HttpOnly, SameSite), HTML/DOM leaks (inline event handlers, javascript: URIs, login over HTTP, sensitive query params, secret-shaped tokens, missing SRI, exposed sourcemaps), exposed paths (.git, .env, wp-admin, debug endpoints, GraphQL introspection), framework probes (WP / Next / Sanity / Strapi), subdomain takeover (DNS + body fingerprint), dependency vulns (npm audit / bun pm audit), project-mode static analysis (dangerouslySetInnerHTML without sanitiser, eval(), JWTs in localStorage, public env vars carrying private-key-shaped values, console.log(process.env…)). | | privacy | Only emitted by mg-seo privacy. Third-party trackers firing without consent, Google Analytics without IP anonymisation, browser-fingerprinting libraries, mailto: addresses with personal-shaped local parts, plaintext phone numbers in body content, service workers registered with broad scope and no version hash. |

Security audits — mg-seo security

mg-seo audit deliberately stays focused on SEO. The dedicated security command shares the same crawl pipeline + reporters but swaps in a separate rule registry and adds active probes that would be too aggressive for the SEO audit:

mg-seo security https://example.com
mg-seo security http://localhost:3000 --auto-start
mg-seo security ./                        # project-only audit (deps + static analysis)
mg-seo security https://example.com --project ./    # crawl + filesystem hygiene

Every security check uses the existing --fail-on, --html, --max-pages, --max-depth, --concurrency flags, plus the following security-specific opt-outs:

| Flag | Effect | |---|---| | --no-probe | Skip the active sensitive-paths probe (.git, .env, /wp-admin, …) | | --probe-paths <file> | Newline-separated file of extra paths to probe | | --no-deps-audit | Skip npm audit / bun pm audit + project-mode static analysis | | --no-takeover-check | Skip the subdomain takeover scan (DNS + body fingerprint) | | --no-tls-probe | Skip the TLS handshake probe | | --no-framework-probe | Skip framework-specific probes (WP/Next/Sanity/Strapi) | | --no-error-probe | Skip the verbose-404/500 detection (some servers log these as suspicious traffic) |

What it covers, in one breath:

  • HTTP headers — HSTS / CSP (with weak-source detection) / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy / COOP+COEP / CORP / Server-Timing leaks / Server + X-Powered-By fingerprints.
  • TLS hygiene — cert expiry, hostname mismatch, weak protocol (≤ TLSv1.1), weak cipher (RC4/3DES/MD5), OCSP stapling, single-handshake probe (no slow rescans).
  • CookiesSecure / HttpOnly / SameSite flags evaluated per Set-Cookie.
  • HTML/DOM — inline event handlers, javascript: URIs, password inputs on HTTP, <form action> to a different registrable domain, target="_blank" without rel="noopener", missing SRI on third-party <script>/<link>, <meta name="generator"> version leaks, secret-shaped tokens inside inline <script>.
  • Exposed paths.git/HEAD, .env*, backups, lockfiles, db.sql, default landing pages, directory listings, /.well-known/security.txt presence, debug/swagger/OpenAPI endpoints, GraphQL introspection.
  • Framework probes — WordPress /wp-json/wp/v2/users enumeration, Next.js _next/required-server-files.json, Sanity /studio and Strapi /admin exposure.
  • Subdomain takeover — resolves CNAME records on subdomains discovered during the crawl; only flags when the body matches a known platform fingerprint (S3/GitHub Pages/Heroku/Netlify/Vercel/Azure/CloudFront/Fastly/…).
  • CORS — wildcard origin + credentials true.
  • robots.txt / sitemap — Disallow lines that advertise admin/staging/backup paths, sitemap entries pointing at staging hostnames, cross-domain sitemap entries.
  • Project-mode dependency auditnpm audit (or bun pm audit) when a project directory is supplied, plus filesystem hygiene: .env files outside .gitignore, exposed sourcemaps in build output, .git folders inside public/, multiple co-existing lockfiles, secret-shaped strings in source.
  • Project-mode static analysisdangerouslySetInnerHTML without an obvious sanitiser nearby, eval() / new Function() / string-form setTimeout, JWT/auth tokens written to localStorage, public env vars (NEXT_PUBLIC_ / VITE_ / REACT_APP_ / PUBLIC_) carrying private-key-shaped values, console.log(process.env…).

Severity is calibrated for production reality:

| Severity | Examples | |---|---| | critical | TLS cert expired, hostname mismatch, password input on HTTP, .git reachable, dangling CNAME, .git folder shipping in public/ | | error | Missing HSTS, weak TLS, mixed content, exposed API debug endpoints, .env not gitignored, public env carrying private-key-shaped value | | warning | Most header gaps, weak CSP, weak cipher, sensitive query params, exposed sensitive paths, vulnerable dep (high), framework-specific probes | | info | Missing security.txt, missing CORP, missing OCSP stapling, generator meta leak, sourcemap shipped, low-severity vuln |

Privacy audits — mg-seo privacy

mg-seo privacy https://example.com
mg-seo privacy http://localhost:3000 --auto-start

privacy is intentionally narrow: it focuses on the privacy-adjacent checks that don't belong in either the SEO audit or the security audit.

| Rule | What it flags | |---|---| | privacy/tracker-without-consent | Trackers (GA4 / GTM / Meta Pixel / Hotjar / Clarity / UA) detected on a page with no CMP loader (Cookiebot / OneTrust / Iubenda / Usercentrics / …) | | privacy/ga-without-ip-anonymization | Google Analytics loaded without anonymize_ip / anonymizeIp configuration | | privacy/fingerprinting-script | FingerprintJS / ClientJS / fp.js style libraries — flagged as personal-data processing under GDPR | | privacy/plaintext-pii-mailto | mailto: links with admin-shaped local parts (admin@, root@, webmaster@) or personal-shaped local parts (first.last@) | | privacy/plaintext-phone-number | Phone-shaped strings in main flow text (≥9 digits) | | privacy/sw-broad-scope | navigator.serviceWorker.register('/sw.js') with root scope and no version hash |

Privacy-specific flags:

| Flag | Effect | |---|---| | --consent-cookie <name> | Re-crawl with the named cookie set so the audit sees the post-consent tracker surface (currently only emits a warning — consent re-crawl will land in v0.5) |

Opportunities (a.k.a. suggestions)

Beyond "this is broken, fix it" findings (issues), mg-seo audit also runs a separate suggestion engine that surfaces "this would lift you, consider it" opportunities. Suggestions never trigger --fail-on and heuristic suggestions never affect the audit score; only rules explicitly marked confidence: "deterministic" (provable-from-crawl signals like a missing author entity) feed a small, capped opportunity penalty. Anything Google Search says it ignores (llms.txt, markdown mirrors) or calls a myth (schema density for AI) is deliberately heuristic, so it can never dock points.

The split exists for two reasons:

  1. Pure scoring — opportunities don't pollute info penalties (capped at -3); they're surfaced in their own report section.
  2. CI-friendly--fail-on warning won't go red because someone hasn't added a FAQ section yet.

| Impact | When to act | |---|---| | high | Big, well-evidenced lifts (knowledge panel eligibility, missing author entities, breadcrumb/recipe rich results). | | medium | Worth doing during the next content sprint. | | low | Polish: do it on pages you're touching anyway. |

Suggestion rules

| Rule | Category | What it flags | |---|---|---| | geo/missing-breadcrumb-schema | geo | Deep pages (depth ≥ 2) with no BreadcrumbList JSON-LD | | geo/add-jsonld-faqpage-on-services | geo | Service/product pages with an FAQ section / accordion but no FAQPage JSON-LD. Low impact — Google retired the FAQ rich result (May 7, 2026); only helps AI Overviews/LLMs now | | geo/faq-schema-suggestion | geo | Any page with ≥3 question headings or a collapsible Q&A section and no FAQPage JSON-LD. Low impact, advisory — AI Overviews only, no Search rich result | | geo/faqpage-rich-result-deprecated | geo | Page already has FAQPage/QAPage markup: FYI that the Search rich result is gone for all sites (May 7, 2026). Keep the JSON-LD for AI engines; reconsider the FAQ format | | geo/add-faq-content | geo | Commercial/purchase-intent pages (services, products, pricing, quote) with no visible Q&A yet. Proactively nudges adding a visible FAQ section for organic long-tail + AI Overviews. Content, not schema — explicitly recommends against FAQPage JSON-LD (Google retired the rich result and bars FAQPage "for advertising purposes") | | geo/howto-schema-suggestion | geo | Tutorial-titled pages with ordered lists but no HowTo schema (AI engines only — Google retired the HowTo rich result in September 2023) | | geo/llms-txt-presence | geo | Missing or incomplete /llms.txt. Non-scoring — Google Search ignores llms.txt (official guidance: it neither helps nor hurts); only relevant for non-Google AI tools | | geo/markdown-mirrors | geo | No markdown mirrors for AI crawlers (per-page .md via <link rel="alternate" type="text/markdown"> or /llms-full.txt). Non-scoring — Google Search ignores these files; targets non-Google AI crawlers only. Per-page .md is the foundation; medium on content-heavy sites, low on small marketing sites where /llms-full.txt is usually overkill | | geo/organization-completeness | geo | Missing or partial Organization schema on the homepage | | geo/author-byline | geo | Article schema without E-E-A-T-grade author identifiers | | geo/freshness | geo | Articles older than 365 days or missing dateModified | | geo/schema-graph-density | geo | Pages exposing fewer than 4 distinct schema types. Non-scoring — per Google's AI optimization guide, structured data isn't required for generative AI features; framed as optional rich-result/context polish | | geo/passage-extractability, geo/answer-first-paragraph, geo/question-headings, geo/paragraph-length, geo/citation-readiness, geo/entity-density, geo/llm-readiness | geo | Long-form content structured for LLM passage extraction (readability + non-Google answer engines; Google itself needs no AI-specific writing style or "chunking"). geo/llm-readiness is a rollup and supersedes its component rules on pages where it fires. | | content/missing-table-of-contents | content | Long-form pages (≥1500 words, ≥6 H2s) without anchor jump-links | | content/expand-thin-section | content | 1-2 thin (<400 words) outliers in an otherwise-substantial section | | content/page-type-depth | content | Pages thin for their page type — below the per-type word bar (commercial 500, product 400, location 300, blog 800; tunable via thresholds.contentDepth) or structurally shallow (few H2s / FAQ / lists / internal links / entities) in the borderline band. Bilingual EN/NL by page language; skips utility / contact / login / checkout / watch pages | | links/under-linked-pages | links | Indexable pages with fewer than 3 inbound internal links | | links/missing-related-content-cluster | links | Topically-related pages (title Jaccard ≥ 0.4) that don't link to each other | | links/missing-link-from-parent | links | Child page exists but its parent hub doesn't link to it (and the parent already links to siblings) | | links/asymmetric-cluster | links | Topically-related page links here but this page doesn't link back | | links/excessive-internal-links | links | Page with too many contextual internal links, or a wall of near-duplicate query-string variants (over-optimization) | | on-page/missing-og-image | on-page | Page declares OG tags but no og:image | | on-page/missing-twitter-card | on-page | Page declares OG tags but no twitter:* meta | | schema/upgrade-article-to-newsarticle | schema | Generic Article on /blog/* or /news/* could be BlogPosting / NewsArticle | | schema/video-decorative-only | schema | Page has only decorative background clips (muted/autoplay/loop, no controls) and no VideoObject, intentionally not video-indexable, so a "Video isn't on a watch page" report is expected (not a defect) | | schema/video-promotional-no-schema | schema | Page has a playable <video controls> on a content-heavy page (homepage infomercial, product teaser) with no VideoObject: correct when you do not want video search results; GSC "not on a watch page" is expected | | fs/add-generate-metadata | fs | Static metadata export on a route that has a dynamic child ([slug]) — switch to generateMetadata | | fs/jsonld-component-suggestion | fs | /blog/* or /news/* route file renders no JSON-LD component | | fs/not-found-metadata | fs | not-found.tsx exists but inherits the parent layout's title |

Measurement & Compliance (analytics/*)

These aren't SEO findings — a page can rank fine without Meta Pixel — so the reporters surface them in a dedicated Measurement & Compliance panel (separate <section> in HTML, separate ## Measurement & Compliance block in Markdown, separate cyan-headed table in the console). Same engine, same --suggestions-only flag, same snapshot diff, same ROI sort.

| Rule | Category | What it flags | |---|---|---| | analytics/missing-search-console-verification | analytics | Homepage has no <meta name="google-site-verification"> tag | | analytics/no-analytics-detected | analytics | No GA4 / GTM / Plausible / Fathom / Umami / Matomo / Hotjar / Clarity anywhere on the site (informational) | | analytics/duplicate-gtm-containers | analytics | A page loads more than one distinct GTM-XXXX container (double-firing pageviews) | | analytics/analytics-without-consent | analytics | GA4 / Meta Pixel / Clarity / Hotjar / UA / GTM is present anywhere AND no CMP (Cookiebot, OneTrust, Usercentrics, Iubenda, Complianz, Termly, Didomi, Axeptio) was detected — GDPR / ePrivacy risk | | analytics/missing-cookie-banner | analytics | No CMP detected anywhere (only fires when analytics-without-consent doesn't, so they never duplicate) |

Disable the whole category with disabledCategories: ["analytics"] if you treat measurement/compliance out-of-band.

ROI sort & deduplication

  • Every suggestion carries both an impact (high/medium/low) and an effort (small/medium/large). The reporters sort by ROI = impact / effort so copy-paste schema fixes float above content rewrites of the same expected lift.
  • Rules can declare supersedes: [...] — when a narrower rule (e.g. geo/add-jsonld-faqpage-on-services) fires on a page, it hides the broader rule (geo/faq-schema-suggestion) so the TODO list never carries duplicates.
  • The Markdown report and console output include a Top pages to act on panel that ranks URLs by (count × impact) — opening one of these is an N-for-1 fix.

Snapshot / diff for opportunities

Audit snapshots persist suggestionKeys + byImpact alongside the existing issueKeys + bySeverity. The diff section now shows resolved / introduced opportunities and per-impact-tier deltas, so the Opportunities layer becomes a progress tracker, not just a static report.

Configuration

// mg-seo.config.ts
export default defineConfig({
  suggestions: {
    enabled: true,           // turn the whole layer off
    minImpact: "low",        // "low" | "medium" | "high"
    maxPerCategory: 20,      // cap per category after ROI-sort
  },
  // Per-rule disable works the same as issue rules; effort overrides too:
  rules: {
    "geo/llms-txt-presence": { enabled: false },
    "links/under-linked-pages": { options: { impact: "high", effort: "medium" } },
  },
});

CLI flags & commands

| Flag / command | Effect | |---|---| | --no-suggestions | Skip the suggestion engine entirely. Only score-affecting rules run. | | --suggestions-only | Run only the suggestion engine on mg-seo audit. Score is reported as 100. | | mg-seo opportunities <source> | First-class entry point for the suggestions-only audit. Prints a condensed table and writes opportunities-latest.md for sharing with the content team. |

<source> accepts:

  • A URL (https://example.com) — production crawl
  • A localhost URL (http://localhost:3000) — dev server crawl
  • A directory (./dist, ./out, ./.next/server/pages) — static output crawl (roadmap)

Development

:: One-shot run from source (no build needed)
dev.cmd audit https://example.com --max-pages 20
dev.cmd links http://localhost:3000

:: Full toolchain
bun install
bun run dev -- audit https://example.com
bun run seo-audit
bun run build
bun run typecheck

Roadmap

  • ✅ v0.1 — init, crawl, audit, links, report (on-page + link rules)
  • ✅ v0.2 — schema, content, technical (robots/sitemap) rule categories
  • ✅ v0.3 — geo (AEO/AI Overview readiness), llms.txt, suggestions layer (deterministic opportunities for SEO/GEO growth)
  • ✅ v0.3.1 — Suggestions v2: snapshot/diff for opportunities, per-URL hotspot rollup, supersedes deduplication, ROI sort (impact / effort), fs/* opportunity rules, dedicated mg-seo opportunities command
  • ✅ v0.3.2 — mg-seo security (HTTP headers, TLS, cookies, exposed paths, framework probes, subdomain takeover, dependency vulns, project-mode static analysis) and mg-seo privacy (consent gating, GA misconfig, plaintext PII, service-worker scope) commands
  • ✅ v0.3.3: Video SEO. VideoObject absolute-URL / missing-schema / thumbnail crawlability (reachability, latency, CORP) checks, plus watch-page detection: schema/video-not-watch-page (a VideoObject on a content-heavy page that Search Console reports as "Video isn't on a watch page") and schema/video-url-mismatch (a same-origin url/embedUrl pointing at a different page). Genuine watch pages (single video, minimal prose, under video.watchPageMaxWords) are exempt from thin-content and text-to-HTML rules, and purely decorative background clips get a non-scoring schema/video-decorative-only note
  • ✅ v0.3.4: schema/video-missing now only fires on watch-page candidates (not homepage promos). New non-scoring schema/video-promotional-no-schema documents that a playable clip on a content-heavy page without VideoObject is correct when you do not want video indexing. schema/video-not-watch-page hint updated: remove VideoObject OR move to a watch page
  • ✅ v0.4.3: schema/invalid-property — property-on-type validation for JSON-LD. Flags properties that aren't valid for their @type (e.g. inLanguage on LocalBusiness, which belongs on CreativeWork/WebPage/Event), matching SemRush's "property not recognized by Schema.org vocabulary" finding. Uses a curated, inheritance-aware allowlist and only checks modeled types — multi-type nodes are validated against the union of their types; unknown types, JSON-LD keywords (@…) and namespaced/IRI keys are skipped — to stay false-positive-safe. Aggregated site-wide like schema/required-properties
  • v0.4 — pseo + spam (March 2026 policies, MinHash near-dup detection), perf (Lighthouse-Node + PSI optional)
  • v0.5 — Playwright optional rendering, watch, diff, ci, HTML reporter, JUnit XML
  • v1.0 — Bun standalone binary, GitHub Action template

© MG Software B.V.