Custom Adapters
Build adapters to integrate Kindling with your tools.
Adapter Interface
Adapters implement a simple interface:
interface KindlingAdapter {
name: string;
version: string;
// Lifecycle
init(config: AdapterConfig): Promise<void>;
destroy(): Promise<void>;
// Capture
capture(event: CaptureEvent): Promise<Observation>;
// Optional: Query
search?(query: SearchQuery): Promise<Observation[]>;
}
Basic Adapter
import { KindlingAdapter, Observation } from '@eddacraft/kindling';
export class MyToolAdapter implements KindlingAdapter {
name = 'my-tool';
version = '1.0.0';
private capsule: string;
async init(config: AdapterConfig): Promise<void> {
this.capsule = config.capsule || 'default';
}
async destroy(): Promise<void> {
// Cleanup
}
async capture(event: CaptureEvent): Promise<Observation> {
return {
content: event.content,
kind: event.kind || 'discovery',
tags: [...(event.tags || []), 'my-tool'],
source: {
type: 'tool',
name: 'my-tool',
version: this.version,
},
capsule: this.capsule,
};
}
}
Registration
Package Export
// index.ts
export { MyToolAdapter } from './adapter';
Package.json
{
"name": "kindling-adapter-mytool",
"kindling": {
"adapter": true,
"entry": "./dist/index.js"
}
}
Install
kindling adapter install kindling-adapter-mytool
Event Sources
Polling
Periodically check for new data:
class PollingAdapter implements KindlingAdapter {
private interval: NodeJS.Timer;
async init(config: AdapterConfig): Promise<void> {
this.interval = setInterval(() => this.poll(), 60000);
}
private async poll(): Promise<void> {
const data = await fetchFromMyTool();
for (const item of data) {
await this.capture(item);
}
}
async destroy(): Promise<void> {
clearInterval(this.interval);
}
}
Webhook
Receive events via HTTP:
class WebhookAdapter implements KindlingAdapter {
private server: http.Server;
async init(config: AdapterConfig): Promise<void> {
this.server = http.createServer((req, res) => {
// Parse webhook payload
// Call this.capture()
});
this.server.listen(config.port || 3456);
}
}
File Watcher
Watch for file changes:
class FileAdapter implements KindlingAdapter {
private watcher: FSWatcher;
async init(config: AdapterConfig): Promise<void> {
this.watcher = watch(config.watchPath, async (event, filename) => {
const content = await readFile(filename, 'utf-8');
await this.capture({ content, kind: 'discovery' });
});
}
}
Configuration Schema
Define adapter configuration:
import { z } from 'zod';
export const MyToolConfigSchema = z.object({
capsule: z.string().default('default'),
apiKey: z.string(),
pollInterval: z.number().default(60000),
tags: z.array(z.string()).default([]),
});
type MyToolConfig = z.infer<typeof MyToolConfigSchema>;
Testing
import { describe, it, expect } from 'vitest';
import { MyToolAdapter } from './adapter';
describe('MyToolAdapter', () => {
it('captures observations', async () => {
const adapter = new MyToolAdapter();
await adapter.init({ capsule: 'test' });
const obs = await adapter.capture({
content: 'Test observation',
kind: 'discovery',
});
expect(obs.content).toBe('Test observation');
expect(obs.tags).toContain('my-tool');
});
});
Publishing
npm publish kindling-adapter-mytool
Users install with:
kindling adapter install kindling-adapter-mytool
Next: Memory commands →