How to Publish and Manage Skills and Plugins in the OpenWork Marketplace

Publishing skills and plugins in OpenWork requires creating a Cloud plugin, attaching it to a marketplace via the Den API, and configuring access grants through the builtin skill system.

The OpenWork marketplace, powered by the Den API MCP agent, treats every plugin as a search-capable capability. Understanding how to publish and manage skills and plugins in the OpenWork marketplace involves mastering the builtin skill workflows, grant-based visibility controls, and the search_capabilities/execute_capability interface.

Architecture Overview

The Den API and Capability Model

OpenWork’s marketplace architecture centers on the Den API, which implements an MCP agent pattern for capability discovery and execution. Every published plugin functions as a searchable capability with a standardized naming convention.

In ee/apps/den-api/src/mcp/marketplace-capabilities.ts, the system constructs capability identifiers using buildMarketplaceCapabilityName, producing strings in the format plugin:<plugin-id>:<config-object-id>. When an agent calls search_capabilities, the Den API merges three distinct sources: the native REST catalog, external MCP connections, and the Marketplace source.

Execution flows through executeMarketplaceCapability in the same file. This function resolves the plugin and config object, validates access through grantRole and resolvePluginArchGrantRole, loads the latestVersion, and returns either an instructional payload for skills or status hints for items requiring connections.

Grant-Based Visibility Control

Visibility operates through a cascading grant system defined across three database tables. The filterVisibleRows helper evaluates these grants to determine capability accessibility.

Grant Table Protection Scope Validation Method
MarketplaceAccessGrantTable Marketplace-level access grantRole on marketplace rows
PluginAccessGrantTable Entire plugin and contents grantRole on plugin rows
ConfigObjectAccessGrantTable Individual config objects grantRole on config-object rows

Plugins without marketplace attachments remain grant-only capabilities. The system reports unsynced content with content_not_synced status and provides contentNotSyncedHint guidance.

MCP Server Requirements

For config objects of type mcp, the Den API evaluates declared MCP servers through mcpDependenciesForObject. The functions marketplaceRequirementAction and statusForRequirement determine whether capabilities are ready for execution, need external connections, or require administrative setup.

Publishing Skills Using Builtins

OpenWork provides four builtin skills defined in ee/apps/den-api/src/mcp/builtin-skills.ts that automate the publishing workflow. These capabilities expose constants like BUILTIN_CREATE_SKILL_CAPABILITY and BUILTIN_ADD_TO_MARKETPLACE_CAPABILITY.

Create-Skill Capability

The Create-Skill capability (skill:create-skill) initiates the publishing pipeline by creating a new Cloud plugin containing a single skill config object. It invokes the postPlugins endpoint and returns both the plugin ID and config object ID required for subsequent steps.

Add-To-Marketplace Capability

The Add-To-Marketplace capability (skill:add-to-marketplace) attaches existing plugins to marketplaces using the postMarketplacesPlugins API. This establishes the link necessary for search_capabilities to discover the plugin through the Marketplace source.

Share-Plugin and Access Management

Two capabilities handle distribution permissions:

  • Share-Plugin (skill:share-plugin): Grants person, team, or organization-wide access to a plugin via postPluginsAccess
  • Add-User-To-Marketplace (skill:add-user-to-marketplace): Grants marketplace-level access without exposing specific plugins

Both update the respective grant tables and validate through getPluginsAccess or getMarketplacesResolved.

Step-by-Step Publishing Workflow

1. Create the Plugin

Execute the Create-Skill capability to establish the plugin container:

{
  "name": "openwork-cloud_execute_capability",
  "capability": "skill:create-skill",
  "body": {}
}

The response contains result.plugin (the plugin UUID) and result.configObject (the skill UUID).

2. Attach to Marketplace

Invoke Add-To-Marketplace with the IDs from step 1:

{
  "name": "skill:add-to-marketplace",
  "capability": "skill:add-to-marketplace",
  "body": {
    "marketplaceId": "<marketplace-uuid>",
    "pluginId": "<plugin-uuid>"
  }
}

Verify publication using getMarketplacesResolved.

3. Configure Access Grants

Run Share-Plugin to distribute access:

{
  "name": "skill:share-plugin",
  "capability": "skill:share-plugin",
  "body": {
    "pluginId": "<plugin-uuid>",
    "orgMembershipId": "om_12345",
    "role": "viewer"
  }
}

Alternatively, use Add-User-To-Marketplace for marketplace-wide permissions.

4. Discovery and Execution

