OpenWork Marketplace Architecture Explained: How to Publish Plugins and Share Skills Across Teams

OpenWork's marketplace architecture consists of a Den control-plane API for storing plugin metadata, marketplace objects that organize skills and MCP connections, team-based access controls, and automatic client-side synchronization that delivers published plugins to teammates without manual installation.

The OpenWork marketplace is the central hub that makes skills, plugins, MCP connections, and LLM providers discoverable and shareable across a team or entire organization. In packages/openwork-bootstrap/bin/openwork.mjs (lines 571-615), the desktop client implements the marketplace sync logic that pulls available catalogs on startup. This article breaks down the architecture using concrete source code references and shows exactly how to publish plugins through both the UI and programmatically.

How the OpenWork Marketplace Architecture Works

The architecture follows a cloud-first, publish-once, consume-anywhere model. Five layers work together to deliver plugins from authors to end users.

Den Control-Plane API

The server-side service stores all marketplace metadata. It exposes REST endpoints consumed by the desktop client and automation scripts.

According to packages/docs/openapi.json, the marketplace-related paths include:

  • GET /v1/marketplaces — list accessible marketplaces
  • POST /v1/marketplaces — create new marketplace
  • GET /v1/plugins and POST /v1/plugins — plugin operations

The OpenAPI specification defines the complete JSON schema for marketplace and plugin objects, including required fields like name, description, marketplaceId, and nested skills arrays.

Marketplace Model Structure

A marketplace object holds a collection of plugins. Each plugin bundles:

Component Purpose Schema Location
Skills Runnable runbooks (executable code) plugin.skills[] in OpenAPI spec
MCP definitions External service connections (Slack, Google, etc.) plugin.mcpConnections[]
Metadata Name, description, version, author info Top-level plugin properties

The marketplaceId field links every plugin to its parent marketplace. This hierarchical structure enables scoped permission grants at the marketplace level.

Desktop Client Integration

The Electron/React UI reads marketplace data on startup. In openwork.mjs, the client-side request helper implements the sync:

// From packages/openwork-bootstrap/bin/openwork.mjs (lines ~571-615)
// Simplified extraction showing the core pattern

async function syncMarketplaces(apiBase) {
  const response = await request(apiBase, '/v1/marketplaces', {
    method: 'GET',
  });
  // Response cached and rendered in Settings → Extensions → Marketplace
  return response.body.items;
}

The UI components in packages/ui/src/react/extensions/ render the marketplace list and plugin creation dialogs. Users experience this as Settings → Extensions → Marketplace.

Team-Based Access Control

Organizations use teams and roles to gate marketplace visibility. The Den API enforces permissions through endpoints like /v1/organizations/:orgId/teams/:teamId/access.

As documented in packages/docs/cloud/team-quickstart.mdx, access grants are cached client-side. When a teammate's team gains marketplace access, plugins appear automatically—no reinstall required.

Cloud-Only Delivery Model

Once published to a marketplace, plugins propagate to all authorized clients on next startup. This behavior is illustrated in evals/voiceovers/marketplace-connect-only-delivery.md: the author publishes once, and teammates see the plugin immediately in their desktop UI.

How to Publish Plugins to the OpenWork Marketplace

Publishing follows four steps whether using the Cloud dashboard or desktop app.

Step 1: Create an Organization Marketplace

If no marketplace exists for your team:

  1. Navigate to Cloud dashboard → Extensions → Marketplaces
  2. Click New marketplace, enter a name, save
  3. The UI calls POST /v1/marketplaces with your organization context

The marketplace entry persists in Den's control plane with a unique id.

Step 2: Create a Plugin Inside the Marketplace

In the desktop app:

  1. Go to Extensions → Plugins, click New plugin

  2. Select your marketplace from the dropdown (pre-selected if only one exists)

  3. Fill name, description, and add skills:

    • Skill name: identifier for invocation
    • Skill description: shown in UI tooltips
    • Skill run: the executable JavaScript/TypeScript code
  4. Optionally attach MCP connections for external API access

Step 3: Save and Publish

Clicking save triggers:

// Equivalent HTTP call made by the UI
POST /v1/plugins
Content-Type: application/json

{
  "name": "Sales-Call-Prep",
  "description": "Generate agendas from briefs",
  "marketplaceId": "mkt_abc123xyz",
  "skills": [
    {
      "name": "generateAgenda",
      "description": "Creates bullet-point agenda",
      "run": "function run(context) { /* implementation */ }"
    }
  ],
  "mcpConnections": []
}

Success returns HTTP 201 with the plugin id. The plugin is immediately visible to teammates with marketplace access.

Step 4: Grant Access (Optional)

