Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/asundar43/simpleclaw/llms.txt

Use this file to discover all available pages before exploring further.

Plugin SDK

The SimpleClaw Plugin SDK enables you to extend the Gateway with custom channels, providers, and tools.

Plugin Types

SimpleClaw supports two plugin categories:
  1. Channel Plugins - Messaging platform integrations (WhatsApp, Telegram, etc.)
  2. Provider Plugins - LLM providers, memory systems, task runners

Installation

The Plugin SDK is included with SimpleClaw:
import {
  // Channel plugin types
  type ChannelPlugin,
  type ChannelConfigAdapter,
  type ChannelOutboundAdapter,
  
  // Provider plugin types
  type SimpleClawPluginApi,
  type ProviderAuthContext,
  
  // Utilities
  buildMediaPayload,
  normalizeWebhookPath,
  registerWebhookTarget,
} from "simpleclaw/plugin-sdk";

Channel Plugin Structure

Basic Example

import type { ChannelPlugin } from "simpleclaw/plugin-sdk";

export const customChannel: ChannelPlugin = {
  id: "custom",
  
  meta: {
    label: "Custom Chat",
    detailLabel: "Custom messaging platform",
    systemImage: "message.circle",
  },
  
  capabilities: {
    outbound: true,
    groups: true,
    threads: false,
    media: ["image", "video", "audio"],
  },
  
  config: {
    // Configuration adapter
    resolveDefaultAccountId: (config) => "default",
    resolveAccount: (config, accountId) => ({
      accountId,
      enabled: true,
    }),
  },
  
  outbound: {
    // Message sending
    async send(ctx, message) {
      // Send message to platform API
      await fetch(`https://api.example.com/send`, {
        method: "POST",
        body: JSON.stringify({
          to: message.to,
          text: message.text,
        }),
      });
    },
  },
  
  status: {
    // Health checks
    async buildAccountSnapshot(ctx, accountId) {
      return {
        accountId,
        configured: true,
        connected: true,
        running: true,
      };
    },
  },
};

Plugin SDK Exports

The SDK provides helpers for common plugin tasks:

Media Handling

import { buildMediaPayload, type MediaPayload } from "simpleclaw/plugin-sdk";

// Convert media to standard format
const media: MediaPayload = buildMediaPayload({
  url: "https://example.com/image.jpg",
  mime: "image/jpeg",
  size: 12345,
  filename: "photo.jpg",
});

Webhook Registration

import {
  registerWebhookTarget,
  normalizeWebhookPath,
} from "simpleclaw/plugin-sdk";

// Register webhook endpoint
registerWebhookTarget({
  channelId: "custom",
  accountId: "default",
  path: normalizeWebhookPath("/webhooks/custom"),
  handler: async (req, res) => {
    const body = await req.json();
    // Process webhook
    res.json({ ok: true });
  },
});

Account Helpers

import { createAccountListHelpers } from "simpleclaw/plugin-sdk";

const helpers = createAccountListHelpers({
  configPrefix: "channels.custom",
  accountSchema: AccountConfigSchema,
});

// List all configured accounts
const accountIds = helpers.listAccountIds(config);

Status Helpers

import {
  buildBaseAccountStatusSnapshot,
  collectStatusIssuesFromLastError,
} from "simpleclaw/plugin-sdk";

// Build standard account snapshot
const snapshot = buildBaseAccountStatusSnapshot({
  accountId: "default",
  enabled: true,
  configured: true,
  connected: state.connected,
  lastError: state.error?.message,
});

Configuration Schema

Define config schema with Zod:
import { z } from "zod";
import { buildChannelConfigSchema } from "simpleclaw/plugin-sdk";

const AccountConfigSchema = z.object({
  apiKey: z.string(),
  webhookUrl: z.string().optional(),
});

const CustomConfigSchema = z.object({
  enabled: z.boolean().default(false),
  accounts: z.record(AccountConfigSchema),
});

