awwwards-mcp 1.0.0 → 1.2.0

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
@@ -4,6 +4,8 @@ Free, open-source MCP server that gives AI agents design inspiration from
4
4
  [Awwwards](https://www.awwwards.com/) — the Mobbin-style visual reference loop,
5
5
  sourced from the web's best award-winning websites.
6
6
 
7
+ <a href="https://github.com/INSANE0777/Awwwards-mcp"><img src="assets/demo.gif" alt="awwwards-mcp in action: search results, design DNA, motion filmstrip, band map — real tool output" width="480"></a>
8
+
7
9
  Your agent searches in natural language ("dark 3D portfolio sites", "soft pastel
8
10
  e-commerce"), sees **real screenshots inline**, and can pull the **design DNA**
9
11
  of any site: color palette, tech stack, design elements, award history.
@@ -22,13 +24,65 @@ of any site: color palette, tech stack, design elements, award history.
22
24
 
23
25
  ## Setup
24
26
 
27
+ Any MCP-compatible coding agent can use awwwards-mcp — no API key, no account.
28
+ Requires Node ≥ 22.13 (`node -v` to check). Pick your agent:
29
+
30
+ **Updates**: the server checks the npm registry once a day and prints an
31
+ stderr notice when a newer `awwwards-mcp` exists (stdout stays clean for the
32
+ JSON-RPC channel — your agent sees the notice as a log line). Set
33
+ `AWWWARDS_AUTO_UPDATE=1` in the server's `env` to opt into background
34
+ self-update; restart your agent afterwards to load it. Nothing is fetched
35
+ more than once a day and serving never waits on the check.
36
+
25
37
  **Claude Code**
26
38
 
27
39
  ```bash
28
40
  claude mcp add awwwards -- npx -y awwwards-mcp
29
41
  ```
30
42
 
31
- **Claude Desktop / Cursor / Windsurf** (`mcpServers` in the config):
43
+ **Codex CLI** (ChatGPT desktop app and the IDE extension share this config)
44
+
45
+ ```bash
46
+ codex mcp add awwwards -- npx -y awwwards-mcp
47
+ ```
48
+
49
+ or in `~/.codex/config.toml` (project-scoped: `.codex/config.toml`):
50
+
51
+ ```toml
52
+ [mcp_servers.awwwards]
53
+ command = "npx"
54
+ args = ["-y", "awwwards-mcp"]
55
+ ```
56
+
57
+ **OpenCode** (`opencode.json` — note the command is an array)
58
+
59
+ ```json
60
+ {
61
+ "$schema": "https://opencode.ai/config.json",
62
+ "mcp": {
63
+ "awwwards": {
64
+ "type": "local",
65
+ "command": ["npx", "-y", "awwwards-mcp"]
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ **ZCode** (`~/.zcode/cli/config.json` — note servers nest under `"mcp": { "servers": ... }`)
72
+
73
+ ```json
74
+ {
75
+ "mcp": {
76
+ "servers": {
77
+ "awwwards": { "command": "npx", "args": ["-y", "awwwards-mcp"], "env": {} }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ **Claude Desktop / Cursor / Windsurf / Gemini CLI / Cline / Continue** — anything
84
+ reading the common `mcpServers` JSON shape (e.g. `~/.claude/claude_desktop_config.json`
85
+ or `~/.gemini/settings.json`):
32
86
 
33
87
  ```json
34
88
  {
@@ -38,25 +92,58 @@ claude mcp add awwwards -- npx -y awwwards-mcp
38
92
  }
39
93
  ```
40
94
 
41
- Optional full-page captures:
95
+ **Anything else** — awwwards-mcp is a plain stdio MCP server: point your client
96
+ at `npx -y awwwards-mcp` and it works. To pin a version, use
97
+ `npx -y awwwards-mcp@1.0.0`.
98
+
99
+ **pi coding agent** has no built-in MCP by design — it uses skills and
100
+ extensions instead. Two options:
101
+
102
+ 1. Install the awwwards-inspiration skill (below). pi reads skills from
103
+ `~/.pi/agent/skills/` or `~/.agents/skills/` (the latter is shared across
104
+ agents following the Agent Skills standard). The skill teaches the workflow;
105
+ for it to reach the live data, add an MCP-supporting pi extension, or run
106
+ the queries in another agent and paste results.
107
+ 2. Skip MCP entirely: ask pi to build you a small CLI wrapper around
108
+ awwwards.com, or use a shared skills directory (`~/.agents/skills/`) so the
109
+ same skill file serves pi and every other agent.
110
+
111
+ Optional full-page captures (needed by `capture_live_site`,
112
+ `analyze_page_structure`, `record_site_motion`):
42
113
 
43
114
  ```bash
44
115
  npm install -g playwright && npx playwright install chromium
45
116
  ```
46
117
 
118
+ `record_site_motion` additionally uses ffmpeg; it resolves the `ffmpeg-static`
119
+ package automatically if present.
120
+
47
121
  ## Skills
48
122
 
49
- This package ships an agent skill that teaches the inspiration workflow —
50
- search, judge from screenshots, pull design DNA, state a design direction —
51
- using the awwwards MCP tools. Copy it into your agent's skills directory:
123
+ This package ships three agent skills. Any agent that follows the
124
+ [Agent Skills standard](https://agentskills.io) can load them; copy them into
125
+ your agent's skills directory:
52
126
 
53
127
  ```bash
54
128
  npm install awwwards-mcp
55
- mkdir -p ~/.claude/skills && cp -r node_modules/awwwards-mcp/skills/awwwards-inspiration ~/.claude/skills/
129
+ mkdir -p ~/.agents/skills && cp -r node_modules/awwwards-mcp/skills/awwwards-inspiration node_modules/awwwards-mcp/skills/awwwards-doctor node_modules/awwwards-mcp/skills/awwwards-motion-study ~/.agents/skills/
56
130
  ```
57
131
 
58
- For ZCode, copy to `~/.zcode/skills/` instead of `~/.claude/skills/`.
59
- Windows: run this from Git Bash, or copy `node_modules\awwwards-mcp\skills\awwwards-inspiration` manually.
132
+ | Skill | What it teaches |
133
+ |-------|-----------------|
134
+ | `awwwards-inspiration` | The inspiration loop: search, judge from screenshots, pull design DNA, state a design direction, capture/motion-first builds. |
135
+ | `awwwards-motion-study` | The full video chain: what to record from a live site (and what to skip), frame-by-frame review (video input or tile-per-element), the motion inventory, and build verification by re-recording. |
136
+ | `awwwards-doctor` | Repair: run `npm run doctor`, apply its fixes, re-anchor parsers after real awwwards.com drift, recover the in-flight task that surfaced the failure. |
137
+
138
+ | Agent | Skills directory |
139
+ |-------|------------------|
140
+ | Claude Code | `~/.claude/skills/` |
141
+ | pi | `~/.pi/agent/skills/` (also reads `~/.agents/skills/`) |
142
+ | ZCode | `~/.zcode/skills/` |
143
+ | Agent Skills-standard agents | `~/.agents/skills/` |
144
+
145
+ Windows: run this from Git Bash, or copy
146
+ `node_modules\awwwards-mcp\skills\awwwards-inspiration` manually.
60
147
 
61
148
  ## Indexing (recommended)
62
149
 
@@ -94,6 +181,162 @@ and content remain the property of Awwwards and the credited creators — don't
94
181
  bulk-scrape, redistribute, or republish them. If you use this commercially,
95
182
  review awwwards.com's terms yourself.
96
183
 
184
+ ## Built with awwwards-mcp: three real sites
185
+
186
+ Three complete sites were built through the full inspiration loop this MCP
187
+ enables, using nothing but the server's tools plus the shipped
188
+ `awwwards-inspiration` skill. Each one exercised a different corner of the
189
+ loop — and every correction the loop caught on the way became doctrine in the
190
+ skill.
191
+
192
+ > **Built in one shot, by a model that can't watch video.** All three sites
193
+ > were built in a single prompt run on **GLM 5.3-flash** — which does not
194
+ > support video input. The loop's motion study worked entirely from
195
+ > frame-tiled filmstrips (ffmpeg, 1–2 fps per element) instead of watching
196
+ > the recordings. With a video-native model, those same `get_site_elements`
197
+ > videos and `record_site_motion` .webm files could be watched directly —
198
+ > timing, easing and overlap read at full fidelity — and the motion-true
199
+ > results would be better still. The skill's frame-tile doctrine is what
200
+ > closes that gap today.
201
+
202
+ **1. [Fallow Press](fallow-press/index.html)**
203
+ ([source](fallow-press/)) — a flat-2D editorial journal, direction
204
+ **Emergence Magazine** (SOTD): pink `#FF9398` on cream and black, torn-paper
205
+ masthead (pure CSS `clip-path`, zero WebGL), giant grotesque display over
206
+ grayscale photography, serif-italic brand, three pages with **separate
207
+ horizontal** projects/about pages (GSAP ScrollTrigger pin +
208
+ `containerAnimation`).
209
+
210
+ | Torn-paper masthead (home) | Horizontal gallery (Fields) | Horizontal chapters (Practices) |
211
+ |---|---|---|
212
+ | ![Fallow Press home — torn-paper masthead over grayscale photography](assets/fallow-home.jpg) | ![Fallow Press Fields — pinned horizontal gallery panel](assets/fallow-fields.jpg) | ![Fallow Press Practices — pink quote chapter](assets/fallow-practices.jpg) |
213
+
214
+ The loop as it ran:
215
+
216
+ 1. `search_sites` (magazine filters) → shortlist judged from inline
217
+ screenshots → `get_site_details` on Emergence Magazine.
218
+ 2. **Capture before building**: `capture_live_site` + `record_site_motion`
219
+ on the live site *first*; full-page PNG and motion .webm kept in
220
+ [fallow-press/ref-motion/](fallow-press/ref-motion/) as the evidence trail.
221
+ 3. Build, then verify: full-page capture plus **panel-center pin shots** of
222
+ both horizontal pages (13 stops each, in
223
+ [fallow-press/_qa/](fallow-press/_qa/) — `capture-qa.mjs` is reusable).
224
+ 4. The pin shots caught a real bug: horizontal-panel entrances used
225
+ `toggleActions: "play none none reverse"`, and 100vw panels hide content
226
+ at midpoints on the way back — copy disappeared mid-view. Fix
227
+ (one-shot play entrances) is now doctrine: **full-viewport panels get
228
+ one-shot entrances**; QA pin shots land at panel **centers**, not uniform
229
+ fractions, or you photograph empty transition zones.
230
+
231
+ **2. Cerebrium recreation** (`C:/Users/Afjal/cerebrium-recreation/`) — a
232
+ fidelity-first recreation of cerebrium.ai, pixel-checked against the live
233
+ reference: full-page captures of both sides, `analyze_page_structure` band
234
+ compare, and SVG icon/legend fixes until the build matched the reference to
235
+ within 1px of total page height (10,871px vs 10,870px). This is the
236
+ **structure-before-pixels** doctrine at its strictest — band maps compared,
237
+ never just totals.
238
+
239
+ ![Cerebrium recreation — full-page build capture](assets/cerebrium-build.jpg)
240
+
241
+ **3. The Meridian** (`C:/Users/Afjal/editorial-site/`) — an editorial journal
242
+ built from ORDR/Hearst references: the first build to run the whole loop
243
+ end-to-end. `analyze_page_structure` caught a masthead band bug by comparing
244
+ the build's band map against the reference's; the reference captures,
245
+ motion film, and the reusable pre-scroll capture script live in
246
+ `editorial-site/_qa/`.
247
+
248
+ ![The Meridian editorial journal — full-page build capture](assets/meridian-build.jpg)
249
+
250
+ ![The Meridian — motion filmstrip from record_site_motion](assets/meridian-filmstrip.jpg)
251
+
252
+ ## Skills used to build these
253
+
254
+ | Skill | Role in the builds |
255
+ |---|---|
256
+ | `awwwards-inspiration` | The 8-step loop itself (ships with this package): search → judge from screenshots → design DNA → capture/motion study → state direction → build → band-map verify. |
257
+ | `gsap-scrolltrigger` | The horizontal pin + `containerAnimation` pattern (ease `"none"`, one-shot entrances) driving both Fallow Press horizontal pages. |
258
+ | `gsap-core` / `gsap-timeline` | Tween composition and sequenced hero entrances (torn-paper drop, panel copy rises). |
259
+ | `frontend-design` | Typography, palette and layout judgment applied when translating reference DNA into original pages. |
260
+ | `lenis` (library, via skill guidance) | smooth scrolling synced to ScrollTrigger on the Fallow Press home page. |
261
+ | `tailwindcss` / plain CSS | All builds are plain hand-rolled CSS — flat 2D, no frameworks needed. |
262
+
263
+ Reduced-motion, JS-less visits, and capture tools all get graceful fallbacks
264
+ (vertical stacks; progressive-enhancement reveals).
265
+
266
+ **What the verification loop caught** — proof the structure-before-pixels
267
+ doctrine is load-bearing:
268
+
269
+ - Element **posters lie**: the first showcase build was designed from poster
270
+ frames alone and rendered a spinning 3D ring as *floating static cards*.
271
+ Downloading the element videos (`get_site_elements`) and frame-tiling them
272
+ revealed the motion truth — now the skill mandates studying motion before
273
+ animating.
274
+ - Full-page captures of reveal-on-scroll builds showed blank sections: `.reveal`
275
+ animation state vs capture's no-scroll reality. Builds ship
276
+ content-visible-without-JS progressive enhancement.
277
+ - Horizontal-panel copy vanished **mid-view** on the Fallow Press pages:
278
+ `toggleActions` reverse reverts entrances while a 100vw panel is still
279
+ holding the viewport (see above).
280
+ - Band-map compare kept the references' rhythm instead of drifting on
281
+ section heights (Cerebrium, The Meridian).
282
+
283
+ Prompt counts: **3** for the original showcase build (the build ask, the
284
+ motion correction that exposed the poster-lie, the structure pass) and
285
+ **1** for Fallow Press ("create a new website using our MCP and skills… no
286
+ 3D websites") — its two follow-ups were caught by the QA loop, not by the
287
+ user. Each correction became doctrine in the shipped `awwwards-inspiration`
288
+ skill: frame-study element videos before animating; judge page architecture
289
+ from the studied passages; tile per element, not one giant filmstrip;
290
+ capture live sites and animation **before** building.
291
+
292
+ ## Can awwwards-mcp crawl the sitemap? (robots.txt notes)
293
+
294
+ The awwwards.com `robots.txt` advertises
295
+ `Sitemap: https://www.awwwards.com/sitemap.xml` and — verified live
296
+ 2026-09-18 — **that sitemap URL returns a soft-404 HTML page** (as do common
297
+ child names like `/sitemap-websites.xml`). So sitemap discovery isn't
298
+ currently a path to more data; the polite crawl surface is exactly what the
299
+ indexer uses:
300
+
301
+ - **Allowed and used**: `/websites/`, `/websites/<filter>/`, `/sites/<slug>`
302
+ (one filter per URL; deep pagination stays un-crawled).
303
+ - **Disallowed and never fetched**: `/tag/`, `/search-websites`,
304
+ `/websites/?` (query-string pagination), `/elements/*`, `/vote/`,
305
+ favourites/likes/follows, and the rest of the 33 rules.
306
+ - Our client (`src/awwwards.ts` `buildFilterUrl`) constructs **only**
307
+ `/websites/…` paths at 1 request/second — the loop stays inside the
308
+ published rules by construction, not by convention.
309
+
310
+ ## Contributing
311
+
312
+ PRs welcome! The project especially needs **parser-drift fixes** — when live
313
+ awwwards.com markup changes, a fresh HTML snapshot attached to an issue often
314
+ becomes the new test fixture and the fastest merged PR. See
315
+ [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide:
316
+
317
+ **Parser-drift is monitored automatically.** A probe script
318
+ ([scripts/parser-drift-probe.mjs](scripts/parser-drift-probe.mjs),
319
+ `npm run drift`) checks every markup anchor the parsers depend on — the
320
+ `split`/`indexOf`/regex literals in `src/parsers.ts` — against the live
321
+ listing and detail pages (2 fetches, 1 request/second, same politeness as
322
+ the client). A daily GitHub Action ([.github/workflows/parser-drift.yml](.github/workflows/parser-drift.yml))
323
+ runs it and, on drift, opens/updates a single tracking issue with the exact
324
+ anchors that changed (and auto-closes it when a later run is green). To run
325
+ it yourself: `npm run drift` (live, exit code 0/1/2) or `npm run drift -- --fixture`
326
+ (offline, checks the committed fixtures still feed every anchor). Raw HTML
327
+ is never diffed or stored — anchors only fire when the parsers actually
328
+ break, so there are no false alarms from cosmetic tweaks.
329
+
330
+ - Development setup & project layout (offline fixture-tested, no network in tests)
331
+ - How to create a PR: fork → `fix/`/`feat/`/`docs/` branch → typecheck + tests → PR template
332
+ - The politeness constraints new code must keep (1 req/s, robots.txt paths, light runtime deps)
333
+
334
+ Bugs and feature ideas start as
335
+ [issues](https://github.com/INSANE0777/Awwwards-mcp/issues/new/choose) with
336
+ templates. Security problems go privately — see
337
+ [SECURITY.md](SECURITY.md). By participating you agree to the
338
+ [Code of Conduct](CODE_OF_CONDUCT.md).
339
+
97
340
  ## Development
98
341
 
99
342
  ```bash
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ import { Cache } from "./cache.js";
12
12
  import { createHandlers } from "./server.js";
13
13
  import { captureLiveSite } from "./capture.js";
14
14
  import { runIndexer, shouldAutoIndex } from "./indexer.js";
15
+ import { checkForUpdate } from "./version-check.js";
15
16
  // The handlers return ToolResponse, which is structurally identical to the
16
17
  // SDK's CallToolResult at runtime ({ content, isError? }). CallToolResult's
17
18
  // schema additionally carries a [k: string]: unknown index signature that a
@@ -100,4 +101,7 @@ if (shouldAutoIndex(cache)) {
100
101
  console.error(`awwwards-mcp: background index failed: ${err instanceof Error ? err.message : String(err)}`);
101
102
  });
102
103
  }
104
+ // Update notice: once a day, compare against the npm registry; stderr-only,
105
+ // never blocks serving. AWWWARDS_AUTO_UPDATE=1 opts into background install.
106
+ checkForUpdate(pkgJson.version);
103
107
  await server.connect(new StdioServerTransport());
package/dist/parsers.js CHANGED
@@ -148,9 +148,12 @@ export function parseElements(html) {
148
148
  continue;
149
149
  }
150
150
  const mediaPath = blob?.collectableImage;
151
- // Only element media (videos/posters live under element/); other blobs
152
- // (site card, collections) must not leak in if the end bound is missing.
153
- if (typeof mediaPath === "string" && mediaPath.startsWith("element/")) {
151
+ // Only element media; other blobs (site card, collections) must not leak
152
+ // in if the end bound is missing. Media paths come in two schemes: modern
153
+ // sites store them under element/YYYY/MM/..., 2018-era sites under
154
+ // external/YYYY/MM/... (both verified live 2026-09-18 — the CDN serves
155
+ // both prefixes and their _static.jpeg posters).
156
+ if (typeof mediaPath === "string" && /^(element|external)\//.test(mediaPath)) {
154
157
  elements.push({
155
158
  title: decodeEntities(String(blob.collectableTitle ?? "")),
156
159
  mediaPath,
@@ -0,0 +1,118 @@
1
+ // Update notification: once a day, compare the running version against the
2
+ // npm registry and (optionally) self-update. Designed around the constraints
3
+ // of a stdio MCP server:
4
+ // - stdout is the JSON-RPC channel → every notice goes to stderr only
5
+ // - serving must never wait on this → fire-and-forget, 3s timeout,
6
+ // every failure swallowed (registry unreachable / package unpublished)
7
+ // - at most one registry hit per day per machine (state in the cache dir)
8
+ // Auto-update is strictly opt-in: AWWWARDS_AUTO_UPDATE=1 runs
9
+ // `npm install -g awwwards-mcp@<latest>` detached, then asks the user to
10
+ // restart their agent; without the env var, the notice just tells the agent.
11
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
12
+ import { spawn } from "node:child_process";
13
+ import { join } from "node:path";
14
+ import { homedir } from "node:os";
15
+ const REGISTRY_URL = "https://registry.npmjs.org/awwwards-mcp/latest";
16
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
17
+ export function parseVersion(v) {
18
+ const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
19
+ if (!m)
20
+ return [0, 0, 0];
21
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
22
+ }
23
+ /** 1 when a > b, -1 when a < b, 0 when equal. Unparseable sorts as oldest. */
24
+ export function compareVersions(a, b) {
25
+ const [pa, pb] = [parseVersion(a), parseVersion(b)];
26
+ for (let i = 0; i < 3; i++) {
27
+ if (pa[i] !== pb[i])
28
+ return pa[i] > pb[i] ? 1 : -1;
29
+ }
30
+ return 0;
31
+ }
32
+ function stateFile() {
33
+ const cacheRoot = process.env.AWWWARDS_CACHE_DIR ?? join(homedir(), ".awwwards-mcp");
34
+ return join(cacheRoot, "version-check.json");
35
+ }
36
+ function readState() {
37
+ try {
38
+ if (!existsSync(stateFile()))
39
+ return null;
40
+ return JSON.parse(readFileSync(stateFile(), "utf8"));
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ function writeState(state) {
47
+ try {
48
+ mkdirSync(join(stateFile(), ".."), { recursive: true });
49
+ writeFileSync(stateFile(), JSON.stringify(state));
50
+ }
51
+ catch {
52
+ // notification is best-effort; an unwritable cache dir is fine
53
+ }
54
+ }
55
+ export function buildUpdateNotice(current, latest, autoUpdateEnabled) {
56
+ if (compareVersions(latest, current) <= 0)
57
+ return null;
58
+ const lines = [
59
+ `awwwards-mcp: update available — v${latest} (running v${current}).`,
60
+ autoUpdateEnabled
61
+ ? `awwwards-mcp: v${latest} is being installed in the background; restart your agent to load it.`
62
+ : `awwwards-mcp: update with \`npm install -g awwwards-mcp@latest\` (or clear your npx cache), or set AWWWARDS_AUTO_UPDATE=1 to self-update on startup.`,
63
+ ];
64
+ return lines.join("\n");
65
+ }
66
+ /**
67
+ * Fire-and-forget update check. Never throws, never blocks the caller:
68
+ * registry errors, timeouts, and unwritable state files are all swallowed.
69
+ */
70
+ export function checkForUpdate(currentVersion) {
71
+ const auto = process.env.AWWWARDS_AUTO_UPDATE === "1";
72
+ // Skip the daily gate when auto-update is on but no latest is known yet?
73
+ // No: state gates the fetch itself, so a fresh install always has a shot.
74
+ const state = readState();
75
+ if (state && Date.now() - state.checkedAt < CHECK_INTERVAL_MS) {
76
+ // within the interval: reuse the known latest to (re)warn without fetching
77
+ if (state.latest) {
78
+ const notice = buildUpdateNotice(currentVersion, state.latest, auto);
79
+ if (notice)
80
+ console.error(notice);
81
+ }
82
+ return;
83
+ }
84
+ void (async () => {
85
+ try {
86
+ const res = await fetch(REGISTRY_URL, {
87
+ signal: AbortSignal.timeout(3000),
88
+ headers: { accept: "application/json" },
89
+ });
90
+ if (!res.ok)
91
+ return; // unpublished / registry hiccup → silent
92
+ const data = (await res.json());
93
+ if (!data?.version)
94
+ return;
95
+ writeState({ checkedAt: Date.now(), latest: data.version });
96
+ const notice = buildUpdateNotice(currentVersion, data.version, auto);
97
+ if (notice)
98
+ console.error(notice);
99
+ if (notice && auto)
100
+ startSelfUpdate(data.version);
101
+ }
102
+ catch {
103
+ // offline / timeout / JSON garbage — never surface, never block
104
+ }
105
+ })();
106
+ }
107
+ function startSelfUpdate(version) {
108
+ try {
109
+ const child = spawn(process.platform === "win32" ? "npm.cmd" : "npm", ["install", "-g", `awwwards-mcp@${version}`], { detached: true, stdio: "ignore" });
110
+ child.on("error", () => {
111
+ console.error(`awwwards-mcp: background self-update failed to start — install manually with \`npm install -g awwwards-mcp@${version}\``);
112
+ });
113
+ child.unref();
114
+ }
115
+ catch {
116
+ // self-update is best-effort; the notice already told the user what to do
117
+ }
118
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "awwwards-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Free MCP server giving AI agents design inspiration from Awwwards: search award-winning sites with inline screenshots and extract design DNA.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -13,6 +13,24 @@
13
13
  "README.md",
14
14
  "skills"
15
15
  ],
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "awwwards",
20
+ "design",
21
+ "inspiration",
22
+ "web-design",
23
+ "screenshots",
24
+ "agent-tools"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/INSANE0777/Awwwards-mcp.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/INSANE0777/Awwwards-mcp/issues"
32
+ },
33
+ "homepage": "https://github.com/INSANE0777/Awwwards-mcp#readme",
16
34
  "engines": {
17
35
  "node": ">=22.13.0"
18
36
  },
@@ -22,6 +40,8 @@
22
40
  "typecheck": "tsc --noEmit",
23
41
  "test": "vitest run",
24
42
  "smoke": "tsx test/live-smoke.ts",
43
+ "drift": "node scripts/parser-drift-probe.mjs",
44
+ "doctor": "node scripts/doctor.mjs",
25
45
  "prepublishOnly": "npm run build"
26
46
  },
27
47
  "dependencies": {
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: awwwards-doctor
3
+ description: Use when awwwards-mcp scraping or tools stop working — BlockedError, "layout may have changed" parser errors, empty results, failed captures, stale searches — or when the user reports the MCP behaving oddly. Guides running the doctor/drift/index scripts, applying their fixes, re-anchoring parsers after real awwwards.com drift, and recovering the in-flight task that surfaced the failure.
4
+ ---
5
+
6
+ # Awwwards MCP Doctor
7
+
8
+ You are repairing the awwwards-mcp toolchain or working around it mid-task.
9
+ Work in this order: **diagnose → fix mechanically → fix parsers (if drift) →
10
+ recover the user's task**. Never delete user data; the only destructive step
11
+ (cache DB moved aside) preserves the old file in the OS temp dir.
12
+
13
+ ## 1. Run the doctor (repo checkout)
14
+
15
+ ```bash
16
+ npm run doctor # diagnose only
17
+ npm run doctor -- --fix # diagnose AND apply available fixes
18
+ npm run doctor -- --json # machine-readable report
19
+ ```
20
+
21
+ The doctor checks five things and knows how to fix the mechanical ones:
22
+
23
+ | Check | Failure meaning | What --fix does |
24
+ |---|---|---|
25
+ | network | awwwards.com blocked/challenged the request | nothing to fix locally — wait, retry later; never retry in a loop |
26
+ | parser-drift | awwwards.com markup changed under the parsers | captures the raw live page into `docs/drift-<timestamp>/` (gitignored) as the fixture for the fix |
27
+ | playwright-chromium / ffmpeg-static | optional capture deps missing or broken | installs them (dev deps + `npx playwright install chromium`) |
28
+ | cache | SQLite DB corrupt/locked, or index older than 30 days | moves an unreadable DB aside (kept in temp) and rebuilds via `npm run index` |
29
+ | boot | `dist/cli.js` doesn't start | `npm run build` |
30
+
31
+ Re-run the doctor after fixing; the verdict must be HEALTHY before you claim
32
+ the MCP is repaired.
33
+
34
+ ## 2. Fixing real parser drift (the only code-level failure)
35
+
36
+ If the doctor reports parser-drift, the anchor probes say which parser broke.
37
+ Repair sequence — keep it mechanical, verify at each step:
38
+
39
+ 1. **Capture the evidence**: the `--fix` snapshot in `docs/drift-*/` IS the
40
+ new truth. (The daily GitHub Action also opens/updates a `parser-drift`
41
+ tracking issue with the drifted anchor names.)
42
+ 2. **Locate the new anchors**: open the captured HTML and find the markup
43
+ that replaced the old anchor (search the old anchor's *purpose*, e.g. the
44
+ card JSON blob, the Elements section heading, the og:image meta).
45
+ 3. **Re-anchor the parsers** in `src/parsers.ts` — change the minimum needed
46
+ (one split/indexOf/regex literal), not the surrounding logic.
47
+ 4. **Update the probe list** in `scripts/parser-drift-probe.mjs`
48
+ (`PROBE_GROUPS`) so the same failure is detectable next time.
49
+ 5. **Refresh the fixture**: download the live page with the same UA the
50
+ client uses and replace `test/fixtures/listing.html` (or the detail
51
+ fixture). The offline tests run against fixtures only — a refreshed
52
+ fixture proves the re-anchor against reality.
53
+ 6. `npm run typecheck && npm test` — all tests must pass.
54
+ 7. Commit as `fix: re-anchor <parser> after awwwards.com markup change`,
55
+ reference the tracking issue.
56
+
57
+ ## 3. End users (installed via npm, not a repo checkout)
58
+
59
+ The doctor scripts live in the repo, so for an npm-installed server:
60
+
61
+ 1. Check the version notice: stderr at startup prints when a newer
62
+ `awwwards-mcp` exists. Update with `npm install -g awwwards-mcp@latest`
63
+ (or clear the npx cache) and restart the agent — a surprising share of
64
+ "broken" reports are old parsers fixed in a newer release.
65
+ `AWWWARDS_AUTO_UPDATE=1` in the MCP server env opts into background
66
+ self-update on startup.
67
+ 2. Reset local state: `rm -rf ~/.awwwards-mcp` is safe (cache + index +
68
+ version-check state rebuild automatically; nothing user-authored lives
69
+ there).
70
+ 3. If it still fails, it's upstream (block or drift): open an issue at the
71
+ awwwards-mcp repo attaching the raw HTML of the failing URL.
72
+
73
+ ## 4. Recovering the in-flight task (the reason you noticed)
74
+
75
+ The user's build/research doesn't wait for the repair. Degrade gracefully:
76
+
77
+ - **Stale cache is your friend**: `search_sites`/`get_site_details` serve
78
+ cached data when live fetches fail — say so in the reply ("7-day-old
79
+ cached data").
80
+ - **BlockedError means stop, not retry**: the client never retries through
81
+ blocks; tell the user and continue with cached/alternate data.
82
+ - **Capture tools down** (playwright missing): fall back to
83
+ `get_site_details` screenshots (CDN-served, no browser needed) until
84
+ `npm run doctor -- --fix` restores chromium.
85
+ - **Parser drift mid-task**: results may be partially empty (e.g. details
86
+ without palette). Surface which part is missing; don't silently present
87
+ degraded data as complete.
88
+ - After the MCP is healthy again, **re-run the failed calls** and reconcile
89
+ with whatever the fallback produced.
90
+
91
+ ## Anti-patterns
92
+
93
+ - Retrying blocked requests in a loop — politeness rules and block
94
+ detection exist precisely so this doesn't hammer awwwards.com.
95
+ - Diffing raw HTML to detect drift — cosmetic page changes would false-alarm
96
+ daily; only parser-anchor probes matter.
97
+ - "Fixing" parsers by loosening them to return empty results quietly — a
98
+ parse that can't find its anchors must error loudly (that is the drift
99
+ signal).
100
+ - Treating the doctor's UNHEALTHY verdict as done because one check passed.
@@ -44,14 +44,27 @@ Run this loop before building anything visual:
44
44
  reports a layout-drift error, fall back to judging the shortlisted
45
45
  screenshots, `get_site_elements` (which uses a different parser), or a
46
46
  `capture_live_site` of the site's URL for a first-hand full-page view.
47
- 6. **Get component-level visuals (when building).** Call `get_site_elements`
48
- on shortlisted sites to see individual design elements — 3D models, video
49
- content, mobile layouts, microcopy — with poster images inline and video
50
- URLs. Treat element posters as texture, not structure: they are video
51
- frames (often mid-animation or near-black), not full-section layouts.
47
+ 6. **Study MOTION before building (element videos, not posters).** Call
48
+ `get_site_elements` on shortlisted sites — it returns per-element video
49
+ URLs (preloader, page transition, case study, about…). **Download those
50
+ videos and tile them at 1–2 fps BEFORE writing any animation code**:
51
+ `curl -o e.mp4 <video-url> && ffmpeg -i e.mp4 -vf "fps=2,scale=480:-1,tile=6x4" -frames:v 1 tile-e.jpg`,
52
+ then Read the tile. (The full capture→review→build procedure — what to record, what to skip, motion inventories, build verification — is the `awwwards-motion-study` skill.) Element **posters are single frames** — they show
53
+ layout, not motion; a 3D carousel looks like floating static cards in a
54
+ poster (this exact mis-build happened). One tile per element shows you the
55
+ whole animation arc (easing, overlap, entrance order). If a marquee
56
+ animation needs finer study, re-tile that video at higher fps. The motion
57
+ IS the design: every reference animation you ship must trace to frames
58
+ you actually studied, and reference videos pass through
59
+ `showcase/ref-motion/` as the design's evidence trail.
52
60
  7. **State the design direction before writing code.** In prose: palette
53
- (hexes from the references), type mood, layout patterns, and tech choices,
54
- each traceable to a reference. Then build.
61
+ (hexes from the references), type mood, layout patterns, page
62
+ architecture (one page vs separate horizontal sections — judge this from
63
+ the reference passages you studied in steps 4–6, not habit), and tech
64
+ choices, each traceable to a reference. Then build. For a site's own
65
+ marquee frontend skills, prefer proven patterns (GSAP ScrollTrigger for
66
+ horizontal scroll chapters, Lenis for smooth scroll) over hand-rolled
67
+ scroll math.
55
68
  8. **Verify structure, then polish.** After building, capture your own build
56
69
  full-page (`capture_live_site` on its `file://` or served URL) and run
57
70
  `analyze_page_structure` on BOTH the reference and the build. Compare band
@@ -69,8 +82,14 @@ Run this loop before building anything visual:
69
82
  - **Dumping raw tool output at the user** — curate: show the shortlist, the
70
83
  chosen direction, and why.
71
84
  - **Designing from a thumbnail or element poster** — thumbnails are hero-only
72
- crops and posters are video frames; neither shows the page's real
73
- structure. When the reference URL is known, capture it full-page (step 4).
85
+ crops, posters are single video frames, and neither shows real structure OR
86
+ motion. When the reference URL is known, capture it full-page (step 4);
87
+ when an element has motion, tile its video (step 6) — never animate from
88
+ posters.
89
+ - **One filmstrip for everything** — a single N×N tiling of the *whole
90
+ recording* makes late tiles tiny and animation arcs illegible. Tile per
91
+ element/per passage at 1–2 fps instead; re-tile finer passages at higher
92
+ fps when easing or overlap matters.
74
93
  - **Padding empty bands to match total height** — if your build's total height
75
94
  matches the reference but a spacer/background band is far taller than the
76
95
  reference's equivalent, the height was stolen from real content sections.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: awwwards-motion-study
3
+ description: Use when building a site whose reference has animation — before writing any motion code, and again when verifying a build. Teaches what to record from a live site (and what to skip), how to study the recordings frame-by-frame (video input or ffmpeg tile-per-element), how to turn the study into a motion inventory, and how to verify the build's motion against the reference. Complements awwwards-inspiration (step 6) with the full capture→review→build procedure.
4
+ ---
5
+
6
+ # Awwwards Motion Study — capture, review, build
7
+
8
+ Motion is design: an award-site hero that "looks the same" in a screenshot
9
+ can be a spinning 3D ring, a scrubbed pin, or a staggered reveal. Static
10
+ captures hide it; element posters (single frames) lie about it. This skill
11
+ covers the whole chain: **record → review → inventory → build → re-record**.
12
+
13
+ The one rule everything serves: **never write animation code from memory or
14
+ posters — every shipped animation must trace to frames you actually studied.**
15
+
16
+ ## 1. What to capture
17
+
18
+ Record a **motion-through pass**: load, dwell, slow scroll, hover tour.
19
+ Three animation classes must all be on camera:
20
+
21
+ | Class | How it gets captured | Typical duration |
22
+ |---|---|---|
23
+ | Preloader / entrance | initial dwell after `load` | 5–8 s |
24
+ | Scroll-triggered + pinned/scrubbed | slow stepped scroll (~450 px / ~600 ms) | length of page |
25
+ | Hover / click / micro-interactions | virtual cursor visits interactive elements and dwells | 10–20 s |
26
+
27
+ **Capture these** — they are design:
28
+
29
+ - Preloader/entrance sequences and hero reveals
30
+ - Scroll-triggered reveals, parallax, horizontal pins, scrubbed timelines
31
+ - Hover states (cards, nav, buttons, image rollovers) and click effects
32
+ - Page transitions if the site has them, sticky/nav behaviors on scroll
33
+ - Marquees, tickers, counters — anything time-driven
34
+
35
+ **Skip these** — they are content, noise, or traps:
36
+
37
+ - **Content videos** (film hero backgrounds, stock footage, showreels):
38
+ they're the site's *media*, not its *interaction* — you'd be styling
39
+ someone's footage, not learning the motion design.
40
+ - **Loading states**: capture *after* they resolve. A half-loaded page
41
+ records lazy placeholders as if they were the design. Dwell first, then
42
+ scroll; if a section is still empty in the recording, it's a capture bug,
43
+ not a design feature.
44
+ - Cookie banners / newsletter modals: dismiss them early (unless you're
45
+ specifically building one) or they eat the first seconds of footage.
46
+ - Ads, tracking iframes, user-generated widgets.
47
+ - **Don't redistribute recordings**: captures are local study artifacts
48
+ (keep them in `ref-motion/` or `_qa/`). Awwwards content and site media
49
+ belong to their creators — study them, don't republish them.
50
+
51
+ ## 2. How to capture
52
+
53
+ Fast path first, fall back when it can't:
54
+
55
+ 1. **`record_site_motion`** (MCP tool): inline filmstrip + saved .webm path.
56
+ Best default. Known ceiling: ~30 s — heavy sites may time out.
57
+ 2. **`scripts/record-scrollthrough.mjs`** (repo checkout, playwright):
58
+ `node scripts/record-scrollthrough.mjs <url> <outDir> <name>.webm` —
59
+ full-control recorder: 7 s preloader dwell, 450 px/600 ms stepped scroll,
60
+ injected virtual cursor (playwright video doesn't render the real one)
61
+ that visits and dwells on interactive elements. Use when the MCP tool
62
+ times out or when you need to customize the tour.
63
+ 3. **Direct playwright**: `chromium.launch()` + `newContext({ recordVideo })`
64
+ when neither fits (custom viewports, login flows, multi-page tours).
65
+
66
+ Capture rules that prevent re-shoots:
67
+
68
+ - **Capture BEFORE building** — the doctrine step. Review first, code second.
69
+ - Use a **consistent viewport** (1440×900 matches the QA scripts) so your
70
+ reference tiles and build tiles are comparable.
71
+ - **Pre-scroll** to fire lazy content, scroll back to top, then record —
72
+ otherwise reveal-on-scroll sections record as blank boxes.
73
+ - For multi-page sites, record **each page** you'll rebuild (home, projects,
74
+ about), not just the hero.
75
+ - On the 30 s MCP ceiling: fall back to the script; never shorten the scroll
76
+ so much that sections get skipped.
77
+
78
+ ## 3. How to review the video
79
+
80
+ Posters are frames; you need frames **in sequence**. Two paths:
81
+
82
+ **Path A — direct video input.** `Read` the .webm first. If your model
83
+ supports video, watching the pass gives timing, easing, and transitions no
84
+ filmstrip can. If the read comes back "media omitted / not supported",
85
+ use Path B.
86
+
87
+ **Path B — tile per element at 1–2 fps.** One tiling *per element/passage*,
88
+ never one mega-strip of the whole recording (late tiles go thumbnail-size
89
+ and easing becomes unreadable — a mis-build happened exactly this way):
90
+
91
+ ```bash
92
+ ffmpeg -i reference.webm -vf "fps=2,scale=480:-1,tile=6x4" -frames:v 1 tile-hero.jpg
93
+ ```
94
+
95
+ Cut the video into per-element segments first (by timestamp from watching
96
+ the strip), then tile each segment. **Re-tile finer at higher fps** when
97
+ easing or overlap matters: `fps=6` on a 3 s passage shows whether an ease
98
+ is `power2.out` vs `expo.out`; overlap ordering needs ~200 ms granularity.
99
+
100
+ While reading tiles, extract per element:
101
+
102
+ | Field | What to look for |
103
+ |---|---|
104
+ | trigger | load / scroll-enter / scroll-scrub / hover / click |
105
+ | transform | opacity, y/x, scale, clip-path, blur — which properties move |
106
+ | duration + easing | count frames between first and last movement; fast-out = expo/power4, soft = power2, linear = scrub |
107
+ | stagger | siblings entering in sequence → measure the delay between them |
108
+ | overlap | does the next element start before the previous ends? |
109
+
110
+ Write the result as a **motion inventory** — a table per page — and build
111
+ from the table, not from impressions:
112
+
113
+ ```markdown
114
+ | Element | Trigger | Transform | Duration | Easing | Overlap |
115
+ |---|---|---|---|---|---|
116
+ | Hero paper | load | yPercent -104→0 | ~1.2 s | power4.out | photo fades under at -1.0 s |
117
+ | Story cards | scroll 86% | opacity + y 56 | ~0.85 s | power3.out | stagger 0.09 |
118
+ ```
119
+
120
+ ## 4. Build from the inventory
121
+
122
+ - Every shipped animation cites an inventory row. If you can't name the
123
+ frames an animation came from, you're inventing — stop and study.
124
+ - Use proven motion primitives, not hand-rolled scroll math: GSAP
125
+ ScrollTrigger (pins, `containerAnimation` with `ease: "none"` for
126
+ horizontal, `scrub` for scrubbed timelines), Lenis for smooth scroll.
127
+ - Full-viewport horizontal panels get **one-shot entrances**
128
+ (`toggleActions: "play none none none"`) — reverse-on-leave hides copy
129
+ mid-view (caught by QA pin shots).
130
+ - Ship `prefers-reduced-motion` fallbacks and progressive enhancement
131
+ (content visible without JS) — a capture tool or JS-less visit must never
132
+ see blank sections.
133
+
134
+ ## 5. Close the loop: re-record your build
135
+
136
+ Verification is the same skill pointed at yourself:
137
+
138
+ 1. Record your build exactly as you recorded the reference (same viewport,
139
+ same pass shape — `record_site_motion` works on `file://` URLs too).
140
+ 2. Tile your build's key passages and **compare tile-to-tile** against the
141
+ reference tiles: same trigger order? similar durations? easing in the
142
+ same family?
143
+ 3. QA captures for scroll builds must land at **panel/section centers**, not
144
+ uniform scroll fractions — mid-transition shots photograph empty
145
+ transition zones and look broken when they aren't.
146
+ 4. Fix what mismatches, re-record, repeat — one loop, not ten.
147
+
148
+ ## Anti-patterns
149
+
150
+ - **Designing from posters or thumbnails** — posters are single frames; the
151
+ 3D carousel reads as "floating static cards" (a real mis-build).
152
+ - **One filmstrip for the whole recording** — element-level detail dies in
153
+ an N×N mega-grid.
154
+ - **Capturing only the hero** — the distinctive motion usually lives below
155
+ the fold.
156
+ - **Recording over a half-loaded page** — lazy placeholders captured as
157
+ design.
158
+ - **Studying nothing, animating from vibes** — "it probably fades in" is
159
+ how builds drift from references.
160
+ - **Republishing recordings** — captures are local study evidence.