Invite-only.
← Templates
Build· Mixed

Ship a VS Code extension to the Marketplace

9-step playbook from 'F5 to debug' to 'live in the VS Code Marketplace and Open VSX.' Real publisher gotchas, real activation traps, real agent prompts.

Open in DockDevelopers shipping their first VS Code extension

Publishing to the VS Code Marketplace is mostly a fight with three things: the Azure DevOps publisher account (a non-obvious prerequisite), Personal Access Token scopes (set them wrong and vsce silently refuses), and activation events (the 1.74+ default of activating on every event sunset, you have to declare what you actually need). This playbook walks the 9 gates with the official Microsoft docs, the package.json snippets that work, and agent prompts for the parts agents can do (drafting the README that doubles as the Marketplace listing, configuring CI for vsce publish, picking minimal activation events).

Outcome

Your VS Code extension live on the Marketplace AND on Open VSX (so VSCodium / Cursor / Theia users can install too), with CI that publishes a new version on every git tag.

Time2-5 daysDifficultyintermediateForDevelopers shipping their first VS Code extension.
The template · 9 steps

Top to bottom. Each step has tasks, pointers, gotchas.

Scaffold a clean extension with generator-code

30 min

If you don't already have a working extension, run npx --package yo --package generator-code -- yo code. The generator gives you a tsconfig, esbuild bundling, .vscodeignore (critical for tree-shaking the .vsix), and a sane package.json. Even if you have an existing extension, compare against the generator output — most early extensions miss .vscodeignore tuning.

Tasks
  • Run npx --package yo --package generator-code -- yo code
  • Pick TypeScript or JavaScript, pick esbuild bundling
  • Pick the extension type: command, language, theme, snippets, keymap
  • Open the generated folder in VS Code, hit F5 to launch the Extension Development Host
  • Verify your command runs in the dev host before doing anything else
Gotchas
  • The generator's default .vscodeignore is too permissive; review it before first package or your .vsix balloons to 10+ MB.
  • Yarn 2+ (Berry) breaks vsce package because of node_modules layout. Use npm or yarn 1.x for extension projects.

Create the publisher in Azure DevOps

30 min

A publisher is the brand you ship under. Publishers are managed in the Visual Studio Marketplace publisher hub but the identity backs onto an Azure DevOps organisation, which is where you mint the Personal Access Token. Pick the publisher ID carefully: it's PERMANENT and shows in every install URL (marketplace.visualstudio.com/items?itemName=publisher.extension).

Tasks
  • Sign in at https://dev.azure.com with the Microsoft account you'll use for publishing
  • Create or pick an Azure DevOps organisation (free tier is fine)
  • Visit https://marketplace.visualstudio.com/manage and Create publisher
  • Pick the publisher ID (lowercase, alphanumeric + hyphens, PERMANENT)
  • Set the display name + brief description (these can change later)
  • Verify the publisher email so the listing can publish
Gotchas
  • The publisher ID is PERMANENT. You cannot rename it once an extension is published under it. Pick a name you'll be happy with in 5 years.
  • If you sign in with a personal Microsoft account that's also tied to a work tenant, Azure DevOps may default to the work tenant. Switch to the personal directory or your tokens won't work.

Mint a Personal Access Token with the right scopes

15 min

vsce publishes by sending a Personal Access Token (PAT) from Azure DevOps. The PAT MUST be created with Organization scope set to All accessible organizations and the Marketplace → Manage scope. Any other scope set is silently rejected by vsce with an unhelpful error.

Tasks
  • https://dev.azure.com/<org>/_usersSettings/tokens → New Token
  • Set Organization to All accessible organizations (NOT a single org)
  • Set expiration (90 days max for default, you can extend to a year)
  • Click 'Show all scopes', tick Marketplace → Manage (NOT just Acquire)
  • Copy the PAT — it shows ONCE; lose it and you mint a new one
  • Save it in your secrets manager (1Password, Bitwarden, GitHub Actions Secrets)
