# How to Create and Publish Custom OpenWork Skills for Team Reuse

> Learn how to create and publish custom OpenWork skills for team reuse. Author skills locally, package with Den API, and grant access to your teams.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-04

---

**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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/packages/types/src/desktop-ipc.ts) (lines 179–212) within the `LocalSkillContent` interface.

```bash
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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`.

```typescript
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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/routes/org/plugin-system/store.ts) (lines 101–103).

```typescript
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`](https://github.com/different-ai/openwork/blob/main/prds/skill-sharing/grant-native-skill-sharing.md)).

```bash

# 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:

```bash
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`](https://github.com/different-ai/openwork/blob/main/SKILL.md) files with proper front-matter in `/.opencode/skills/`, scanned automatically by [`desktop-ipc.ts`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/builtin-skills.ts).
- **Control access** through `POST /v1/plugins/:pluginId/access`, which writes grants to `PluginAccessGrantTable` managed by [`store.ts`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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.