# How OpenWork Extensions and Plugin Architecture Work: A Technical Deep Dive

> Explore the OpenWork extensions and plugin architecture. Discover how OpenCode plugins leverage a three-layer system for actions, queries, and UI commands in this technical deep dive.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-09

---

**OpenWork’s extension system is built on OpenCode plugins that expose affordances—actions, queries, and UI commands—to the desktop app through a three-layer architecture comprising extension manifests, a Den API plugin system, and transactional store logic.**

The `different-ai/openwork` repository implements a declarative, permission-checked plugin framework that allows agents to discover, create, and invoke extensions while maintaining secure access control. This architecture bridges the OpenCode agent runtime with the OpenWork desktop client, enabling seamless integration between AI agents and external tools.

## Extension Manifest and UI Bridge

OpenWork extensions are defined by **manifests** that declare capabilities, UI contributions, and lifecycle detection rules. When the desktop app starts, the `OpenWork-extensions-preview` plugin loads built-in manifests from `DEFAULT_OPENWORK_EXTENSION_MANIFESTS` defined in [`dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts).

Each manifest specifies:

- **Resources**: `opencode-plugin`, `mcp`, `tool`, `secret`
- **Contributions**: Settings panels, side-panels, and composer prompts
- **Enablement rules**: Environment variables, platform checks, and permission flags
- **Lifecycle detection**: Patterns like `detection: ["plugin:opencode-chrome-devtools"]`

### Agent-Facing Tools

Agents interact with extensions through three core tool functions exposed by the UI bridge:

```typescript
// Returns the full UI/affordance snapshot
openwork_context: { 
  description: "...", 
  async execute() { /* ... */ } 
}

// Read-only affordance query
openwork_query: { 
  description: "...", 
  args: openworkAffordanceRequestSchema.shape,
  async execute(rawArgs) { /* ... */ } 
}

// Mutating command execution
openwork_execute: { 
  description: "...", 
  args: openworkAffordanceRequestSchema.shape,
  async execute(rawArgs, context) { /* ... */ } 
}

```

The **UI bridge** (`uiBridgeRequest`) discovers running OpenWork desktop processes via a JSON file ([`openwork-ui-control.json`](https://github.com/different-ai/openwork/blob/main/openwork-ui-control.json)) in the user-specific config directory. This ensures agents operate against the actual client state rather than simulated browsers.

## Plugin System API (Den)

The **Den API** provides REST-like endpoints for plugin lifecycle management under [`dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts). All routes are wrapped with `withPluginArchOrgContext`, which injects role-based authorization checks.

### Core Endpoints

| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/v1/plugins` | POST | Create plugins with optional components and marketplace publishing |
| `/v1/plugins/:id` | GET | Retrieve plugin details including resolved memberships |
| `/v1/plugins/:id` | PATCH | Update name/description metadata |
| `/v1/plugins/:id/access` | POST | Grant access to members, teams, or org-wide |
| `/v1/me/library` | GET | List effective plugin library for the authenticated user |

### Authorization Context

Every request processes through `requirePluginArchCapability` and `requirePluginArchResourceRole` checks. The middleware injects an `actorContext` containing the current member, team list, session data, and organization information, ensuring all operations respect the `plugin_arch` permission model.

## Store Layer and Business Logic

The `createPluginBundle` function in [`dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts) serves as the core transaction engine for plugin creation.

### Bundle Creation Workflow

The function executes six atomic steps within a database transaction (`DbTransaction`):

1. **Capability validation**: Verifies `plugin.create` permission
2. **Component parsing**: Validates config object payloads (skills, agents, tools)
3. **Config persistence**: Creates entries in `ConfigObjectTable` and `ConfigObjectVersionTable`
4. **Plugin registration**: Inserts the plugin record into `PluginTable`
5. **Marketplace publishing**: Optionally adds to `MarketplacePluginTable`
6. **Org-wide distribution**: Checks `isPluginArchOrgAdmin` before granting universal access

### Serialization

The `serializePlugin` function returns a structured representation including extension metadata, marketplace summaries, and component counts (e.g., number of `skill`, `agent`, or `tool` objects). This serialization ensures consistent API responses across the plugin ecosystem.

## Access Control and Security Model

OpenWork implements **resource-grant tables** to model permissions: `PluginAccessGrantTable`, `ConfigObjectAccessGrantTable`, and related entities.

### Grant Types

- **`orgWide`**: Grants access to all organization members
- **`orgMembershipId`**: Specific member-level grants
- **`teamId`**: Team-scoped access

The helper `ensureGrantTargetsInOrganization` validates that grants reference only members and teams within the caller’s organization, preventing cross-org privilege escalation. All grant operations are atomic and validate against the `plugin_arch` role hierarchy.

## GitHub Import and MCP Integration

OpenWork supports importing **Claude-compatible plugins** from public GitHub repositories through an automated parsing pipeline.

### Import Process

