# How to Create a Custom Agent Adapter in Paperclip AI

> Learn how to create a custom agent adapter in Paperclip AI by implementing the Adapter interface exporting from a new package and registering it in the registry.

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

---

**To create a custom agent adapter in Paperclip AI, implement the `Adapter` interface defined in [`packages/adapter-utils/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/types.ts), export it from a new package under `packages/adapters/`, and register it in [`packages/adapter-utils/src/registry.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/registry.ts).**

Paperclip AI treats every LLM and external tool as an adapter that conforms to a strict TypeScript contract. Creating a custom agent adapter allows you to integrate proprietary models or third-party APIs into the Paperclip execution runtime.

## What Is an Agent Adapter?

In the `paperclipai/paperclip` repository, an agent adapter is a thin wrapper that standardizes communication between the orchestration layer and a specific language model or external service. All adapters share a common contract defined in [`packages/adapter-utils/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/types.ts), ensuring the runtime can dispatch requests without knowing implementation details.

## Prerequisites and Project Structure

The repository uses a monorepo workspace structure. Each adapter lives as an independent package under `packages/adapters/`, such as the reference **claude-local** adapter.

Before starting, ensure you have:

- Node.js and pnpm installed
- Familiarity with the core types in [`packages/adapter-utils/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/types.ts)

## Step-by-Step Implementation

### Step 1: Scaffold the Adapter Package

Create a new directory under `packages/adapters/` for your adapter (e.g., `my-custom-adapter`). Initialize it with a standard [`package.json`](https://github.com/paperclipai/paperclip/blob/main/package.json) that declares the entry point and workspace compatibility:

```json
{
  "name": "@paperclipai/my-custom-adapter",
  "version": "0.1.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc -p ."
  }
}

```

Create [`src/index.ts`](https://github.com/paperclipai/paperclip/blob/main/src/index.ts) to re-export your implementation:

```typescript
export * from "./my-custom-adapter";

```

### Step 2: Implement the Adapter Interface

The core contract requires an object that implements the `Adapter` type. In [`packages/adapter-utils/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/types.ts), this interface mandates specific methods and metadata properties.

Create [`src/my-custom-adapter.ts`](https://github.com/paperclipai/paperclip/blob/main/src/my-custom-adapter.ts) with the following structure:

```typescript
import type {
  Adapter,
  AdapterConfig,
  GenerateRequest,
  GenerateResponse,
  RunContext,
  Env,
} from "@paperclipai/adapter-utils";

export const myCustomAdapter: Adapter = {
  name: "my_custom_adapter",
  displayName: "My Custom Adapter",
  meta: { description: "Connects Paperclip to My-API" },

  async init(config: AdapterConfig, env: Env) {
    if (!env.MY_API_KEY) {
      throw new Error("Missing MY_API_KEY");
    }
    // Adapter-specific initialization logic
  },

  async generate(request: GenerateRequest, ctx: RunContext) {
    const response = await fetch("https://api.myservice.com/generate", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${ctx.env.MY_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ prompt: request.prompt }),
    });
    const data = await response.json();
    return { text: data.output } as GenerateResponse;
  },

  async shutdown() {
    // Optional cleanup logic
  },
};

```

Key requirements:

- **name**: Unique snake_case identifier used by the registry
- **displayName**: Human-readable label shown in the UI
- **init**: Validates configuration and secrets before execution
- **generate**: The primary method that receives prompts and returns responses
- **shutdown**: Optional cleanup method called after run completion

### Step 3: Register the Adapter

The runtime loads adapters through the **adapter-registry** located at [`packages/adapter-utils/src/registry.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/registry.ts). Import your adapter and add it to the `defaultAdapters` map:

```typescript
import { myCustomAdapter } from "@paperclipai/my-custom-adapter";

export const defaultAdapters = {
  // ...existing adapters
  my_custom_adapter: myCustomAdapter,
};

```

