# Paperclip Plugin Architecture: How to Build Custom Plugins for Paperclip

> Learn Paperclip's plugin architecture and build custom plugins. Understand the manifest, worker process, and UI bundle to extend Paperclip's capabilities.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: how-to-guide
- Published: 2026-08-16

---

**Paperclip's extensibility is powered by a plugin SDK that uses a three-part architecture: a manifest declaring capabilities and UI slots, a worker process communicating over JSON-RPC, and an optional React UI bundle that renders inside the host application.**

Paperclip is an open-source AI-powered workspace platform. Its modular design allows developers to extend core functionality without modifying the main codebase. This guide explains how the **Paperclip plugin architecture** works and how you can build your own custom plugins using the official SDK.

## The Three Core Components of a Paperclip Plugin

Every Paperclip plugin consists of three mandatory or optional pieces located under `packages/plugins/sdk`:

1. **Manifest ([`manifest.ts`](https://github.com/paperclipai/paperclip/blob/main/manifest.ts))** — Declares identity, version, capabilities, UI slots, and entrypoints
2. **Worker ([`worker.ts`](https://github.com/paperclipai/paperclip/blob/main/worker.ts))** — Runs in an isolated process and implements lifecycle hooks via the `PluginContext` API
3. **UI bundle (`ui/…`)** — Optional React components that render in Paperclip's interface

The Paperclip server spawns a **worker process** for each installed plugin. Communication happens over **JSON-RPC 2.0** on stdio, with the contract defined in [`packages/plugins/sdk/src/protocol.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/protocol.ts).

## Defining the Plugin Manifest

The manifest shape `PaperclipPluginManifestV1` in [`src/manifest.ts`](https://github.com/paperclipai/paperclip/blob/main/src/manifest.ts) drives discovery and security gating.

```ts
import type { PaperclipPluginManifestV1 } from "@paperclipai/plugin-sdk";

const manifest: PaperclipPluginManifestV1 = {
  id: "my-awesome-plugin",
  apiVersion: 1,
  version: "0.1.0",
  displayName: "My Awesome Plugin",
  description: "Demo plugin that adds a sidebar link and a detail tab.",
  author: "Your Name",
  categories: ["ui"],
  capabilities: [
    "ui.sidebar.register",
    "ui.detailTab.register",
    "projects.read",
    "plugin.state.read",
  ],
  instanceConfigSchema: {
    type: "object",
    properties: {
      showFeature: {
        type: "boolean",
        title: "Show Feature",
        default: true,
      },
    },
  },
  entrypoints: {
    worker: "./dist/worker.js",
    ui: "./dist/ui",
  },
  ui: {
    slots: [
      {
        type: "projectSidebarItem",
        id: "my-sidebar-link",
        displayName: "My Feature",
        exportName: "MySidebarLink",
        entityTypes: ["project"],
        order: 5,
      },
      {
        type: "detailTab",
        id: "my-detail-tab",
        displayName: "My Tab",
        exportName: "MyDetailTab",
        entityTypes: ["project"],
        order: 5,
      },
    ],
  },
};

export default manifest;

```

Source: [[`packages/plugins/examples/plugin-file-browser-example/src/manifest.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/examples/plugin-file-browser-example/src/manifest.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/examples/plugin-file-browser-example/src/manifest.ts)

### Key Manifest Fields

- **`capabilities`** — Strings that gate what the worker may call on the host, such as `ui.sidebar.register`, `projects.read`, or `plugin.state.read`
- **`instanceConfigSchema`** — JSON Schema describing per-instance settings surfaced in the admin UI
- **`entrypoints`** — Paths to compiled worker ([`dist/worker.js`](https://github.com/paperclipai/paperclip/blob/main/dist/worker.js)) and UI bundle (`dist/ui`)
- **`ui.slots`** — Declares extension points: `projectSidebarItem`, `detailTab`, `commentAnnotation`, and more

## Building the Worker with `definePlugin`

The worker entrypoint calls `definePlugin` from `@paperclipai/plugin-sdk`. The only required hook is `setup(ctx)`, where you register handlers.

```ts
import { definePlugin } from "@paperclipai/plugin-sdk";

export default definePlugin({
  async setup(ctx) {
    // Register UI data source
    ctx.data.register("my-feature-enabled", async ({ companyId }) => {
      const cfg = await ctx.config.get(companyId);
      return { enabled: cfg.showFeature ?? false };
    });

    // Subscribe to domain events
    ctx.events.on("issue.created", async (event) => {
      const { companyId, payload } = event;
      ctx.logger.info("New issue created", { issueId: payload.id, companyId });
      
      await ctx.http.fetch(`https://api.example.com/notify`, {
        method: "POST",
        body: JSON.stringify({ issueId: payload.id }),
      });
    });

    // Register background job
    ctx.jobs.register("full-sync", async (job) => {
      // Long-running work here
    });
  },

  async onHealth() {
    return { status: "ok", message: "All systems nominal" };
  },
});

```

Source: [[`packages/plugins/sdk/src/define-plugin.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/define-plugin.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/sdk/src/define-plugin.ts)

### The `PluginContext` API

