Skip to main content
This guide walks you through creating a complete Atomemo plugin using the JavaScript/TypeScript SDK.

Prerequisites

Before you begin, ensure you have:
  • Bun runtime installed (v1.0 or higher)
  • Node.js knowledge (TypeScript recommended)
  • A code editor with TypeScript support

Project Setup

1

Initialize your project

Create a new directory and initialize a Bun project:
mkdir my-plugin
cd my-plugin
bun init
2

Install the SDK

Install the Atomemo Plugin SDK and its peer dependencies:
bun add @choiceopen/atomemo-plugin-sdk-js
bun add zod chalk dotenv
3

Set up environment variables

Create a .env file in your project root:
HUB_MODE=debug
HUB_WS_URL=wss://hub.atomemo.ai
HUB_DEBUG_API_KEY=your_api_key_here
DEBUG=true
NODE_ENV=development
See Environment Setup for detailed configuration options.
4

Create your plugin file

Create an index.ts file with the basic plugin structure:
import "dotenv/config"
import { createPlugin } from "@choiceopen/atomemo-plugin-sdk-js"

const plugin = await createPlugin({
  name: "my-plugin",
  display_name: { en_US: "My Plugin" },
  description: { 
    en_US: "A simple plugin to get started with Atomemo" 
  },
  icon: "🔌",
  locales: ["en_US"],
  version: "0.1.0",
})

// Add features here

await plugin.run()
5

Run your plugin

Start your plugin in debug mode:
bun run index.ts
You should see output indicating successful connection to the Hub Server.

Plugin Definition Structure

The createPlugin function accepts a configuration object with these required fields:

Required Fields

FieldTypeDescription
namestringUnique identifier for your plugin (kebab-case)
display_nameRecord<Locale, string>Human-readable name (localized)
descriptionRecord<Locale, string>Brief description of plugin functionality
iconstringEmoji or icon representing your plugin
localesstring[]Supported locales (e.g., ["en_US", "zh_CN"])

Optional Fields

FieldTypeDescription
versionstringPlugin version (defaults to npm_package_version)
authorstringAuthor name (auto-populated in debug mode)
emailstringAuthor email (auto-populated in debug mode)
transporterOptionsTransporterOptionsCustom WebSocket configuration

Understanding Plugin Lifecycle

When you call plugin.run(), the SDK:
1

Initializes environment

  • Validates required environment variables
  • Determines runtime mode (debug or release)
  • Fetches or loads author information
2

Establishes connection

  • Creates WebSocket connection to Hub Server
  • Joins the appropriate channel based on mode:
    • Debug mode: debug_plugin:{plugin-name}
    • Release mode: release_plugin:{plugin-name}__{mode}__{version}
3

Registers plugin

In debug mode:
  • Sends plugin definition to Hub Server
  • Writes definition.json to disk for reference
4

Listens for events

  • credential_auth_spec: Credential authentication requests
  • invoke_tool: Tool invocation requests
  • Handles graceful shutdown on SIGINT/SIGTERM

Plugin Modes

Debug Mode

Enabled when HUB_MODE=debug (default). Features:
  • Dynamic registration with Hub Server
  • Hot-reload support
  • User authentication via OneAuth API
  • Requires HUB_DEBUG_API_KEY
  • Generates definition.json for inspection
// Debug mode automatically enabled
const plugin = await createPlugin({
  name: "my-plugin",
  // ...
})

Release Mode

Enabled when HUB_MODE=release. Features:
  • Production-ready deployment
  • Reads author info from definition.json
  • Versioned channel connections
  • No API key required
# Set in .env for release
HUB_MODE=release
NODE_ENV=production

Adding Features

Once your plugin is created, you can add features:
const plugin = await createPlugin({ /* ... */ })

// Add credentials
plugin.addCredential({
  name: "api-key",
  display_name: { en_US: "API Key" },
  // ...
})

// Add tools
plugin.addTool({
  name: "send-message",
  display_name: { en_US: "Send Message" },
  invoke: async ({ args }) => {
    // Tool implementation
    return { success: true }
  },
})

// Add models
plugin.addModel({
  name: "provider/model-name",
  display_name: { en_US: "Custom Model" },
  model_type: "llm",
  // ...
})

await plugin.run()
See dedicated guides for each feature type:

Error Handling

The SDK automatically handles common errors:

Connection Errors

When the WebSocket connection fails:
Error: Can't connect to the Plugin Hub server.

This is usually because the Debug API Key is missing or has expired.

Run `atomemo plugin refresh-key` to get a new key.

Validation Errors

If your plugin definition is invalid, you’ll see detailed Zod validation errors:
// Invalid configuration
const plugin = await createPlugin({
  // Missing required fields
  name: "test",
})
// Error: Invalid plugin definition
// - display_name: Required
// - description: Required
// ...

Best Practices

Follow semantic versioning for your plugin versions:
version: "1.2.3"
// MAJOR.MINOR.PATCH
Support multiple locales from the start:
display_name: {
  en_US: "My Plugin",
  zh_CN: "我的插件",
}
Choose clear, action-oriented names:
// Good
name: "slack-connector"

// Avoid
name: "plugin1"
The SDK automatically handles SIGINT and SIGTERM signals. Your async operations should be designed to complete or cancel cleanly.

Next Steps

Add Credentials

Secure API keys and authentication

Add Tools

Create executable functionality

Add Models

Integrate AI models

Debug Mode

Test and troubleshoot your plugin

Build docs developers (and LLMs) love