This registration makes the adapter available to the UI and runtime service layers.

### Step 4: Configure Secrets and Authentication

If your adapter requires API keys, declare them in the adapter-auth schema at [`packages/adapter-utils/src/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/auth.ts). Follow the pattern used by existing adapters like **Codex-Local**, which pulls secrets from the Paperclip secret store using environment variable bindings.

Reference your secrets in the `init` method via the `env` parameter to validate presence before runtime execution.

### Step 5: Write Unit Tests

Place test files adjacent to your implementation using the pattern `src/**/*.test.ts`. Use the built-in test helpers from `packages/adapter-utils/testing` and Vitest for assertions:

```typescript
import { myCustomAdapter } from "./my-custom-adapter";
import { expect, test } from "vitest";

test("generate returns a response", async () => {
  const fakeCtx = { env: { MY_API_KEY: "test-key" } } as any;
  const resp = await myCustomAdapter.generate({ prompt: "Hello" }, fakeCtx);
  expect(resp).toHaveProperty("text");
});

```

Update the root [`vitest.config.ts`](https://github.com/paperclipai/paperclip/blob/main/vitest.config.ts) to include your adapter's test files:

```typescript
export default defineConfig({
  test: {
    include: ["packages/adapters/**/src/**/*.test.ts"],
  },
});

```

### Step 6: Build and Verify

Run the monorepo build commands from the repository root:

```bash
pnpm -r typecheck
pnpm -r build
pnpm test

```

Verify integration by checking that your adapter appears under **Agent → Adapter Selection** in the UI. The interface queries the `/api/agents/:company/adapter-models/:type` endpoint, which populates from the registry you updated.

## Summary

- **Adapter Location**: Create new adapters under `packages/adapters/` with their own [`package.json`](https://github.com/paperclipai/paperclip/blob/main/package.json) and [`src/index.ts`](https://github.com/paperclipai/paperclip/blob/main/src/index.ts) entry point.
- **Core Interface**: Implement the `Adapter` type from [`packages/adapter-utils/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/types.ts), providing `name`, `displayName`, `init`, and `generate` methods.
- **Registration**: Import and export your adapter in [`packages/adapter-utils/src/registry.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/registry.ts) to make it available to the runtime.
- **Secrets**: Validate required environment variables in the `init` method and define them in [`packages/adapter-utils/src/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/auth.ts).
- **Testing**: Use Vitest with helpers from `packages/adapter-utils/testing` and place tests in `src/**/*.test.ts` files.

## Frequently Asked Questions

### What is the minimum code required to create a functional custom adapter?

At minimum, you must export an object conforming to the `Adapter` interface with a `name` property, `displayName`, and a `generate` method that accepts `GenerateRequest` and `RunContext` and returns a `Promise<GenerateResponse>`. The `init` method is required by the type system but can be a no-op if your adapter needs no setup.

### How does Paperclip AI handle authentication for custom adapters?

Authentication is handled through the `Env` object passed to the `init` method. Store sensitive values like API keys in the Paperclip secret store, reference them in [`packages/adapter-utils/src/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/auth.ts), and access them via `ctx.env` or the `env` parameter in `init`. The runtime injects these values securely at execution time.

### Can I use external npm packages in my custom adapter?

Yes. Since each adapter is a standalone package in the monorepo, you can declare external dependencies in its [`package.json`](https://github.com/paperclipai/paperclip/blob/main/package.json). Install them using pnpm from the repository root, and they will be available to your adapter code after running `pnpm -r build`.

### Where does the UI fetch the list of available adapters from?

The React UI pulls the adapter list from the `/api/agents/:company/adapter-models/:type` endpoint, which aggregates entries from the `defaultAdapters` map exported in [`packages/adapter-utils/src/registry.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/adapter-utils/src/registry.ts). Ensure your adapter is registered there and the server is rebuilt for it to appear in the dropdown.