The `PluginContext` interface in [`packages/plugins/sdk/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/types.ts) exposes these capabilities:

| API | Purpose |
|-----|---------|
| `ctx.events.on(event, handler)` | Subscribe to host-emitted domain events |
| `ctx.jobs.register(id, handler)` | Register background jobs |
| `ctx.data.register(key, fetcher)` | Provide data to UI components |
| `ctx.config.get(companyId)` | Resolve company-scoped configuration |
| `ctx.secrets.resolve(ref)` | Resolve secret references |
| `ctx.http.fetch(url, options)` | Perform outbound HTTP calls |
| `ctx.state.get/set(params)` | Read/write scoped key-value state |
| `ctx.logger.info/warn/error` | Structured logging |
| `ctx.activity.log(entry)` | Emit audit activity logs |
| `ctx.metrics.write(record)` | Emit numeric metrics |
| `ctx.telemetry.track(event)` | Track custom telemetry events |
| `ctx.span.record(span)` | Record provider spans for tracing |

Source: [[`packages/plugins/sdk/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/types.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/sdk/src/types.ts)

## Host-Worker RPC Protocol

All method calls are typed in `HostToWorkerMethods` (host calls worker) and `WorkerToHostMethods` (worker calls host). The host invokes `initialize`, `configChanged`, `getData`, `performAction`, and `environment*` methods. The worker calls back for services like `config.get`, `state.get`, and `http.fetch`.

Errors are namespaced under `PLUGIN_RPC_ERROR_CODES` in [`packages/plugins/sdk/src/protocol.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/protocol.ts). For example, `CROSS_TENANT_CONFIG` fires when a single-tenant plugin receives configuration for a second company.

Source: [[`packages/plugins/sdk/src/protocol.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/protocol.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/sdk/src/protocol.ts)

## Creating React UI Components

When your manifest declares UI slots, the compiled UI bundle must export components matching each `exportName`. The host injects these into the appropriate location and supplies data from `ctx.data.register`.

```tsx
import * as React from "react";
import { usePluginData } from "@paperclipai/plugin-sdk";

export const MySidebarLink = () => {
  const { data, loading } = usePluginData("my-feature-enabled");
  if (loading) return null;
  if (!data?.enabled) return null;

  return (
    <a href="#" onClick={() => alert("Clicked My Feature!")}>
      My Feature
    </a>
  );
};

```

The component name `MySidebarLink` must match the `exportName` in your manifest. The host loads the UI bundle under a sandbox that respects your declared capabilities.

## Optional: Sandbox Providers for Isolated Execution

For plugins requiring isolated runtimes—such as Docker containers or remote VMs—the SDK defines a **sandbox driver** interface via `environment*` methods. Reference implementations live in `packages/plugins/sandbox-providers/`:

- `novita` — Serverless GPU sandbox provider
- `e2b` — E2B code sandbox
- `daytona` — Daytona workspace environment

Opt in by including `environment.*` in your `capabilities` array and implementing the corresponding RPC handlers.

Source: [[`packages/plugins/sdk/src/protocol.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/protocol.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/sdk/src/protocol.ts)

## Installing Your Custom Plugin

Build and install from the host CLI:

```bash

# Build the plugin (monorepo checkout)

pnpm --filter @paperclipai/plugin-file-browser-example build

# Install via local path (development)

npx paperclipai plugin install ./packages/plugins/examples/plugin-file-browser-example

```

The host persists your manifest in the database, resolves the worker entrypoint at [`dist/worker.js`](https://github.com/paperclipai/paperclip/blob/main/dist/worker.js), and spawns the process. Uninstalling stops the worker and removes the manifest.

Source: [[`packages/plugins/examples/plugin-file-browser-example/README.md`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/examples/plugin-file-browser-example/README.md)](https://github.com/paperclipai/paperclip/blob/master/packages/plugins/examples/plugin-file-browser-example/README.md)

## Summary

- **Paperclip plugin architecture** consists of a manifest, worker process, and optional UI bundle
- The **manifest** ([`manifest.ts`](https://github.com/paperclipai/paperclip/blob/main/manifest.ts)) declares capabilities that gate host API access and UI slots that determine where components render
- The **worker** uses `definePlugin` from `@paperclipai/plugin-sdk` to register event handlers, jobs, data providers, and lifecycle hooks
- **JSON-RPC 2.0** over stdio enables typed, bidirectional communication between host and worker
- The **PluginContext API** in [`packages/plugins/sdk/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/types.ts) provides 12+ subsystems for configuration, state, HTTP, logging, metrics, and more
- **UI components** receive data via `ctx.data.register` and render inside capability-restricted sandboxes
- **Sandbox providers** enable isolated execution environments for security-sensitive operations

## Frequently Asked Questions

### How does Paperclip isolate plugins from the main application?

Paperclip spawns each plugin in a **separate worker process** and communicates over JSON-RPC on stdio. The host enforces capability-based access control—plugins can only call host methods explicitly listed in their manifest's `capabilities` array. Sandboxed execution environments are available for additional isolation.

### What programming language are Paperclip plugins written in?

Plugins are written in **TypeScript** and compiled to JavaScript. The SDK (`@paperclipai/plugin-sdk`) provides full type safety for the manifest, context APIs, and RPC protocol. UI components use React with hooks provided by the SDK.

### Can a plugin access data across multiple companies or tenants?

No. The plugin system enforces tenant isolation. If a single-tenant plugin receives configuration for a second company, the host returns `PLUGIN_RPC_ERROR_CODES.CROSS_TENANT_CONFIG`. The `PluginContext` automatically scopes state, configuration, and secrets to the requesting company's ID.

### Where can I find complete working examples of Paperclip plugins?

The repository includes `packages/plugins/examples/plugin-file-browser-example/` demonstrating sidebar items and detail tabs. For workspace manipulation examples, see `packages/plugins/plugin-workspace-diff/src/`. Sandbox provider implementations are in `packages/plugins/sandbox-providers/`.