How to Create and Publish Custom OpenWork Skills for Team Reuse

To create and publish custom OpenWork skills for team reuse, author a markdown file locally in /.opencode/skills/, package it into a plugin via the Den API POST /v1/plugins, and grant access to teams or organizations using POST /v1/plugins/:pluginId/access.

OpenWork treats a skill as reusable instructional markdown that teams can invoke through the Model Context Protocol (MCP). According to the different-ai/openwork source code, making a skill available to your entire organization requires three logical steps: local authoring, cloud publication, and access grants.

Authoring Skills Locally

Start by creating a markdown file named SKILL.md inside a folder under /.opencode/skills/. The desktop application automatically scans this directory on startup and registers each skill as a local capability.

The file must begin with a YAML front-matter block containing name and description fields, followed by the instructional content. This structure is defined in packages/types/src/desktop-ipc.ts (lines 179–212) within the LocalSkillContent interface.

mkdir -p .opencode/skills/weather-alerts

cat > .opencode/skills/weather-alerts/SKILL.md <<'EOF'
---
name: weather-alerts
description: Gives a short weather forecast for a city.
---
Return a concise weather forecast for {{city}}.
EOF

The readLocalSkill function in desktop-ipc.ts parses these files and makes them available through the local IPC channel, allowing immediate testing before cloud publication.

Publishing Skills to the Cloud

Once authored locally, package the skill into a plugin—a versioned bundle of capabilities—using the Den REST API. The create-skill builtin implemented in ee/apps/den-api/src/mcp/builtin-skills.ts stores the markdown as a configObject of type skill, encrypting the raw text in the encryptedTextColumn of the ConfigObjectTable.

import { denFetch } from "@openwork/behaviors";
import { readFile } from "fs/promises";

const rawSourceText = await readFile(".opencode/skills/weather-alerts/SKILL.md", "utf8");

const createRes = await denFetch(session, "/v1/plugins", {
  method: "POST",
  headers: { 
    authorization: `Bearer ${session.token}`, 
    "x-openwork-org-id": orgId 
  },
  body: JSON.stringify({
    name: "Weather Alerts",
    components: [{ type: "skill", input: { rawSourceText } }],
  }),
});

const pluginId = createRes.body.item.id;

This pattern is demonstrated in the integration test evals/specs/skill-grant-access.test.ts (lines 56–68), which validates the end-to-end flow from creation to execution.

Granting Team Access

After creating the plugin, share it using the plugin access endpoint. The grant system stores permissions in PluginAccessGrantTable and ConfigObjectAccessGrantTable, as implemented in ee/apps/den-api/src/routes/org/plugin-system/store.ts (lines 101–103).

await denFetch(session, `/v1/plugins/${encodeURIComponent(pluginId)}/access`, {
  method: "POST",
  headers: { 
    authorization: `Bearer ${session.token}`, 
    "x-openwork-org-id": orgId 
  },
  body: JSON.stringify({
    teamId: "team-xyz",
    role: "viewer",
  }),
});

Grants can be issued at the organization, team, or individual member level. Once granted, the skill appears in every member’s capability search results without requiring additional tool definitions or client updates.

Discovering and Executing Skills via MCP

Team members discover granted skills through the search_capabilities MCP tool and execute them via execute_capability. The MCP handler searchMarketplaceCapabilities joins the plugin-grant tables to return only authorized skills, as outlined in the grant-native skill sharing design document (prds/skill-sharing/grant-native-skill-sharing.md).


# Search for available skills

curl -X POST https://api.openworklabs.com/mcp/agent \
  -H "Authorization: Bearer $MCP_TOKEN" \
  -d '{
    "jsonrpc":"2.0",
    "id":1,
    "method":"tools/call",
    "params":{
      "name":"search_capabilities",
      "arguments":{
        "query":"weather",
        "type":"skills",
        "limit":10
      }
    }
  }'

The response contains capability names formatted as plugin:<pluginId>:<configObjectId>. Executing the skill returns the original markdown plus provenance metadata:

curl -X POST https://api.openworklabs.com/mcp/agent \
  -H "Authorization: Bearer $MCP_TOKEN" \
  -d '{
    "jsonrpc":"2.0",
    "id":2,
    "method":"tools/call",
    "params":{
      "name":"execute_capability",
      "arguments":{
        "name":"plugin:<pluginId>:<skillId>"
      }
    }
  }'

The agent receives the rawSourceText along with context indicating the content originates from your organization’s library, then processes the markdown instructions accordingly.

Summary

  • Author locally by placing SKILL.md files with proper front-matter in /.opencode/skills/, scanned automatically by desktop-ipc.ts.
  • Publish via API by posting to /v1/plugins with the skill markdown as a component, handled by the create-skill builtin in builtin-skills.ts.
  • Control access through POST /v1/plugins/:pluginId/access, which writes grants to PluginAccessGrantTable managed by store.ts.
  • Execute universally using standard MCP tools search_capabilities and execute_capability, which resolve granted skills without requiring new tool definitions.

Frequently Asked Questions

What file format does OpenWork expect for skills?

OpenWork expects a markdown file named SKILL.md containing a YAML front-matter block with name and description fields, followed by instructional content. The desktop IPC implementation in packages/types/src/desktop-ipc.ts validates this format when loading local skills.

How does the MCP determine which skills a user can access?

The MCP handler searchMarketplaceCapabilities joins the PluginAccessGrantTable and ConfigObjectAccessGrantTable to filter results based on the user’s organization, team memberships, and individual grants. Only skills with active grants appear in search_capabilities responses.

Can skills be version controlled before publishing?

Yes. Since skills are plain markdown files stored in /.opencode/skills/, you can track them in Git or any version control system before packaging them into immutable plugin versions via the Den API.

What role is required to grant skill access to a team?

Any user with manager or admin privileges for the plugin can grant access via POST /v1/plugins/:pluginId/access. The grant system supports viewer, editor, and manager roles, with the latter two providing increasing levels of modification rights over the plugin contents.

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 →