Connector SDK — Getting Started
This guide walks through building a new connector adapter against the stable ConnectorAdapterV1 interface (Phase M, RFC-0001). For deeper review-process details and existing adapters, see Adapter Contribution Guide.
Prerequisites
- Node.js 22+
@lenserfight/adapters-connectoravailable in your workspace (it's already in the monorepo; install from the relative workspace path or from npm once published).- Familiarity with TypeScript modules and async/await.
1. Install
For an in-monorepo example:
import {
ConnectorAdapterV1,
CONNECTOR_SCOPES,
HttpConnectorAdapter,
registerConnectorAdapter,
type ConnectorMetadata,
type DispatchEvent,
type DispatchResult,
type VerifyResult,
} from '@lenserfight/adapters-connector'Out-of-tree projects depend on the package directly:
pnpm add @lenserfight/adapters-connector2. Implement ConnectorAdapterV1
The interface has four methods. Implementations must resolve all promises (never throw) — surface failures via ok: false results instead.
export function createMyAdapter(opts: {
serviceToken: string
endpoint: string
}): ConnectorAdapterV1 {
const metadata: ConnectorMetadata = {
slug: 'my-service',
name: 'My Service',
kind: 'api',
scopes: [CONNECTOR_SCOPES[0]], // pin to canonical scopes
isActive: true,
description: 'Bridges LenserFight runs to my-service.example.com.',
}
const inner = new HttpConnectorAdapter({
metadata,
endpoint: opts.endpoint,
serviceToken: opts.serviceToken,
})
return {
id: () => 'my-service',
metadata: () => metadata,
verify: (token: string): Promise<VerifyResult> => inner.verify(token),
dispatch: (event: DispatchEvent): Promise<DispatchResult> => inner.dispatch(event),
}
}The same shape is used by examples/connectors/chainabit-example/src/adapter.ts — read it end-to-end before writing your own.
3. Register the adapter
Registration adds the adapter to the in-process registry so the LenserFight runtime can resolve it by id. Idempotent — registering the same id twice throws.
registerConnectorAdapter('my-service', () =>
createMyAdapter({
serviceToken: process.env.MY_SERVICE_TOKEN!,
endpoint: 'https://api.my-service.example.com',
}),
)Operators register the connector record in Supabase so users can grant tokens to it:
lf connectors add \
--slug my-service \
--name "My Service" \
--kind api \
--scopes "lenses:read,workflows:write"4. Verify with the CLI
After registering and adding the connector:
lf connectors test my-service --token <service-token>The CLI invokes your adapter's verify(token) and prints ok plus the returned scopes. A failed verify exits non-zero with the underlying reason field.
5. Test conformance
Adapter tests should cover the documented contract corners:
describe('MyAdapter', () => {
it('verify returns ok=false for an expired token', async () => {
const adapter = createMyAdapter({ serviceToken: 'expired', endpoint: '...' })
const r = await adapter.verify('expired')
expect(r.ok).toBe(false)
})
it('dispatch never throws — returns ok=false on transport error', async () => {
const adapter = createMyAdapter({ serviceToken: 'x', endpoint: 'http://127.0.0.1:1' })
const r = await adapter.dispatch({ type: 'noop', payload: {} })
expect(r.ok).toBe(false)
})
})Run the project's tests with pnpm nx test <your-project>.
What's stable, what's not
ConnectorAdapterV1 is governed by RFC-0001:
- Stable in V1: the four method signatures,
ConnectorMetadatashape, and theCONNECTOR_SCOPESgrammar. - Subject to additive change in V1 minor releases: optional fields on
ConnectorMetadata,DispatchEvent,DispatchResult,VerifyResult. - Breaking changes: bump to
ConnectorAdapterV2with a deprecation cycle; V1 continues to work during overlap.
Pin to the versioned symbol (ConnectorAdapterV1), not the unversioned ConnectorAdapter alias, so a future V2 can't silently change the shape under you.
Next steps
- Adapter Contribution Guide — full review process and PR labels.
- RFC-0001 Connector Interface — interface governance and change process.
- Chainabit example adapter — runnable reference integration.