Gotchas
  • PAT scope must be Marketplace → MANAGE. Picking only 'Acquire' or 'Publish' fails silently with a 401 from vsce.
  • Organization scope must be 'All accessible organizations'. A single-org PAT cannot publish.
  • PATs expire. Calendar a renewal at 80 days or your CI publish breaks at the worst time.

Configure package.json for the Marketplace

1-2 hr

The Marketplace listing is generated from package.json + README.md + CHANGELOG.md + the icon file. Required fields: publisher, name, displayName, description, version, engines.vscode, categories, icon (128x128 PNG), repository. Skipping any of these gives a vsce package warning that's actually a fail-on-publish.

Tasks
  • Set publisher (the ID you created in step 2)
  • Set name (lowercase, kebab-case, unique within publisher)
  • Set displayName (human-readable, shown on Marketplace)
  • Set description (one sentence, shown in search results)
  • Set version using semver (1.0.0 for first release)
  • Set engines.vscode to the minimum version your code requires (e.g. ^1.74.0)
  • Set categories from the official list (Programming Languages, Linters, Themes, etc.)
  • Add icon: a 128x128 PNG, referenced from package.json
  • Add repository (REQUIRED for the Marketplace's 'Repository' link)
  • Set bugs + homepage URLs
Gotchas
  • If repository is missing, vsce package warns but still publishes. The Marketplace listing has a broken Repo link until you re-publish with it set.
  • engines.vscode must be a real released version. ^1.999.0 fails review for users on stable.
  • Icons with alpha channel are fine here, unlike Apple. PNG, 128x128, transparent corners, ship as ./icon.png.

Minimise activationEvents (post-1.74 default)

1-2 hr

VS Code 1.74 changed the default: extensions now activate ONLY on events you explicitly declare. The old onStartupFinished catch-all is allowed but heavily discouraged because it slows VS Code startup for every user. Declare exactly the events your commands / languages / views need.

Tasks
  • List every command your extension contributes (in contributes.commands)
  • For each command, add onCommand:<id> to activationEvents (only if the command is the first interaction)
  • If you contribute a language, use onLanguage:<id> instead of onStartupFinished
  • If you contribute a view, use onView:<viewId>
  • If your extension is a theme: NO activation events needed; themes activate lazily
  • Run the Extension Development Host with --inspect-extensions to confirm activation timing
Gotchas
  • onStartupFinished forces the extension to load on every VS Code window open, slowing startup for every user. Marketplace surfaces this in the 'Extension Activation Time' chart.
  • If your extension uses workspaceContains:<glob>, write a tight glob. workspaceContains:** activates on every workspace and is treated like onStartupFinished.
  • If you contribute a theme + commands: ONLY declare onCommand events. Themes don't need activation; the user picking your theme triggers it lazily.
Agent prompt for this step
Audit this VS Code extension's source code + package.json and produce the minimum-correct activationEvents list.

Rules:
1. For every command in contributes.commands: include onCommand:<id> only if the user can invoke that command from the Command Palette without already activating the extension some other way (e.g. opening a relevant file).
2. For every language in contributes.languages: include onLanguage:<id>.
3. For every view in contributes.views: include onView:<viewId>.
4. For every webview / authentication / debug provider: use the matching activation event.
5. NEVER include "*" or onStartupFinished unless absolutely necessary, and explain why if you do.

Output the new activationEvents array + a one-line justification per event.

Write a README that doubles as the Marketplace listing

3-5 hr

The Marketplace listing IS your README.md, rendered as GitHub-flavoured markdown. Hero image, animated GIF demo, install instructions, configuration table — all from the README. Spend an afternoon on this; it's the only thing standing between a curious user and an install.

Tasks
  • Hero section: extension name + 1-line value prop + animated GIF demo
  • Features section: 3-5 bullets, each with a screenshot or GIF
  • Configuration section: table of every setting in contributes.configuration
  • Commands section: table of every command in contributes.commands
  • Requirements section: VS Code version + any external tools (Node, a CLI, etc.)
  • Known issues + release notes link to CHANGELOG.md
  • License at the bottom + link to GitHub repo
  • Run vsce package locally and open the .vsix in VS Code to preview the listing
Gotchas
  • Relative image paths in README work locally but break on the Marketplace. Use absolute GitHub raw URLs (https://raw.githubusercontent.com/...) for any image referenced in README.md.
  • Animated GIFs over 4 MB don't autoplay smoothly on the Marketplace. Compress aggressively (gifski, ezgif).
  • The Marketplace renders only the README as the description. Markdown links work, JS doesn't, embedded video doesn't.
Agent prompt for this step
Draft a README for this VS Code extension that doubles as the Marketplace listing.

Read package.json + the source. Output:

1. Hero: extension name + one-sentence value prop. Slot for a GIF demo.
2. Features: 3-5 bullets, each with a placeholder for a screenshot.
3. Configuration: a markdown table of every setting in contributes.configuration with name, type, default, and one-line description.
4. Commands: a markdown table of every command in contributes.commands with id, title, and where to invoke (Command Palette / context menu / shortcut).
5. Requirements: VS Code engine version + external tools.
6. Release Notes: link to CHANGELOG.md.
7. License + repo link.

Tone: developer-to-developer. No "revolutionary", no exclamation marks. Lead with what the extension does, then how to use it.

Package and publish with vsce

30 min for first publish

vsce package builds a .vsix; vsce publish uploads it to the Marketplace. Either bump version manually (vsce publish minor) or bump first then publish (npm version minor && vsce publish). The first publish on a new publisher takes 5-15 minutes to appear in search.

Tasks
  • npm i -g @vscode/vsce
  • Run vsce package locally to get a .vsix and inspect its contents
  • Run vsce publish (or vsce publish minor / patch / major to bump first)
  • When prompted, paste the PAT from step 3
  • Optional: vsce login <publisher> once to cache the PAT for future publishes
  • Verify the listing at https://marketplace.visualstudio.com/items?itemName=<publisher>.<name>
  • Install the extension in a clean VS Code profile to confirm the install path
Gotchas
  • .vscodeignore is critical. Without it, your .vsix bundles node_modules + .git + tests and uploads 50+ MB. Keep it under 1 MB for theme/snippet packs, under 5 MB for most extensions.
  • The first publish takes 5-15 min to appear in search. Don't panic-resubmit.
  • If you change the publisher field after first publish, you cannot re-publish under the new publisher. Pick once.

Cross-publish to Open VSX for VSCodium + Cursor users

30 min

The Visual Studio Marketplace's terms of service prohibit non-Microsoft IDE forks (VSCodium, Cursor, Gitpod, Theia) from using it. Open VSX is the open-source mirror those forks pull from. Publishing to both takes one extra command and reaches a much larger fork audience.

Tasks
  • Sign up at https://open-vsx.org with your GitHub account
  • Create a namespace matching your VS Code Marketplace publisher ID
  • Mint an Eclipse Open VSX access token from the User Settings page
  • npm i -g ovsx
  • Run ovsx publish path/to/your.vsix -p <token>
  • Verify the listing at https://open-vsx.org/extension/<publisher>/<name>
  • Add ovsx publish to your CI workflow next to vsce publish
Gotchas
  • Open VSX namespaces are first-come-first-serve. Claim yours when you create your VS Code publisher, even if you don't publish there day one.
  • VSCodium + Cursor pull from Open VSX by default. If you only ship to the VS Code Marketplace, those users see your extension as 'unavailable'.
  • If your extension uses a Microsoft-only API (proposed APIs, walkthroughs from internal templates), it may run on VS Code but fail on VSCodium. Test on both.

Set up CI to publish on tag + write a CHANGELOG

1-2 hr

Manually running vsce publish from your laptop is fine for v1. For ongoing maintenance, push the publish step into GitHub Actions: on every git tag like v1.0.1, run npm ci && vsce publish && ovsx publish, with the PAT + Open VSX token in repo Secrets. Add a CHANGELOG.md the Marketplace renders under 'Changelog'.

Tasks
  • Create .github/workflows/publish.yml triggered on tag push (v*.*.*)
  • Add VSCE_PAT + OVSX_PAT to repo Secrets
  • Workflow steps: checkout, npm ci, npm test, vsce publish, ovsx publish
  • Adopt CHANGELOG.md (Keep a Changelog format works well: ## [1.0.1] - 2026-04-28 + Added/Changed/Fixed sections)
  • Configure auto-changelog or release-please if you don't want to maintain it manually
  • Tag v1.0.1 locally + push: git tag v1.0.1 && git push --tags — confirm CI publishes both
Gotchas
  • Secrets in GitHub Actions are not exposed to PRs from forks by default. Your publish workflow must run on push to main or on tag, never on PR.
  • vsce publish will refuse if the version in package.json equals an existing version on the Marketplace. Tag-based workflows must npm version <bump> before vsce publish OR fail loudly.
  • Open VSX has a separate failure mode (rate limit, namespace mismatch). Don't make the workflow fail-fast on ovsx if vsce already succeeded; the Marketplace publish is your hot path.
Hand the template to your agent

Workspace-wide agent prompt.

Paste this into your agent's permanent system prompt so the agent reads, writes, and maintains the template's surfaces as you work through the steps.

Agent system prompt
You are an agent on the "Ship a VS Code extension" playbook workspace at your-org/ship-a-vscode-extension.

Your role: maintain the four surfaces (Steps, Pointers, Brief, Release log) as the user works through the 9-step playbook.

Cadence:
- When the user marks a step Done, append a line to the Brief.
- When a publish runs (vsce publish + ovsx publish), append a row to Release log: version, date, both store URLs, install count delta.
- Mirror new pointers (links) from steps into the Pointers table.

First MCP tool calls:
1. list_surfaces(workspace_slug="ship-a-vscode-extension")
2. list_rows(workspace_slug="ship-a-vscode-extension", surface_slug="steps")
3. get_doc(workspace_slug="ship-a-vscode-extension", surface_slug="brief")

Do NOT modify canonical step titles. Append substeps as new rows.
FAQ

Common questions on this template.

How long does VS Code Marketplace 'review' take?
There is no human review for most extensions. Once vsce publish completes, the listing appears in 5-15 min for new publishers, instantly for existing publishers. Microsoft does occasionally pull extensions for security or policy issues post-publish (e.g. impersonating popular extension names) so don't take 'no review' as 'no consequences'.
Do I need a publisher account before I can develop?
No. You can build and run the extension locally (F5 in VS Code) without ever creating a publisher. The publisher account + Azure DevOps PAT are only required at publish time. Many devs sit on a working .vsix for days before they create the publisher.
Why won't my PAT work?
Three reasons in order of frequency: (1) Organization scope is set to a single org instead of 'All accessible organizations', (2) Marketplace scope is set to Acquire instead of Manage, (3) the PAT belongs to a Microsoft account that's signed into a different Azure DevOps directory than your publisher. Re-mint the PAT with All accessible organizations + Marketplace Manage and try again.
Should I publish to Open VSX too?
Yes, if you want VSCodium / Cursor / Gitpod / Theia users to install you. The VS Code Marketplace ToS bans those forks from using it, so they pull from Open VSX. The publish flow is one extra command (ovsx publish) and reaches an audience that grows every quarter.
Can my AI agents help with the extension?
Yes. Agents are particularly useful for: drafting the README that becomes the Marketplace listing, writing a minimum activationEvents list from a code audit, drafting the CHANGELOG from git log, configuring the GitHub Actions publish workflow. The playbook ships agent prompts inline.

Open this template as a workspace.

We mint a fresh copy in your org with the steps as table rows, the pointers as a separate table, and the brief as a doc. Bring your agents, start checking off boxes.