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
labelis 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 loadedshutdown?(): 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
configSchemavalidation - 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
ServerAdapterModuleinpackages/adapter-utils/src/types.tsis the mandatory contract for all Paperclip agent adaptersexecuteis the only required method—everything else enables richer platform integration- Adapters register via
registerServerAdapter()inserver/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →