# The seo package wants a sign-in before it will talk about traffic, but not before it will crawl

> A single globally installed command that audits a site, ranks the findings by affected URLs and visibility, and can be pointed at your own Search Console property instead of a screenshot. The crawl half works with no account at all, and the file is unusually explicit about the difference between what it observed and what it inferred.

**iannuttall/seo** — The only SEO skill your agent needs. 70+ SEO audit tools through a local CLI and MCP server, using your own crawl, Search Console, and GA4 data.

- Repository: https://github.com/iannuttall/seo
- Website: https://seoskill.dev
- Stars: 545 · Forks: 45
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/iannuttall-seo

## One global install, one command, one walkthrough

The quick start is three lines and a stated requirement of Node 22 or newer:

```sh
npm i -g seo
seo start
seo report
```

The middle of those three is the interesting one. Setup walks through Google sign-in, the Search Console property you want to work on, an optional traffic analytics connection, and a local project profile that ties those together. Nothing else in the tool works without that profile being configured, which is a deliberate design choice: the tool's whole argument is that it uses your data rather than a shared sample.

Once setup has run, the file makes the scope of the install explicit. The command is then available in every terminal, script, continuous integration job, and local MCP client on the machine, because the install is global rather than per project. The package itself declares the runtime floor more precisely than the prose does, asking for Node 22.19.0 or newer, and it ships a single binary named after the tool.

## A shared Google app, with your own client as the fallback

Signing in to Google requires an OAuth client, and this is the part of the setup that usually goes wrong for people building tools like this one. The answer here is a shared application: public releases can include a shared Google app, and if that app is not available in the build you installed, setup walks you through adding your own desktop OAuth client instead.

That fallback is not an afterthought in the repository. The package manifest carries a dedicated script whose whole job is to inject that shared client into a build, and the root directory also holds a script folder that houses it. In other words, the two paths are both first-class: a user on an official release gets the shared app without configuring anything, and a user on a fork, an internal build or an air-gapped install gets pointed at their own desktop client rather than being left with a sign-in that cannot succeed.

For anyone evaluating the tool, that is the piece to test first. A sign-in flow that depends on an application you do not control is a dependency worth knowing about before the first report, not after.

## The crawl half needs no account at all

The most useful line in the file is the one that says you can start before connecting anything. Passing a URL runs a local technical report: it crawls the site, returns findings and an action list, and saves the technical evidence, with no sign-in involved.

What it deliberately does not do is pretend. The file is explicit that this mode does not know your traffic, your queries or your rankings until you add a Search Console property, and it separates that silence from a zero rather than filling it. A site with no impressions and a site the tool cannot see are different states, and only one of them is a finding.

The command set for that unauthenticated mode is short and specific: a crawl of a site, a single page audit pointed at one URL, and the URL form of the main report. Every one of them produces technical evidence. None of them produces demand evidence, and the file never implies otherwise, which is the behaviour that makes the rest of the tool worth trusting.

## Three research providers ship inside the main package

Three paid data providers are built into the main package rather than offered as separate integrations: DataForSEO, Semrush, and Ahrefs. All three are optional, and the instruction attached to them is a constraint rather than an invitation. Connect one only when external result, keyword, domain, competitor, or link evidence would change the decision.

That framing does real work. A tool that can reach three commercial providers can also quietly become a tool that always reaches them, producing recommendations shaped by an estimate rather than by the property in front of it. Setting the bar at evidence that would change a decision keeps the provider connection tied to a specific question rather than to the default path.

The packaging matches the model. The module ships a separate public export for a provider interface, so building against the provider layer does not require importing the whole command surface, and the file list in the manifest ships the built output plus a skills folder and an evals folder, which are the two directories you would need to inspect the packaged behaviour rather than the source.

## Four rules about what a report is allowed to claim

The section that matters most for judging output is the one about how the reports stay honest, and it states four rules rather than making a general claim about quality.

First, observed evidence stays separate from derived findings and recommended actions, so you can always trace a claim back to the crawl row or the provider row underneath it. Second, partial data is never reported as a zero: a capped, filtered, or sampled source says so, and it cannot support a definitive all-clear. Third, heuristics are labelled as heuristics, so a convention or a threshold in the code is never presented as a rule a search engine actually applies. Fourth, every recommendation comes with a way to check that the fix worked, instead of a promise about rankings or traffic.

Taken together these are the rules that separate an audit tool from an assertion engine. The second and fourth are the ones that matter in practice. A report that turns a capped data source into a confident clean bill of health is worse than no report, and a recommendation without a verification step just relocates the guessing to after the work is done.

## Ranking the work instead of listing every warning

Most audit output is a flat list, and the file is explicit that this one is not. Findings are ranked using affected URLs, rule severity, search visibility, and analytics value, rather than treating every warning as equally worth fixing. Each finding is meant to answer four questions: why it matters, what evidence supports it, how to fix it, and how to verify the change.

The everyday command set follows from that ranking. Alongside the main report, there are commands for refreshing priorities, for quick wins, for second page results, and for watching technical regressions, which is what a site owner would run on a schedule rather than once:

```sh
seo report
seo refresh-priorities
seo quick-wins
seo second-page
seo technical-watch
```

A saved project profile is what makes those commands argument-free, holding a site, a Search Console property, a traffic analytics connection and your brand terms in one place. Where that does not fit, the file offers the profile-free equivalents, including the two-site identifier form used when you want a property without a profile:

```sh
seo report --site sc-domain:example.com
seo report --url https://example.com
seo crawl https://example.com
seo audit-page --url https://example.com/pricing
```

There is also a path for data you already exported yourself. Pointing the report at a folder or CSV of a Search Console performance download reads the query and page tables, keeps them separate from the crawl, and names which exported pages the crawl could not reach. Large inventories page, and the file tells you to keep passing an inventory page number until the next page field comes back null.

## Packaged for an agent as much as for a terminal

The package is not only a command. Alongside the binary, the manifest publishes two extra entry points, one for the MCP server and one for the provider interface, and the file describes the output contract as deterministic JSON, Markdown, stable rule identifiers, and a compact local MCP surface. Stable rule identifiers are the part that matters most for an agent: a rule that renames itself between runs is a rule you cannot track.

The repository is arranged for the same audience. A plugin folder for a coding assistant, a dedicated agent notes file, a second assistant-specific file, a fixtures folder for test data, and an evals folder all sit at the root, and the manifest ships both the skills folder and the evals folder inside the published tarball. You are meant to be able to read what the tool considers correct behaviour rather than inferring it from a report.

The remaining root files are the ordinary commercial ones, and they are shipped rather than linked: privacy, security, terms, and a trademarks file. Publishing them inside the package means the copy of the licence and the terms you agreed to travel with the version you installed, which is a small thing that removes an entire class of ambiguity later.

## Conclusion

The design decision worth taking from this one is the split between a report that has evidence behind it and a report that has none. Crawling a site needs no account and produces useful findings immediately, while anything about queries, traffic or rankings waits until you connect first-party data, and the tool says which of the two you are looking at rather than quietly filling the gap. If you work on sites you own, that distinction is the whole argument for using it: you get numbers that came from your property, a ranking that accounts for how many URLs a rule touches, and an exit hatch to research providers only when your own data cannot settle a decision.

## FAQ

### Do I need a Google account to run an audit with the seo command?

No for the technical half. Pointing the report at a URL crawls the site and returns findings and an action list with no sign-in at all. Connecting a Search Console property through the setup command is what adds traffic, query and ranking data, and the tool is explicit that it does not guess at those before you connect one.

### What do I need to install the seo CLI?

Node 22 or newer, then a global install of the package. The setup command then walks through Google sign-in, choosing a Search Console property, an optional traffic analytics connection, and a local project profile. The package manifest asks for Node 22.19.0 or newer as the runtime floor.

### Which keyword and competitor data providers does seo support?

DataForSEO, Semrush, and Ahrefs, all optional and all built into the main package rather than added separately. The guidance is to connect one only when external result, keyword, domain, competitor, or link evidence would change the decision you are making.

### What is an MCP server in the context of the seo package?

It is the second entry point the package publishes, alongside the command itself and a provider interface module. The design goal is a compact local surface that gives an assistant deterministic JSON or Markdown output with stable rule identifiers, so the same crawl, console and research evidence is available to an agent as to a person at a terminal.

### How does the seo command rank which fixes to make first?

Findings are ordered using affected URLs, rule severity, search visibility, and analytics value rather than treating every warning equally, and each finding carries why it matters, what evidence supports it, how to fix it, and how to verify the change. Saved crawl reports can be compared without repeating the crawl.

### Can I use the seo command with data I already exported from Search Console?

Yes. Pointing the report at a folder or CSV containing a Search Console performance download makes it read the query and page tables, keep them separate from the crawl, and name which exported pages the crawl could not reach. Large page inventories come back in stable pages, so you keep passing an inventory page number until the next page field is null.

## Sources

- [iannuttall/seo on GitHub](https://github.com/iannuttall/seo)
- [Issues](https://github.com/iannuttall/seo/issues)
- [License: Apache-2.0](https://github.com/iannuttall/seo/blob/main/LICENSE)
- [Project website](https://seoskill.dev)
- [README](https://github.com/iannuttall/seo/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/iannuttall-seo
