We Shipped Five Skills Nobody Could Install
💥 The symptom: Every paid Godmode skill installed through our MCP server, reported success, then quietly vanished. Claude Code couldn't see it.
📦 Bug one (visible): The zip wrapped the skill in an extra folder, so it unpacked to
skills/<slug>/<slug>/ — one level too deep to register.🔑 Bug two (silent): Before extracting, the installer checks a sha256 fingerprint. The fingerprint pinned on the server had drifted from the actual bytes, so the download hard-failed.
🔧 The fix: Rebuild every zip flat, re-pin the fingerprints to the exact bytes, and ship all three moving parts in one atomic deploy.
✅ The result: All five skills install clean again. Verified end-to-end through the MCP.
The symptom: install succeeds, skill disappears
You ask Claude to install a Godmode skill. The MCP server says "installed." You restart Claude Code and the skill isn't there.
No error. No stack trace. The single most useless kind of failure: the one that reports success.
Installing a skill is a six-step relay. Each step hands off to the next, and two of those steps were quietly broken. Click through the chain to see where.
The two broken links were the last two. Verify-the-fingerprint and unpack-to-disk. Everything up to that point worked, which is exactly why "installed" came back before anything actually landed.
What a sha256 fingerprint actually is
A sha256 is a fingerprint of a file. Feed in the exact bytes, get back the same 64-character code every time. Change one byte and the code changes completely.
The installer uses it as a bouncer. It downloads the zip, recomputes the fingerprint, and compares it to a value the server promised in advance. Match, and it unpacks. No match, and it refuses.
→ same code
one byte off
→ total mismatch
Think of it like a tamper-evident seal on a parcel. The courier checks the seal against the number on the docket before handing it over. If they don't match, the parcel is either damaged or swapped, and it doesn't get delivered. The fingerprint is that seal for a download: it proves the bytes are exactly the ones we meant to send, not a corrupted copy or a wrong file.
So the check is a feature, not the bug. It's the thing standing between you and a half-downloaded or tampered skill. The bug was that we let the promised fingerprint drift away from the actual bytes — so the bouncer started turning away the real guest.
Bug one: the zip nested one folder too deep
A skill is a folder with a SKILL.md at its root. Claude Code looks for it at exactly skills/<slug>/SKILL.md. One level off and the skill is invisible.
Our packaging script zipped each skill with its own folder inside. On extraction that nests an extra level. Toggle between the two and watch the path.
The wrapper version put SKILL.md at skills/godmode/godmode/SKILL.md. Claude Code looked in skills/godmode/, found a folder instead of a manifest, and registered nothing.
The fix was to repackage every skill flat — SKILL.md and the runner files sitting at the zip root, no wrapper directory. One line in the build script. The whole class of bug disappears.
Bug two: the fingerprint had drifted
The expected fingerprint isn't computed live. It's pinned in a hardcoded catalog inside the server's edge function. When we rebuilt the zips to fix bug one, the bytes changed — but the pinned fingerprint still described the old bytes.
So the installer downloaded the new, correct zip, recomputed its fingerprint, compared it to the stale pin, and hard-failed. Doing its job perfectly, against the wrong reference. Pick a scenario and watch the gate decide.
The fix was to re-pin the catalog to the fingerprints of the exact bytes we'd just built, then prove zero drift with a verifier script before anything shipped.
$ node scripts/verify-catalog-shas.mjs
godmode-lite 2.3.0 b1eb16d8... OK
godmode 2.3.1 21635f56... OK
godmode-plus 2.7.0 a0bf2740... OK
evolution 3.3.0 2bf4e80b... OK
one-shot 3.12.0 dc525a10... OK
-> 0 drift. safe to deploy.
The fix has to ship in one piece
Here's the part that makes this more than a one-line patch. The fingerprint lives in three places that all have to agree, and they live in three different systems.
The bytes
The actual zip files, stored in Cloudflare KV. This is what the installer downloads.
The pin
The expected fingerprint, hardcoded in the catalog inside the Supabase edge function.
The gate
The downloads worker that serves a signed URL only for the filename the edge function expects.
Push the new bytes to KV but leave the old pin in place, and you flip every live install into a hard fingerprint failure. Deploy the new worker without the matching edge function, and a skill served under a renamed file stops resolving. Half a deploy is worse than no deploy.
So the release runs as one atomic script. All three systems, one pass, verified at the end.
three systems
verified last
[1] Build flat — repackage all 5 skills, no wrapper dir
↓
[2] Re-pin — copy the new fingerprints into the catalog
↓
[3] Verify — verify-catalog-shas.mjs must report zero drift
↓
[4] KV upload — push the 4 paid zips to Cloudflare KV
↓
[5] Deploy edge fn — ship the catalog with --no-verify-jwt
↓
[6] Deploy worker — the downloads gate
↓
[7] Verify live — re-download, re-hash, confirm the bytes match
Do
Treat the bytes, the pin, and the gate as one release. Rebuild, re-pin, verify zero drift, then deploy all three in a single pass and re-test a real download at the end.
Don't
Don't push zips to storage and "fix the catalog later." The moment the bytes and the pin disagree, every install in the wild hard-fails the fingerprint check.
What shipped
Receipt, not a brag. Every paid skill rebuilt flat, every fingerprint re-pinned, one deploy script to make the three systems move together.
| Skill | Version | Repackaged | Install |
|---|---|---|---|
| Godmode Lite (free) | 2.3.0 | flat | ✅ clean |
| Godmode | 2.3.1 | flat | ✅ clean |
| Godmode+ | 2.7.0 | flat | ✅ clean |
| Evolution | 3.3.0 | flat | ✅ clean |
| One-Shot Scripts | 3.12.0 | flat | ✅ clean |
| Change | Detail |
|---|---|
| Root cause 1 | Wrapper-dir zips nested skills to skills/<slug>/<slug>/ |
| Root cause 2 | Catalog sha256 pins drifted from the rebuilt bytes |
| Build change | All zips repackaged flat (SKILL.md + runner at root) |
| New script | deploy-skill-release.sh — atomic KV + edge fn + worker |
| Guard added | verify-catalog-shas.mjs blocks any deploy with drift |
| Verification | End-to-end install of all 5 skills through the live MCP |
Why this was so sneaky
Success that lied
The MCP reported "installed" before the bytes ever landed on disk. A green checkmark over a failed extraction.
Drift you can't grep
The bytes and the pin live in two systems. Nothing in a code diff shows they've fallen out of sync.
Looks fine locally
Unzip the wrapper zip by hand and it looks perfectly normal. The nesting only bites the installer's exact path.
Three-part deploys
Storage, edge function, and worker deploy separately. Any one of them lagging half-breaks the release.
The lesson is not "fingerprint checks are fragile." The check did exactly what it should: it refused a file that didn't match its promise. The real discipline is keeping the promise and the bytes in lockstep, and never shipping one of them without the other.
Install Godmode from inside Claude
The skills are live and verified. Add the Godmode MCP server, and Claude can install any skill you own in one line — fingerprint-checked on the way in.
Get Godmode More post-mortems