For fine-grained control:

  • Extensions → Marketplaces → [Your Marketplace] → Members
  • Add teams or individual users
  • Changes propagate on client restart or manual sync

Programmatic Plugin Publishing

Use the same request helper as the CLI for automation. This Node.js example mirrors openwork.mjs internals:

// publish-plugin.mjs
import { request } from './packages/openwork-bootstrap/bin/openwork.mjs';

const API_BASE = 'https://api.openworklabs.com';

// 1. Locate or verify marketplace access
const markets = await request(API_BASE, '/v1/marketplaces', { method: 'GET' });
if (markets.status !== 200) throw new Error(`List failed: ${markets.status}`);

const targetMarket = markets.body.items.find(m => m.name === 'Engineering') 
                  || markets.body.items[0];

// 2. Publish plugin with embedded skill
const publish = await request(API_BASE, '/v1/plugins', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Incident-Response-Automation',
    description: 'Auto-create PagerDuty incidents from alerts',
    marketplaceId: targetMarket.id,
    skills: [
      {
        name: 'createIncident',
        description: 'Creates PagerDuty incident with context',
        run: `
          function run(ctx) {
            const { title, severity } = ctx.args;
            // PagerDuty API call implementation
            return { incidentId: 'PD-12345', url: '...' };
          }
        `.trim()
      }
    ],
    mcpConnections: [
      { service: 'pagerduty', scope: 'incidents:write' }
    ]
  })
});

if (publish.status !== 201) {
  throw new Error(`Publish failed: ${JSON.stringify(publish.body)}`);
}

console.log(`Published plugin: ${publish.body.id}`);

The request function handles authentication, retries, and error formatting identically to the desktop client's marketplace sync (see openwork.mjs lines 571-615).

Marketplace Architecture Flow: Complete Data Path


┌─────────────────┐     GET /v1/marketplaces      ┌─────────────────┐
│   Desktop App   │ ◄──────────────────────────── │  Den API Server │
│  (openwork.mjs) │                               │  (OpenAPI spec) │
└────────┬────────┘                               └────────┬────────┘
         │                                                │
         │  Renders marketplace list                      │  Stores:
         │  Settings → Extensions → Marketplace           │  - marketplace records
         ▼                                                │  - plugin definitions
┌─────────────────┐                                       │  - team access grants
│     User UI     │                                       │
│  (packages/ui)  │                                       │
└────────┬────────┘                                       │
         │                                                │
         │  Author publishes plugin                       │
         │  POST /v1/plugins                              │
         └──────────────────────────────────────────────► │
                                                          │
                              ┌─────────────────┐         │
                              │  Team Members   │ ◄───────┘
                              │  (auto-sync on  │   Access check:
                              │   next startup) │   team grants
                              └─────────────────┘

Key Source Files Reference

File Purpose
packages/openwork-bootstrap/bin/openwork.mjs Marketplace sync client, request helper, CLI implementation
packages/docs/openapi.json Complete REST API specification for marketplaces and plugins
packages/docs/cloud/team-quickstart.mdx Step-by-step UI guide for marketplace creation
evals/voiceovers/marketplace-connect-only-delivery.md Delivery model documentation and UX scenarios
packages/ui/src/react/extensions/ React components for marketplace UI (MarketplaceList, PluginEditor)

Summary

  • OpenWork marketplace architecture centers on Den control-plane APIs that store plugin metadata and enforce team-based access controls
  • Marketplace objects contain plugins, which bundle skills (runnable code) and optional MCP connections
  • Desktop clients sync marketplace content automatically via request() calls in openwork.mjs (lines 571-615)
  • Publish-once delivery means plugins appear for all authorized teammates without manual installation steps
  • Programmatic publishing uses the same request helper and POST /v1/plugins endpoint as the UI

Frequently Asked Questions

What is the minimum required to publish a plugin?

A marketplace, a name, and at least one skill with a run function. The description and mcpConnections fields are optional. The OpenAPI schema in packages/docs/openapi.json marks only name, marketplaceId, and skills as required.

How do teammates receive published plugins automatically?

The desktop client calls GET /v1/marketplaces on every startup. If a teammate's team has marketplace access, the response includes current plugins. The UI renders these without requiring manual install steps. This sync logic is in packages/openwork-bootstrap/bin/openwork.mjs.

Can plugins be published without the Cloud dashboard?

Yes. Use the programmatic example above with the request helper from openwork.mjs. Any HTTP client that authenticates to the Den API and calls POST /v1/plugins with valid JSON can publish.

What limits access to a marketplace's plugins?

Team grants. In Extensions → Marketplaces → Members, owners add teams or users. The Den API checks these grants on every marketplace list request. Users without grants never receive those plugins in their sync response.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →