lumen · open-source seo toolkit

See your site clearly.

lumen crawls your site from your own machine, asks the data sources that actually let you ask, and labels every number with where it came from. Pluggable providers, MCP-first, Apache-2.0. It runs on free services — yours and ours.

$ npm install -g @lumen-seo/cli$ lumen audit https://example.com --max-pages 5042 pages crawled · 6 issues (2 error, 4 warning) · exit code 1

01 · why

The checks you need, none of the index you can’t have

What ships: a bounded, polite technical audit of your own site (18 rules — meta, content, links, performance basics), page reports from PageSpeed Insights and CrUX when you bring keys, keyword ideas from sources that exist without a paid account, best-effort rank checks, and authority proxies from Open PageRank and Tranco.

What deliberately doesn’t: there is no free search-volume database, no free backlink graph, and no free clickstream panel — so lumen doesn’t fabricate one. A number with no source isn’t a metric, it’s a costume. When the honest answer is “no free source exists for this”, that is the answer you get.

Two lists: what lumen can tell you from free sources, and what it refuses to invent.SHIPSNOT FAKINGlocal site audit — 18 rules, robots-safepage reports — PSI + CrUX (BYOK)keyword ideas — suggest + demand proxiesrank checks — best-effort, labeledauthority — Open PageRank + Trancosearch-volume databasebacklink indextraffic / clickstream estimatesno free source → no number.
Fig. 01 | Everything lumen can tell you without a paid index — and the things it declines to invent.

02 · pluggable

Every data source is a plugin

All external data flows through one provider interface with five boundaries — keywords, serp, pagespeed, crux, authority. The built-ins cover seven sources; you select and swap them in lumen.config.json, and bring-your-own-key providers stay dormant until their environment variable (LUMEN_PSI_KEY, LUMEN_CRUX_KEY, LUMEN_OPR_KEY) shows up. No key, no call, no crash — the capability is reported as not configured and everything else keeps working.

An unknown provider name in your config is a config error that lists the valid options, not a stack trace at 2 a.m. And the config stores env-var names, never values — keys are read at call time and never written down.

Seven provider chips feeding one registry, which feeds one engine surface.google-suggestwikipedia-demandpagespeed · BYOKcrux · BYOKopenpagerank · BYOKtranco · ddg-serpproviderregistryone engine,one surface:cli · mcp · thin worker
Fig. 02 | Every external number enters through the provider interface. Swap a source, keep the commands.

03 · agents first

Built for agents, usable by humans

Every capability is an MCP tool first — lumen_audit_site, lumen_page_report, lumen_keyword_ideas, lumen_rank_check, lumen_authority — and each is also a CLI command, because terminals are still a UI. Tools take an optional response_format (concise by default) so an agent gets high-signal output without drowning in JSON.

Locally you run the stdio server with one command; remotely, a thin gateway can serve the API-shaped subset from a free Cloudflare Worker. The audit itself always runs where the CPU is free — your machine — and the gateway says so instead of pretending to crawl.

One engine with three doors: CLI terminal, local stdio MCP for agents, and a thin remote gateway.one enginecrawl · judge · labelcli — humanlumen audit · keywords · rankstdio mcp — agentlumen_audit_site + 4 moreworker gatewaythin subset; crawl stays localexit codes for CI0 ok · 1 threshold · 2 configprovenance labelssource on every value--json everywherepipe straight into anything
Fig. 03 | One engine, three doors: the CLI, a stdio MCP server for agents, and a thin remote gateway that never pretends to crawl.

04 · data honesty

If we don’t know, we say so

Every metric lumen emits is wrapped with its provenance — which provider produced it, what kind of source it is (official, community, heuristic, lab, or field), and when it was retrieved. Autocomplete-derived ideas are labeled as autocomplete-derived ideas. A heuristic difficulty score is a heuristic, not a Google number wearing a lab coat.

Missing data is reported as missing — never zero-filled. An interrupted audit is marked incomplete: true rather than quietly shipping half a report as the whole truth. Boring? Maybe. But you can put these numbers in a doc without an asterisk the size of your conscience.

Three example metric cards: one field source, one gray source, and one honest no-source gap.2.4 sLCP p75source: crux · field12keyword ideassource: google-suggest · graysearch volumeno free source → not shownevery value carries { value, source: { provider, kind }, retrievedAt }
Fig. 04 | Every number carries its source. No source, no number — that’s the whole policy.

next

Go somewhere useful

Start with the quickstart, wire your agent up via MCP onboarding, or read the 18 audit rules before you let a robot near your sitemap.