dist/cursor/medusa-commerce/skills/medusa-plugins/SKILL.md
Develop and publish Medusa v2 plugins — plugin structure, plugin vs module comparison, npm packaging, and reusable plugin template. Use when building distributable Medusa extensions.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins medusa-pluginsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://docs.medusajs.com/learn/fundamentals/plugins for plugin overviewsite:docs.medusajs.com plugin vs module for comparisonsite:docs.medusajs.com create medusa plugin for plugin scaffoldingsite:docs.medusajs.com plugin options configuration for plugin options patternsite:docs.medusajs.com publish plugin npm for packaging and distribution| Aspect | Module | Plugin |
|--------|--------|--------|
| Scope | Single concern (data + service) | Full feature (modules + routes + workflows + admin) |
| Location | src/modules/ in project | Separate npm package |
| Distribution | Not distributable | Published to npm |
| Configuration | Registered in modules array | Registered in plugins array |
| Contains | Models, service, loaders | Modules, API routes, workflows, subscribers, admin, links |
| Use case | Project-specific domain | Reusable across projects |
A plugin is essentially a packaged Medusa project that can contain all the same src/ directories.
| Directory | Contents |
|-----------|----------|
| src/modules/ | Custom modules (models, services) |
| src/workflows/ | Workflow definitions |
| src/api/store/, src/api/admin/ | API routes + middlewares.ts |
| src/subscribers/ | Event subscribers |
| src/admin/widgets/, src/admin/routes/ | Admin UI extensions |
| src/links/ | Module link definitions |
| Root | package.json, tsconfig.json, README.md |
A plugin mirrors the standard Medusa src/ directory structure — all conventions apply.
The plugin's package.json must include specific fields:
| Field | Value | Purpose |
|-------|-------|---------|
| name | medusa-plugin-my-feature | npm package name |
| main | ./dist/index.js | Compiled entry point |
| types | ./dist/index.d.ts | TypeScript declarations |
| files | ["dist", "!dist/**/*.map"] | Included in npm package |
| keywords | ["medusa-plugin"] | Discoverability |
| peerDependencies | @medusajs/framework | Medusa version compatibility |
// src/index.ts
// Fetch live docs for plugin export shape
// Plugin entry exports nothing directly --
// Medusa discovers modules, routes, etc. by convention
export default {}
Plugins receive configuration options from the consuming project:
// In consuming project's medusa-config.ts plugins array
// Fetch live docs for plugin registration options
{
resolve: "medusa-plugin-my-feature",
options: { apiKey: process.env.MY_FEATURE_API_KEY },
}
// In a plugin loader or service
// Fetch live docs for accessing plugin options from container
// Options are injected via the module/plugin options mechanism
| Step | Command | Purpose |
|------|---------|---------|
| Build | tsc or tsup | Compile TypeScript to dist/ |
| Watch | tsc --watch | Development rebuild on changes |
| Clean | rm -rf dist | Remove compiled output |
| Pack | npm pack | Create tarball for testing |
| Step | Command | Context |
|------|---------|---------|
| 1. Build plugin | npm run build | In plugin directory |
| 2. Create link | npm link | In plugin directory |
| 3. Link in project | npm link medusa-plugin-my-feature | In Medusa project |
| 4. Register | Add to plugins in medusa-config.ts | In Medusa project |
| 5. Migrate | npx medusa db:migrate | In Medusa project |
| Approach | Description |
|----------|-------------|
| Unit tests | Test services, workflow steps in isolation |
| Integration tests | Use medusa-test-utils to spin up a test Medusa instance |
| Local linking | Link plugin into a real Medusa project and test manually |
| CI pipeline | Build + unit tests on every push |
| Check | Why |
|-------|-----|
| Build succeeds | Consumers need compiled JS |
| files in package.json | Only ship dist/, not src/ |
| peerDependencies set | Avoid version conflicts |
| README with install instructions | Developer experience |
| .npmignore or files field | Exclude tests, config, etc. |
| License field set | Legal clarity |
| Version follows semver | Compatibility signals |
npm version patch # or minor / major
npm publish --access public
# Fetch live docs for Medusa plugin registry conventions
| Convention | Example |
|-----------|---------|
| npm package name | medusa-plugin-my-feature |
| Module key inside plugin | my-feature |
| API route prefix | /store/my-feature, /admin/my-feature |
| Admin widget files | src/admin/widgets/my-feature-widget.tsx |
medusa-plugin-* naming convention for npm discoverabilitypeerDependencies on @medusajs/framework and @medusajs/medusa -- do not bundle themdist/) -- never ship src/ or test filesnpm link against a real Medusa project before publishingFetch the Medusa plugin documentation for exact project structure, build configuration, and publishing requirements before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.