Files
Cahid Arda 78a248deee DX-3026: rename the Box proof-of-work skill to remote work and widen its triggers (#43)
* 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
2026-09-11 20:29:19 +03:00

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:

  1. Edit skills/<skill-name>/SKILL.md — the source of truth.
  2. Run npm run build to regenerate skills/upstash/.
  3. 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 add and enabledPlugins key 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:

  • version and description match plugin.json everywhere, 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.json carries a complete interface block — Codex renders the plugin card from it, and a missing logo/composerIcon is 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, description and minClientVersions — 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 plus logo.
  • 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.