export const configSchema = buildChannelConfigSchema({
  schema: CustomConfigSchema.shape,
  uiHints: {
    "accounts.*.apiKey": {
      label: "API Key",
      sensitive: true,
      help: "Get this from Custom Chat dashboard",
    },
  },
});

Outbound Message Delivery

import type { ChannelOutboundAdapter } from "simpleclaw/plugin-sdk";

export const outbound: ChannelOutboundAdapter = {
  async send(ctx, message) {
    const { to, text, media } = message;
    const account = ctx.account;
    
    // Send text message
    if (text) {
      await api.sendText({
        apiKey: account.apiKey,
        recipient: to,
        message: text,
      });
    }
    
    // Send media attachments
    if (media?.length) {
      for (const item of media) {
        await api.sendMedia({
          apiKey: account.apiKey,
          recipient: to,
          url: item.url,
          caption: item.caption,
        });
      }
    }
  },
  
  async sendReaction(ctx, params) {
    await api.react({
      apiKey: ctx.account.apiKey,
      messageId: params.messageId,
      emoji: params.emoji,
    });
  },
};

Security Adapter

Implement allowlist and DM policy:
import type { ChannelSecurityAdapter } from "simpleclaw/plugin-sdk";
import { isNormalizedSenderAllowed } from "simpleclaw/plugin-sdk";

export const security: ChannelSecurityAdapter = {
  resolveSenderAllowed: (ctx, sender) => {
    const allowFrom = ctx.account.allowFrom || [];
    
    return isNormalizedSenderAllowed({
      senderId: sender.id,
      allowFrom,
    });
  },
  
  resolveDmPolicy: (ctx) => {
    return ctx.account.dmPolicy || "pairing";
  },
};

Gateway Adapter

Handle incoming messages:
import type { ChannelGatewayAdapter } from "simpleclaw/plugin-sdk";

export const gateway: ChannelGatewayAdapter = {
  async start(ctx) {
    const account = ctx.account;
    
    // Connect to platform API
    const client = await connect(account.apiKey);
    
    // Listen for incoming messages
    client.on("message", async (msg) => {
      await ctx.handleInbound({
        from: msg.sender.id,
        text: msg.text,
        timestamp: msg.timestamp,
      });
    });
  },
  
  async stop(ctx) {
    // Cleanup
    await client?.disconnect();
  },
};

Plugin Packaging

Create a plugin package:
{
  "name": "@simpleclaw/channel-custom",
  "version": "1.0.0",
  "main": "dist/index.js",
  "simpleclaw": {
    "extensions": [
      {
        "type": "channel",
        "id": "custom",
        "entry": "./dist/index.js"
      }
    ]
  },
  "dependencies": {
    "simpleclaw": "^2026.3.0"
  }
}

Provider Plugin Example

import type {
  SimpleClawPluginService,
  SimpleClawPluginServiceContext,
} from "simpleclaw/plugin-sdk";

export const customProvider: SimpleClawPluginService = {
  id: "custom-llm",
  
  async init(ctx: SimpleClawPluginServiceContext) {
    // Initialize provider
  },
  
  async auth(ctx) {
    // OAuth flow or API key validation
    return {
      success: true,
      profile: {
        id: "user-123",
        name: "User Name",
      },
    };
  },
  
  async invoke(ctx, request) {
    // Handle LLM request
    const response = await fetch("https://api.example.com/chat", {
      method: "POST",
      body: JSON.stringify(request),
    });
    
    return response.json();
  },
};

Testing Plugins

import { describe, test, expect } from "vitest";
import { customChannel } from "./index.js";

describe("custom channel", () => {
  test("sends messages", async () => {
    const ctx = createMockContext();
    
    await customChannel.outbound.send(ctx, {
      to: "user-123",
      text: "Hello!",
    });
    
    expect(mockApi.sendText).toHaveBeenCalledWith({
      recipient: "user-123",
      message: "Hello!",
    });
  });
});

Next Steps

Channel Plugins

Deep dive into channel plugin development

Provider Plugins

Build custom LLM and tool providers