* DX-3023: add the Box proof-of-work skill for the remote MCP box and blob tools A new skill, upstash-box-proof-of-work, walks an agent through doing a task inside an Upstash Box over mcp.upstash.com and delivering a PR with screenshots: create a box with a browser, work and screenshot, mint presigned Blob upload URLs, curl the PNGs from the box straight to the bucket, and open the PR with the public URLs embedded. It is the first skill written for the box_* and blob_* tools of the remote server rather than the CLI or SDKs. Also lists the skill in the README table and skills.sh.json, points the combined skill's routing preamble and AGENTS.md at it, and adds box and blob to the ?features= groups in the README. Claude-Session: https://claude.ai/code/session_01UKzMsrVKo7FVLNWeKtNH2t * DX-3026: rename the Box proof-of-work skill to remote work and widen its triggers The skill only triggered when the user asked for a task "in a Box" and never fired in four runs of the same request, even one that named Upstash. It now covers remote work in general: when to take a task into a box (asked for remote/sandbox/cloud work, a PR, a preview URL, or a screenshot as the deliverable, isolation, parallel tasks), the one-box-per-task flow with box_git clone first, preview URLs, screenshots via Blob, and a scaling section on labels, snapshots and fork. Proof of work is one use of it rather than the whole point. Claude-Session: https://claude.ai/code/session_01BiJaoXfVZRsmciwe2YbjuR * DX-3026: name the code factory in the remote-work skill description Claude-Session: https://claude.ai/code/session_01BiJaoXfVZRsmciwe2YbjuR * DX-3026: drop the features= scoping from the remote-work skill Claude-Session: https://claude.ai/code/session_01BiJaoXfVZRsmciwe2YbjuR
7.9 KiB
Agent guide
Keep skill docs short and effective
Skills are consumed by agents, not read as blog posts. Say what the correct behaviour is and show it — then stop. Don't pile on caveats, notes and inline comments beyond what a reader needs to act on the example.
Never edit skills/upstash/ by hand
It is generated by scripts/build.mjs from each skills/upstash-*/SKILL.md. The build strips the frontmatter and writes overview.md into the combined skill.
To change a sub-skill:
- Edit
skills/<skill-name>/SKILL.md— the source of truth. - Run
npm run buildto regenerateskills/upstash/. - Commit both the source and the regenerated output.
CI runs npm run check, which fails if the generated output is stale. check.mjs asserts that git status --porcelain is empty after rebuilding, so it only passes on a fully clean tree — commit your source edits and the regenerated output before running it, otherwise it reports them as "out of date".
The build's pre-check also refuses to run while skills/upstash/ has unstaged changes — if you edited it by mistake, git restore skills/upstash/ first.
MCP first, CLI second
The plugins now install the hosted MCP server alongside the skills, so for
account and data operations the agent usually has authenticated tools already
in the session — creating and inspecting databases, indexes and Blob buckets,
running Redis commands, stats, logs, backups, QStash schedules and the DLQ, plus Box
(shell, browser, git, previews) and Blob upload URLs. skills/upstash-box-remote-work/
is the one skill written for those remote Box and Blob tools. The CLI covers
the same ground but needs a global npm install, a login and an API key.
So skills/upstash-cli/ opens by telling the agent to prefer the MCP when its
tools are present and to fall back to the CLI for shell work — CI steps,
provisioning scripts, piping JSON — and scripts/header.md says the same in
the combined skill's routing preamble. Keep that ordering when editing either
one, and do not add CLI instructions to an SDK skill that would pull an agent
away from the MCP.
The SDK skills are unaffected. They are about writing application code, which the MCP does not do.
Skill descriptions are the search index
skills.sh matches multi-word searches against each skill's description (single-word searches match the name). Keep descriptions long and intent-rich: what the SDK is, a "Use when…" list of concrete tasks, and the words users actually type (rate limiting, message queue, background jobs, vector database, session storage…). Never rename a skill to improve ranking — change the description instead. description must stay on one line and must not contain : (the build script parses it with a regex, and YAML would otherwise need quoting).
Gemini CLI extension
gemini-extension.json at the root makes the repo installable with gemini extensions install https://github.com/upstash/skills (it clones the default branch; no tag needed). Listing in the gallery at geminicli.com/extensions is crawler-only: the repo must carry the gemini-cli-extension GitHub topic and have a tag, and there is no publish command or submission form. We have not tagged this repo for that yet, so bump version in gemini-extension.json only when the skills change in a way users should see.
The plugin is titled "Upstash Redis"
The skills and the MCP cover every Upstash product, and the descriptions list
them all. The title is still Upstash Redis, because Redis is the flagship
and because marketplace search is largely name-driven — someone browsing for
"redis" has to find us. Every user-facing plugin title reads Upstash Redis:
displayName in the Claude and Cursor plugin.json and in both marketplace
entries, and interface.displayName for Codex.
Three things nearby are not display surfaces and must not be renamed:
"name": "upstash"— the install identifier. It is what/plugin install upstash@upstash,codex plugin marketplace addandenabledPluginskey off. Renaming it breaks every install command in the README and orphans existing installs.author.name/owner.name:"Upstash"— the company publishing the plugin, not the plugin itself.- Marketplace-level
displayName(.agents/plugins/marketplace.json) — names the vendor's marketplace, which hosts the plugin; the plugin's own title comes from.codex-plugin/plugin.json.
This does not loosen the skills rule above: skill names are still never changed
for ranking. That rule is about the name: in a SKILL.md frontmatter, whose
slug is a stable identifier; this one is about plugin titles, which are free
text. scripts/check-manifests.mjs enforces both halves — the titles must say
"Upstash Redis", the slug and vendor must not.
Plugin manifests must stay in sync
Six manifests describe the same plugin to different clients: plugin.json
(portable Agent Plugins) plus mcp.json, .claude-plugin/, .codex-plugin/,
.cursor-plugin/ and gemini-extension.json. They drift easily — a change to
one is almost always a change to all of them.
There is deliberately no opencode.json in the repo. Nothing would install it:
OpenCode users get the skills from npx skills add upstash/skills --agent opencode and configure the MCP themselves (see the README). OpenCode reads
opencode.json from the project root, so a committed one would only ever
configure OpenCode for people running it inside a clone of this repo — a
personal dev setting, not something the repo should ship. It is gitignored;
keep your own if you want the MCP while working here.
npm run check runs scripts/check-manifests.mjs, which asserts that:
versionanddescriptionmatchplugin.jsoneverywhere, including the Claude and Cursor marketplace entries;- the description mentions the MCP server, since that is the only place a user learns the plugin ships one;
- every client points the MCP at
https://mcp.upstash.com/mcp; .codex-plugin/plugin.jsoncarries a completeinterfaceblock — Codex renders the plugin card from it, and a missinglogo/composerIconis why an installed plugin shows no Upstash icon.
Schemas are the source of truth, not the other manifests
These clients look alike and are not. Check the real schema before copying a
field from one manifest to another — several are additionalProperties: false,
where a stray field is a hard validation failure rather than harmless noise.
| Client | Schema | Strict? | Icon field | $schema key |
|---|---|---|---|---|
| Agent Plugins | agent-plugins.org/schemas/1.0.0/ (serves JSON) |
yes | none | yes, and it resolves |
| Claude Code | code.claude.com/schemas/ (serves the docs page, not JSON) |
no | none | accepted by claude plugin validate |
| Cursor | github.com/cursor/plugins/schemas/ |
yes | logo |
no — the key itself is rejected |
| Codex | plugin-json-spec.md in openai/codex, no URL |
— | interface.logo, interface.composerIcon |
none published |
| Gemini CLI | none published | — | none | none published |
Two traps worth remembering:
- Cursor's marketplace entry allows only
name,source,descriptionandminClientVersions— not the version/author/license/category/tags block that Claude's entry takes. Per-plugin metadata goes in.cursor-plugin/plugin.json, which does accept all of it pluslogo. - Cursor rejects
$schema. Its schemas declare no such property, and none of Cursor's own 65 official manifests carry one.
Validate for real rather than by eye: claude plugin validate <path> checks the
Claude manifests, and Cursor's schemas can be diffed against ours from a clone
of github.com/cursor/plugins. scripts/check-manifests.mjs encodes what both
found.
Branding assets live in assets/. Codex and Cursor both render them; Claude
Code, Gemini CLI and the Agent Plugins schema have no icon field, so the icon
cannot be wired up for those clients.