npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@useatlas/plugin-sdk

v0.0.17

Published

Type definitions and helpers for authoring Atlas plugins

Downloads

3,385

Readme

@useatlas/plugin-sdk

Type definitions and helpers for authoring Atlas plugins. Plugins extend Atlas with new datasources, context providers, interaction surfaces, actions, and sandbox backends.

Quick Start

import { createPlugin } from "@useatlas/plugin-sdk";
import { z } from "zod";

export const myPlugin = createPlugin({
  configSchema: z.object({ url: z.string() }),
  create: (config) => ({
    id: "my-datasource",
    types: ["datasource"] as const,
    version: "1.0.0",
    config,
    connection: {
      create: () => makeConnection(config.url),
      dbType: "postgres",
    },
  }),
});

Register in atlas.config.ts:

import { defineConfig } from "@atlas/api/lib/config";
import { myPlugin } from "./my-plugin";

export default defineConfig({
  plugins: [myPlugin({ url: process.env.MY_DB_URL! })],
});

Plugin Types

Atlas has five plugin types. Each extends AtlasPluginBase with variant-specific fields.

Datasource (AtlasDatasourcePlugin)

Connect to a database. Provides a connection factory, SQL dialect hints, and optional entity definitions.

A datasource plugin must provide at least one connection factory: create() for a static config-defined connection, createFromConfig() for a DB-stored (admin-registered) per-workspace connection, or both. A plugin with only createFromConfig() is an adapter-only plugin — registered for use but with no static datasource (the multi-tenant SaaS model, where every connection is added per workspace).

| Field | Type | Required | Description | |-------|------|----------|-------------| | connection.create() | () => PluginDBConnection \| Promise<PluginDBConnection> | One of create/createFromConfig | Factory that returns a connection with query() and close() from the plugin's config-time config (static / self-host) | | connection.createFromConfig() | (config) => PluginDBConnection \| Promise<PluginDBConnection> | One of create/createFromConfig | Factory that builds a connection from a DB-stored per-(workspace, install) config (adapter-only / SaaS) | | connection.dbType | PluginDBType | Yes | Database type identifier (postgres, mysql, clickhouse, snowflake, duckdb, or custom) | | entities | EntityProvider | No | Semantic layer fragments merged into the table whitelist at boot | | dialect | string | No | SQL dialect guidance injected into the agent system prompt |

Reference: clickhouse

Context (AtlasContextPlugin)

Inject knowledge into the agent. Context plugins load additional system prompt fragments, semantic layer extensions, or external metadata.

| Field | Type | Required | Description | |-------|------|----------|-------------| | contextProvider.load() | () => Promise<string> | Yes | Returns context string appended to the agent system prompt | | contextProvider.refresh() | () => Promise<void> | No | Cache invalidation hook |

Reference: yaml-context

Interaction (AtlasInteractionPlugin)

Add communication surfaces. Interaction plugins mount HTTP routes or manage non-HTTP transports (stdio, SSE).

| Field | Type | Required | Description | |-------|------|----------|-------------| | routes | (app: Hono) => void | No | Mount Hono routes for webhooks, OAuth, etc. Optional for non-HTTP transports |

Reference: chat, mcp

Action (AtlasActionPlugin)

Enable agent side-effects. Action plugins provide AI SDK tools with approval controls.

| Field | Type | Required | Description | |-------|------|----------|-------------| | actions | PluginAction[] | Yes | Array of action definitions | | actions[].name | string | Yes | Tool name | | actions[].tool | ToolSet[string] | Yes | AI SDK tool definition | | actions[].actionType | string | Yes | Category identifier (e.g. jira:create) | | actions[].reversible | boolean | Yes | Whether the action can be undone | | actions[].defaultApproval | ActionApprovalMode | Yes | auto, manual, or admin-only | | actions[].requiredCredentials | string[] | Yes | Config fields needed at runtime |

Reference: jira, email

Sandbox (AtlasSandboxPlugin)

Provide code isolation for the explore tool. Sandbox plugins create backends that execute shell commands in an isolated environment.

| Field | Type | Required | Description | |-------|------|----------|-------------| | sandbox.create(semanticRoot) | (string) => PluginExploreBackend | Yes | Factory that creates an explore backend | | sandbox.priority | number | No | Higher = tried first. Built-in: Vercel=100, E2B=90, Daytona=85, nsjail=75, sidecar=50, just-bash=0. Plugin default: 60 | | security | object | No | Informational metadata about isolation guarantees |

Reference: nsjail, vercel-sandbox

Base Fields

All plugins share these fields from AtlasPluginBase:

| Field | Type | Required | Description | |-------|------|----------|-------------| | id | string | Yes | Unique identifier (e.g. clickhouse-datasource) | | type | PluginType | Yes | One of datasource, context, interaction, action, sandbox | | version | string | Yes | SemVer version | | name | string | No | Human-readable display name | | config | TConfig | No | Plugin-specific configuration | | initialize(ctx) | (AtlasPluginContext) => Promise<void> | No | Called once at server boot. Throw to block startup | | healthCheck() | () => Promise<PluginHealthResult> | No | Periodic health probe | | teardown() | () => Promise<void> | No | Graceful shutdown (LIFO order) | | onUninstall(workspaceId) | (string) => Promise<void> \| void | No | Per-workspace uninstall hook — revoke external webhook subscriptions / OAuth grants the plugin registered for that workspace | | hooks | PluginHooks | No | Agent lifecycle and HTTP hooks | | schema | Record<string, PluginTableDefinition> | No | Declarative table definitions for the internal DB |

Lifecycle

register → initialize(ctx) → healthCheck() → ... → teardown()
                          └── onUninstall(workspaceId)   (per-workspace, at uninstall time)
  1. Register — Plugins are listed in the plugins array in atlas.config.ts. Config is validated at factory call time (before server starts).
  2. Initialize — Called once at server boot with AtlasPluginContext. Throw to signal fatal initialization failure. Context provides:
    • ctx.db — Internal Postgres (auth/audit DB). Null when DATABASE_URL is not set.
    • ctx.connections — Connection registry for analytics datasources.
    • ctx.tools — Tool registry for adding agent tools.
    • ctx.logger — Pino-compatible child logger scoped to the plugin.
    • ctx.config — Resolved Atlas configuration.
  3. Health check — Periodic probe. Return { healthy: false, message } to signal degradation. Never throw. Results surface in the plugins component of GET /api/health — a failing probe shifts the top-level status to degraded (HTTP 200, never 503).
  4. Teardown — Graceful shutdown in reverse registration order (LIFO). Use teardown() to release process-level state — third-party connections, drained queues, timers. Note: teardown() runs on server shutdown, not on a per-workspace uninstall (uninstall is a DB-row removal, not a process event) — per-workspace cleanup belongs in onUninstall.
  5. Per-workspace uninstallonUninstall(workspaceId) fires when a workspace uninstalls the plugin (both the marketplace DELETE route and WorkspaceInstaller.uninstall), before Atlas removes the install row and credential stores — so the plugin can still authenticate against the external platform. Use it to revoke webhook subscriptions, OAuth grants, or any other external state registered for that workspace. Attribution rule: never revoke a subscription you can't positively attribute to the uninstalling workspace (recorded id, metadata tag, or workspace marker in the callback URL) — the credential may be shared with other workspaces or out-of-band tooling. Best-effort: a thrown error is logged with the plugin id + workspaceId and the uninstall proceeds (each invocation also runs against a 15s host-side deadline); never rely on it for load-bearing cleanup. It does not fire on datasource disconnects or a workspace purge — only the two plugin-uninstall paths above. See packages/api/src/lib/integrations/jira/lazy-builder.ts for a reference implementation (revokes only workspace-attributed Jira dynamic webhook subscriptions).

v1.1 note: AtlasPluginContext will gain executeQuery, conversations, and actions fields for full host-level decoupling. Currently, interaction plugins that need these inject them via config callbacks.

Uninstall Contract

DELETE /api/v1/admin/marketplace/:id removes a plugin from a workspace. The cleanup contract:

| State | Survives uninstall? | Notes | |-------|---------------------|-------| | workspace_plugins row | No (deleted) | Canonical "is this plugin installed?" record. | | scheduled_tasks rows tagged with the plugin's catalog_id | No (deleted) | Scoped by (plugin_id, org_id) so cleanup never crosses workspaces. scheduled_task_runs cascade via FK. Cleanup runs in a separate statement after the install row is removed; partial failure leaves the uninstall committed and is recorded as a cleanupFailed: true audit event. | | plugin_<pluginId>_* tables (declared via schema) | Yes (retained) | Reinstall picks up where it left off — cached digest history, sync cursors, etc. Hard-reset only via workspace purge. | | In-process hook registrations | Not detached on uninstall — teardown() runs only at server shutdown | Hooks become inert for the uninstalled workspace because dispatch checks workspace_plugins for the installation. | | Webhook subscriptions registered with external platforms | Yes — unless your onUninstall(workspaceId) revokes them | Atlas has no visibility into external state. Implement onUninstall to revoke the subscription — it fires before the install row and credentials are removed, so your plugin can still authenticate. Revoke only subscriptions attributable to the uninstalling workspace. An un-revoked webhook keeps delivering events to a workspace that no longer has the plugin installed. Note the hook fires only on plugin uninstalls — datasource disconnects and workspace purges skip it. |

If your plugin creates scheduled_tasks rows, set plugin_id = $catalogId and org_id = $orgId on insert so the uninstall cleanup picks them up. Untagged tasks (plugin_id IS NULL) are treated as user-created and survive uninstall. See the authoring guide for the full lifecycle.

Config Validation

Use createPlugin() with a Zod schema for typed, validated configuration:

import { createPlugin } from "@useatlas/plugin-sdk";
import { z } from "zod";

const configSchema = z.object({
  url: z.string().url(),
  poolSize: z.number().int().positive().optional(),
});

export const myPlugin = createPlugin({
  configSchema,
  create: (config) => ({
    id: "my-datasource",
    types: ["datasource"] as const,
    version: "1.0.0",
    config,
    connection: { create: () => connect(config.url), dbType: "postgres" },
  }),
});

