Paperclip Agent Adapter Interface: The ServerAdapterModule Contract Explained

The standard interface for Paperclip agent adapters is the ServerAdapterModule TypeScript interface, defined in packages/adapter-utils/src/types.ts.

Every adapter in the Paperclip ecosystem—whether built-in or external—must implement this contract to be discovered, configured, and executed by the control plane. This article breaks down the ServerAdapterModule structure, its required methods, and how adapters register with the platform according to the actual source code in paperclipai/paperclip.


Core Interface: ServerAdapterModule

The canonical definition lives at packages/adapter-utils/src/types.ts (line 419). All server-side adapters must satisfy this interface:

export interface ServerAdapterModule {
  /** Unique identifier for the adapter – also used as the UI label */
  type: string;

  /** Human-readable name shown in the UI (optional – defaults to `type`) */
  label?: string;

  /** Optional JSON-schema that describes the adapter's configuration UI */
  configSchema?: AdapterConfigSchema;

  /** Optional capability flags (e.g. supports streaming, file upload, etc.) */
  capabilities?: AdapterCapabilities;

  /** Optional environment support definition (e.g. required env vars) */
  environment?: AdapterEnvironmentSupport;

  /** Core entry point – runs a task on the adapter */
  execute: (
    ctx: AdapterExecutionContext,
  ) => Promise<AdapterExecutionResult>;

  /** Optional lifecycle hooks */
  init?: () => Promise<void>;
  shutdown?: () => Promise<void>;

  /** Optional session-management or multi-process communication helpers */
  sessionManagement?: AdapterSessionManagement;

  /** Optional access-control policy for the adapter */
  acp?: AdapterRuntimeMcpAccess;
}

The execute property is the only strictly required method beyond type. All other fields are optional but enable richer integration with Paperclip's UI and runtime.


Required Methods and Properties

type: string

The unique identifier for the adapter. This string appears in:

  • The adapter registry lookup
  • UI labels (unless label is explicitly provided)
  • Route parameters when invoking adapters

execute: The Runtime Entry Point

Every adapter must implement execute with this signature:

execute: (
  ctx: AdapterExecutionContext,
) => Promise<AdapterExecutionResult>

This method receives an AdapterExecutionContext containing the task definition, input data, and execution environment, then returns a promise resolving to AdapterExecutionResult with success, output, and logs fields.


Optional Lifecycle Hooks and Configuration

init and shutdown

Adapters can implement init and shutdown for setup and teardown:

  • init?(): Promise<void> — Called once when the adapter is first loaded
  • shutdown?(): Promise<void> — Called during server shutdown for graceful cleanup

configSchema: Dynamic UI Generation

The optional configSchema property accepts an AdapterConfigSchema object. Paperclip uses this to auto-generate configuration forms in the UI, eliminating the need for custom frontend code per adapter.

capabilities: Feature Flags

Use capabilities (type AdapterCapabilities) to declare supported features like:

  • Streaming responses
  • File upload handling
  • Multi-turn conversation support

The control plane reads these flags to adapt its behavior per adapter.


Adapter Registration in the Paperclip Registry

Built-in adapters are declared as const …Adapter: ServerAdapterModule in server/src/adapters/registry.ts. This file imports the ServerAdapterModule type from server/src/adapters/types.ts, which re-exports it from the core utilities.

Example registration pattern:

// server/src/adapters/registry.ts
import { claudeLocalAdapter } from "./claude-local/index.js";
import { codexAdapter } from "./codex/index.js";
import { cursorAdapter } from "./cursor/index.js";

registerServerAdapter(claudeLocalAdapter);
registerServerAdapter(codexAdapter);
registerServerAdapter(cursorAdapter);

The registry supports both built-in adapters and external NPM packages. Any module exporting a ServerAdapterModule-compatible object can be loaded dynamically.


Minimal Adapter Implementation Example

Here's a complete "noop" adapter that satisfies the ServerAdapterModule contract:

// packages/adapters/noop/src/index.ts
import type { ServerAdapterModule } from "@paperclipai/adapter-utils";

export const noopAdapter: ServerAdapterModule = {
  type: "noop",
  label: "No-Op Adapter",
  
  execute: async ({ task, context }) => ({
    success: true,
    output: `Task "${task.name}" completed with no side effects.`,
    logs: [],
  }),
};

Register it in the server:

// server/src/adapters/registry.ts
import { noopAdapter } from "@paperclipai/adapters-noop";
registerServerAdapter(noopAdapter);

Execution Flow: How the Server Invokes Adapters

Routes resolve adapters by type and call execute directly. From server/src/routes/adapters.ts:

import type { ServerAdapterModule } from "../adapters/types.js";

export async function runAdapter(
  adapterType: string,
  runPayload: { taskId: string; input: unknown },
) {
  const adapter = requireServerAdapter(adapterType);
  const ctx = buildExecutionContext(runPayload);
  const result = await adapter.execute(ctx);
  return result;
}

The requireServerAdapter utility performs a registry lookup by type, validating that the returned object conforms to ServerAdapterModule before invocation.


UI-Side Adapter Discovery

The same contract propagates to the frontend via ui/src/adapters/registry.ts. This enables:

  • Consistent adapter metadata across server and client
  • Shared configSchema validation
  • Type-safe adapter configuration forms

When you add an adapter to the server registry, the UI automatically discovers its metadata through this shared interface.


Summary

  • ServerAdapterModule in packages/adapter-utils/src/types.ts is the mandatory contract for all Paperclip agent adapters
  • execute is the only required method—everything else enables richer platform integration
  • Adapters register via registerServerAdapter() in server/src/adapters/registry.ts
  • The interface supports built-in and external adapters through a single, type-safe API surface
  • Optional fields (configSchema, capabilities, environment) power automatic UI generation and runtime adaptation

Frequently Asked Questions

What happens if an adapter is missing the execute method?

The TypeScript compiler will reject the adapter at build time. At runtime, requireServerAdapter() throws if the resolved module lacks execute, preventing the server from accepting tasks for that adapter type.

Can external NPM packages implement Paperclip adapters?

Yes. Any NPM package that default-exports or named-exports an object satisfying ServerAdapterModule can be loaded by the server. The registry dynamically imports these packages and validates their shape against the interface.

How does the UI know which configuration fields an adapter needs?

The optional configSchema field contains a JSON Schema describing the adapter's configuration UI. The Paperclip frontend reads this schema to render appropriate form controls without custom code per adapter.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →