# How to Integrate Custom Modules into OpenWork: A Complete Plugin Development Guide

> Learn to integrate custom modules into OpenWork with this complete plugin development guide. Extend OpenWork's capabilities easily without modifying core code.

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

---

**OpenWork's plugin architecture lets you extend the platform by creating self-contained packages that declare capabilities, registering them with the Den server via the REST API, and invoking them via the skill runtime without modifying core code.**

The `different-ai/openwork` repository implements a flexible plugin system that enables developers to integrate custom modules seamlessly. Whether you need to add data connectors, AI skills, or UI widgets, understanding how to integrate custom modules into OpenWork allows you to extend functionality while keeping the core platform untouched.

## Understanding OpenWork's Plugin Architecture

OpenWork treats custom modules as **plugins**—self-contained packages that declare one or more **capabilities** (skills). The integration flow follows three distinct phases: creating the plugin package with its manifest and handlers, registering the metadata with the Den server, and consuming the capability through the runtime's `execute_capability` endpoint.

## Step 1: Create the Plugin Package

A valid plugin requires both a skill manifest and a capability handler. The manifest describes inputs, outputs, and metadata, while the handler contains the execution logic.

### Define the Skill Manifest

Create a [`skill.yml`](https://github.com/different-ai/openwork/blob/main/skill.yml) file that declares the capability interface. The manifest must specify the capability ID, input schema, and output schema according to the plugin schema defined in [`packages/types/src/dynamic-artifacts.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/dynamic-artifacts.ts).

```yaml

# skill.yml

name: My Plugin – Hello World
description: |
  Demonstrates how to expose a custom capability.
capability: my-plugin:hello-world
inputs:
  - name: name
    type: string
    description: Name of the person to greet
outputs:
  - type: string
    description: Greeting message

```

### Implement the Capability Handler

Write the execution logic in TypeScript using the `@openwork/skill-runtime` SDK. The handler runs inside the OpenWork agent sandbox and processes input parameters defined in the manifest.

```typescript
// my-plugin/src/index.ts
import { registerCapability } from '@openwork/skill-runtime';

registerCapability({
  id: 'my-plugin:hello-world',
  description: 'Returns a friendly greeting.',
  handler: async (input: { name: string }) => {
    return `👋 Hello, ${input.name}! – from MyPlugin`;
  },
});

```

### Add Optional UI Components

If your plugin provides user interface elements, export a React component and reference it in the skill manifest under `uiComponent`. OpenWork's UI loader dynamically imports the component when the skill is invoked.

```tsx
// my-plugin/src/ui/GreetingWidget.tsx
export const GreetingWidget = ({ name }: { name: string }) => (
  <div className="p-2 bg-gray-100 rounded">{`👋 Hello, ${name}!`}</div>
);

```

Update [`skill.yml`](https://github.com/different-ai/openwork/blob/main/skill.yml) to include the component path:

```yaml
uiComponent: "./ui/GreetingWidget"

```

## Step 2: Register the Plugin with the Den

The **Den server** stores plugin metadata and makes capabilities available to all OpenWork clients. Registration occurs via the Den API endpoint `POST /v1/plugins`, which accepts a payload matching the schema in [`packages/types/src/dynamic-artifacts.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/dynamic-artifacts.ts) containing fields such as `pluginId`, `artifactId`, and `artifactVersionId`.

Use the OpenWork CLI to register your plugin:

```bash
openwork den plugins create \
  --name "My Plugin" \
  --manifest ./skill.yml \
  --artifact ./my-plugin.tar.gz

```

Internally, the server persists this metadata and prepares the plugin for runtime loading.

## Step 3: Consume the Plugin in Agents

Once registered, OpenWork automatically loads the plugin at startup through the [`runtime-opencode-config-store.ts`](https://github.com/different-ai/openwork/blob/main/runtime-opencode-config-store.ts) module, which merges built-in and custom plugins into the runtime's capability registry. Agents invoke the new capability using the standardized format `skill:<pluginId>:<capability>`.

```typescript
import { executeCapability } from '@openwork/agent';

const result = await executeCapability(
  'skill:my-plugin:hello-world',
  { name: 'Alice' }
);
console.log(result); // → "👋 Hello, Alice! – from MyPlugin"

```

The [`apps/server/src/skills.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/skills.ts) file handles the underlying validation and routes the request to the `execute_capability` endpoint, ensuring the handler receives the correct input parameters.

## Debugging Plugin Integration

When troubleshooting registration or execution failures, consult the diagnostic events emitted from [`packages/types/src/agent-context-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/agent-context-diagnostics.ts). This module tracks plugin loading states, validation errors, and runtime exceptions, providing visibility into the integration pipeline without requiring core code modifications.

## Key Source Files and Implementation Details

Understanding the following files helps when debugging complex integrations:

- **[`packages/types/src/dynamic-artifacts.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/dynamic-artifacts.ts)**: Defines the JSON schema for plugin artifacts including `pluginId` and version identifiers.
- **[`apps/server/src/runtime-opencode-config-store.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-opencode-config-store.ts)**: Merges custom plugins into the runtime configuration at startup.
- **[`apps/server/src/skills.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/skills.ts)**: Validates skill manifests and exposes capabilities through the Den API.
- **[`packages/types/src/agent-context-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/agent-context-diagnostics.ts)**: Emits diagnostic events for plugin lifecycle monitoring.

## Summary

- Create a self-contained plugin package with a [`skill.yml`](https://github.com/different-ai/openwork/blob/main/skill.yml) manifest and TypeScript handler using `@openwork/skill-runtime`.
- Register the plugin via the Den API (`POST /v1/plugins`) or CLI, supplying metadata that matches the schema in [`dynamic-artifacts.ts`](https://github.com/different-ai/openwork/blob/main/dynamic-artifacts.ts).
- Invoke capabilities using the `skill:<pluginId>:<capability>` namespace through `executeCapability`.
- Debug integration issues using diagnostic events from [`agent-context-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/agent-context-diagnostics.ts).
- Extend functionality with optional React UI components declared in the skill manifest.

## Frequently Asked Questions

### What file format should I use for the skill manifest?

OpenWork supports YAML ([`skill.yml`](https://github.com/different-ai/openwork/blob/main/skill.yml)) or Markdown ([`skill.md`](https://github.com/different-ai/openwork/blob/main/skill.md)) formats for the skill manifest. The file must declare the capability ID, input parameters, output types, and optional UI component paths so the server in [`apps/server/src/skills.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/skills.ts) can validate and register the capability correctly.

### How does OpenWork validate custom plugins before loading them?

The Den server validates plugin metadata against the schema defined in [`packages/types/src/dynamic-artifacts.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/dynamic-artifacts.ts), checking required fields like `pluginId` and `artifactId`. The [`runtime-opencode-config-store.ts`](https://github.com/different-ai/openwork/blob/main/runtime-opencode-config-store.ts) module then merges valid plugins into the runtime configuration, while [`skills.ts`](https://github.com/different-ai/openwork/blob/main/skills.ts) verifies the skill manifest structure before exposing the capability via the `execute_capability` endpoint.

### Can I integrate plugins written in languages other than TypeScript?

While the examples use TypeScript, OpenWork's plugin architecture supports any language that can compile to JavaScript or run in the agent sandbox. The critical requirement is that the final artifact must export capabilities using the `registerCapability` interface or equivalent bindings, and the manifest must conform to the YAML schema expected by the Den server.

### Where can I find diagnostic logs if my plugin fails to load?

Diagnostic events for plugin registration, loading, and execution are emitted from [`packages/types/src/agent-context-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/agent-context-diagnostics.ts). These events provide detailed error messages about validation failures, missing artifacts, or runtime exceptions, allowing you to trace failures through the Den server logs or agent context outputs without modifying core OpenWork code.