The system executes these steps via [`dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts):

1. Parse repository tree using `getPublicGithubRepositoryTree`
2. Read [`plugin.json`](https://github.com/different-ai/openwork/blob/main/plugin.json) and [`marketplace.json`](https://github.com/different-ai/openwork/blob/main/marketplace.json) configuration files
3. Validate skill front-matter using `parseSkillMarkdown`
4. Create config objects for each discovered component
5. Optionally publish to the organization's marketplace

### MCP Configuration

Managed-Control-Plane (MCP) requirements follow the `pluginMcpRequirementConfigureSchema`. Administrators configure remote MCP servers (such as *Computer Use*), specify authentication types (`apikey` or `oauth`), and select credential modes (`per_member` or `shared`). The `configurePluginMcpConnectionResponse` endpoint stores these bindings in `PluginMcpRequirementBindingTable`.

## Code Examples

### Creating a Plugin via Den API

```typescript
// Assuming 'creator' holds a valid session token
const newPlugin = await denFetch(creator, "/v1/plugins", {
  method: "POST",
  json: {
    name: "Demo Plugin",
    description: "Shows how to create a plugin",
    components: [
      {
        type: "skill",
        input: {
          rawSourceText: `---\nname: demo-skill\ndescription: A demo skill.\n---\n\nReturn a greeting.`,
        },
      },
    ],
    orgWide: false,
  },
});

console.log("Created plugin ID:", newPlugin.body?.item?.id);

```

*Source:* [`dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts) – route registration (line 130); [`dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts) – `createPluginBundle` implementation (line 2608).

### Granting Member Access

```typescript
await denFetch(creator, `/v1/plugins/${pluginId}/access`, {
  method: "POST",
  json: {
    role: "viewer",
    orgMembershipId: memberId,
  },
});

```

*Source:* [`dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts) – `pluginAccess` handler (line 722).

### Querying Extension Manifests

```typescript
// Inside an OpenCode agent context
const ctx = await openwork_context.execute();
const plugins = JSON.parse(ctx).availableAffordances
  .filter(a => a.id.startsWith("plugin:"))
  .map(a => a.id);

console.log("Available plugins:", plugins);

```

*Source:* [`dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts) – `openwork_context` tool definition (line 200).

### Executing Plugin Actions

```typescript
await openwork_execute.execute({
  id: "extension.call",
  args: {
    extensionId: "google-workspace",
    action: "calendar.read",
    args: { start: "2024-01-01", end: "2024-01-31" },
  },
});

```

*Source:* [`dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/opencode-plugins/openwork-extensions-preview.ts) – `executeOpenworkAffordance` handling (line 560).

## Summary

- **OpenWork extensions** rely on OpenCode plugins exposing affordances through a manifest-based system defined in [`openwork-extensions-preview.ts`](https://github.com/different-ai/openwork/blob/main/openwork-extensions-preview.ts).
- **The Den API** provides CRUD endpoints for plugin lifecycle management under [`dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/routes.ts), gated by `plugin_arch` role-based authorization.
- **Transaction safety** is ensured by `createPluginBundle` in [`store.ts`](https://github.com/different-ai/openwork/blob/main/store.ts), which atomically creates config objects, plugin records, and marketplace entries.
- **Access control** uses resource-grant tables (`PluginAccessGrantTable`) supporting org-wide, member-specific, and team-scoped permissions.
- **GitHub integration** allows importing Claude-compatible plugins, while MCP configuration supports remote server authentication via `PluginMcpRequirementBindingTable`.

## Frequently Asked Questions

### What is an affordance in OpenWork's plugin architecture?

An **affordance** represents an action, query, or UI command that a plugin exposes to agents. Affordances are defined in extension manifests and accessed through the `openwork_query` (read-only) and `openwork_execute` (mutating) tool functions. The system treats affordances as the primary interface between AI agents and external capabilities.

### How does OpenWork secure plugin access across organizations?

OpenWork secures plugins through **resource-grant tables** (`PluginAccessGrantTable`) and the `ensureGrantTargetsInOrganization` helper. All grant operations validate that members and teams belong to the requesting organization, preventing cross-org privilege escalation. Additionally, the `withPluginArchOrgContext` middleware enforces `plugin_arch` capabilities on every API route.

### Can OpenWork import existing Claude plugins from GitHub?

Yes. The platform imports Claude-compatible plugins by parsing public repository trees, reading [`plugin.json`](https://github.com/different-ai/openwork/blob/main/plugin.json) and [`marketplace.json`](https://github.com/different-ai/openwork/blob/main/marketplace.json) files, and validating skill front-matter via `parseSkillMarkdown`. The `createPluginBundle` function then transforms these components into native OpenWork config objects and optionally publishes them to the organization's marketplace.

### What is the role of the UI bridge in OpenWork extensions?

The **UI bridge** (`uiBridgeRequest`) connects agents to the actual OpenWork desktop client by discovering the process through [`openwork-ui-control.json`](https://github.com/different-ai/openwork/blob/main/openwork-ui-control.json) in the user's config directory. This bridge ensures that agents query real UI state and execute commands against the live desktop application rather than simulated environments, maintaining synchronization between agent actions and user interface updates.