The factory validates config at call time — invalid config fails fast during startup, not at first use. The configSchema accepts any object with a parse(input) method (Zod schemas satisfy this).

For simple plugins without config validation, use definePlugin():

import { definePlugin } from "@useatlas/plugin-sdk";

export default definePlugin({
  id: "my-context",
  types: ["context"],
  version: "1.0.0",
  contextProvider: { async load() { return "Extra context..."; } },
});

Hooks

Hooks use a matcher + handler pattern inspired by Better Auth. Matchers are optional — omit to always fire.

hooks: {
  beforeQuery: [{
    matcher: (ctx) => ctx.connectionId === "warehouse",
    handler: (ctx) => {
      // Return { sql } to rewrite, throw to reject, return void to pass through
      return { sql: ctx.sql.replace(/SELECT \*/, "SELECT id, name") };
    },
  }],
  afterQuery: [{
    handler: (ctx) => {
      console.log(`Query took ${ctx.durationMs}ms, returned ${ctx.result.rows.length} rows`);
    },
  }],
}

| Hook | Context | Mutation | Description | |------|---------|----------|-------------| | beforeQuery | { sql, connectionId? } | { sql } | Fires before SQL execution. Return to rewrite, throw to reject | | afterQuery | { sql, connectionId?, result, durationMs } | — | Fires after SQL execution | | beforeExplore | { command } | { command } | Fires before explore command. Return to rewrite, throw to reject | | afterExplore | { command, output } | — | Fires after explore command | | onRequest | { path, method, headers } | — | HTTP-level: fires before routing | | onResponse | { path, method, status } | — | HTTP-level: fires after response |

Schema Migrations

Plugins declare internal database tables via the schema property:

schema: {
  slack_installations: {
    fields: {
      team_id: { type: "string", required: true, unique: true },
      bot_token: { type: "string", required: true },
      installed_at: { type: "date" },
    },
  },
},

Run migrations with the CLI:

atlas migrate          # Preview migration SQL
atlas migrate --apply  # Apply to internal DB

Tables are prefixed with the plugin ID to avoid collisions.

$InferServerPlugin

Extract plugin types on the client side without importing server code:

import type { $InferServerPlugin } from "@useatlas/plugin-sdk";
import type { clickhousePlugin } from "@useatlas/clickhouse";

type CH = $InferServerPlugin<typeof clickhousePlugin>;
// CH["Config"]   → { url: string; database?: string }
// CH["Type"]     → "datasource"
// CH["DbType"]   → "clickhouse"
// CH["Actions"]  → never (not an action plugin)
// CH["Security"] → never (not a sandbox plugin)

Works with both createPlugin() factory functions and definePlugin() direct objects.

Available inferred fields: Config, Type, Id, Name, Version, DbType (datasource only), Actions (action only), Security (sandbox only).

CLI

atlas plugin list                              # List installed plugins from atlas.config.ts
atlas plugin create <name> --type <type>       # Scaffold a new plugin (datasource|context|interaction|action|sandbox)
atlas plugin add <package-name>                # Install a plugin package via bun

atlas plugin create generates a plugins/<name>/ directory with src/index.ts, src/index.test.ts, package.json, and tsconfig.json.

Type Guards

import {
  isDatasourcePlugin,
  isContextPlugin,
  isInteractionPlugin,
  isActionPlugin,
  isSandboxPlugin,
} from "@useatlas/plugin-sdk";

if (isDatasourcePlugin(plugin)) {
  // plugin is AtlasDatasourcePlugin — connection, entities, dialect available
}

Reference Implementations

Note: These are workspace packages (@useatlas/*) within the monorepo. Use them as reference when authoring your own plugins.

| Plugin | Type | Package | Description | |--------|------|---------|-------------| | clickhouse | Datasource | @useatlas/clickhouse | ClickHouse HTTP transport adapter | | mysql | Datasource | @useatlas/mysql | MySQL pool-based adapter | | snowflake | Datasource | @useatlas/snowflake | Snowflake callback-based adapter | | duckdb | Datasource | @useatlas/duckdb | DuckDB in-process adapter | | yaml-context | Context | @useatlas/yaml-context | YAML semantic layer context provider | | mcp | Interaction | @useatlas/mcp | MCP server lifecycle (stdio + SSE) | | chat | Interaction | @useatlas/chat | Chat SDK bridge — Slack, Teams, Discord, etc. via unified adapter | | jira | Action | @useatlas/jira | Create JIRA tickets from analysis | | email | Action | @useatlas/email | Send email reports via Resend | | nsjail | Sandbox | @useatlas/nsjail | Linux namespace isolation via nsjail | | sidecar | Sandbox | @useatlas/sidecar | HTTP-isolated container sidecar | | vercel-sandbox | Sandbox | @useatlas/vercel-sandbox | Firecracker microVM via @vercel/sandbox | | daytona | Sandbox | @useatlas/daytona | Daytona managed cloud sandbox | | e2b | Sandbox | @useatlas/e2b | E2B Firecracker microVM (managed) |

Source

The definitive type definitions are in src/types.ts. Factory functions and type guards are in src/helpers.ts.