Agents discover published skills through search_capabilities queries. Executing a capability name like plugin:<plugin-id>:<config-object-id> via execute_capability returns the skill's Markdown source and provenance metadata.

Code Implementation Examples

Creating a Skill via Node.js

const fetch = require('node-fetch');

async function createSkill(title, mdBody) {
  const skillMd = `---\nname: ${title}\ndescription: ${title}\n---\n${mdBody}`;
  const resp = await fetch('https://api.openworklabs.com/mcp/agent/execute_capability', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: 'skill:create-skill',
      capability: 'skill:create-skill',
      body: {}
    })
  });
  const { result } = await resp.json();
  console.log('Created plugin', result.plugin, 'configObject', result.configObject);
  return result;
}

Publishing to a Marketplace

async function publishToMarketplace(pluginId, marketplaceId) {
  const resp = await fetch('https://api.openworklabs.com/mcp/agent/execute_capability', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: 'skill:add-to-marketplace',
      capability: 'skill:add-to-marketplace',
      body: {
        marketplaceId: marketplaceId,
        pluginId: pluginId
      }
    })
  });
  return resp.json();
}

Granting Team Access

async function grantTeamAccess(pluginId, teamId, role = 'viewer') {
  const resp = await fetch('https://api.openworklabs.com/mcp/agent/execute_capability', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: 'skill:share-plugin',
      capability: 'skill:share-plugin',
      body: {
        pluginId: pluginId,
        teamId: teamId,
        role: role
      }
    })
  });
  return resp.json();
}

Searching and Executing Capabilities

async function findAndExecuteSkill(query) {
  // Search capabilities
  const search = await fetch(`https://api.openworklabs.com/mcp/agent/search_capabilities?q=${query}`);
  const { matches } = await search.json();
  const pluginCap = matches.find(m => m.name.startsWith('plugin:'));
  
  if (!pluginCap) throw new Error('Skill not found');
  
  // Execute capability
  const exec = await fetch('https://api.openworklabs.com/mcp/agent/execute_capability', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: pluginCap.name })
  });
  const { result } = await exec.json();
  return result.content;
}

Key Source Files and Functions

According to the different-ai/openwork source code, these files implement the marketplace publishing system:

Summary

  • Create plugins using the skill:create-skill builtin capability, which generates a Cloud plugin and config object via postPlugins
  • Publish to marketplaces by executing skill:add-to-marketplace, which creates the marketplace link through postMarketplacesPlugins
  • Control visibility through cascading grants checked by filterVisibleRows across MarketplaceAccessGrantTable, PluginAccessGrantTable, and ConfigObjectAccessGrantTable
  • Discover capabilities via search_capabilities, which identifies marketplace entries using the plugin:<id>:<id> naming convention
  • Execute skills through execute_capability, which resolves grants, loads latestVersion, and returns instructional payloads or connection hints

Frequently Asked Questions

How does the Den API verify access to marketplace plugins?

The Den API verifies access through the grantRole and resolvePluginArchGrantRole functions defined in the plugin system access routes. These functions check three hierarchical grant tables: marketplace-level grants, plugin-level grants, and config-object-level grants. The filterVisibleRows helper in marketplace-capabilities.ts applies these checks during the search_capabilities call to ensure agents only discover authorized capabilities.

What is the difference between grant-only plugins and marketplace-published plugins?

Grant-only plugins exist in the system and are searchable but lack marketplace attachments, causing the UI to display an objectHint suggesting local installation. Marketplace-published plugins appear in the Marketplace source merged by the Den API in ee/apps/den-api/src/mcp/agent.ts. Both types require proper grants from PluginAccessGrantTable, but only marketplace plugins receive the full distribution benefits of the marketplace discovery layer.

How does the system handle skills requiring external MCP connections?

When executing capabilities of type mcp, the Den API evaluates requirements through mcpDependenciesForObject and marketplaceRequirementAction. If external MCP connections are missing, executeMarketplaceCapability returns a needs_connection status and a hint containing openworkYourConnectionsUrl. The capability remains unavailable for execution until an administrator configures the required connection bindings in plugin-mcp-requirement-bindings.ts.

Can I update a skill after publishing it to the marketplace?

Yes. The Den API always loads the latestVersion when executing capabilities through executeMarketplaceCapability. To update a skill, modify the config object's content or Markdown source, which creates a new version. The marketplace capability name (plugin:<plugin-id>:<config-object-id>) remains constant, so existing grants and marketplace links continue functioning while agents receive the updated content on subsequent execute_capability calls.

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 →