Building Plugins

A step-by-step guide to creating your first Animus plugin.

Philosophy: Skills First

Animus plugins follow an opinionated approach: teach agents to use tools, don't wrap tools for agents.

Agents already know Bash, CLI tools, stdout, pipes, and exit codes. The most natural way to give an agent a new capability is to write a script, document it in a SKILL.md, and let the agent run it.

  • Token efficiency: A SKILL.md uses ~500 tokens. An equivalent MCP server consumes 13,000-18,000 tokens in tool schemas.
  • Composability: CLI scripts pipe, chain, and redirect naturally.
  • Self-modification: An agent that uses scripts can create new tools on its own.

Use MCP only when credential isolation via transport is needed, persistent connections are required (WebSocket, database pools), you need structured access control, or a third party already publishes an MCP server.

The Plugin Gradient

Not every plugin needs every feature. Start simple and move up only when a lower level can't solve the problem.

Level Pattern When to Use Example
1. Pure Skill SKILL.md documenting existing CLI tools or APIs Teaching the agent to use something that already exists Weather (curl + free APIs)
2. Skill + Scripts SKILL.md + bundled scripts in scripts/ Agent needs a custom CLI tool that doesn't exist yet Agent Browser
3. Skill + Scripts + Credentials Level 2 + run_with_credentials for secret injection Script needs API keys the agent shouldn't see Nano Banana Pro (Gemini API)
4. Skill + MCP SKILL.md for knowledge + MCP server for structured tools Persistent connections, hundreds of entities, or third-party MCP server Home Assistant

Plugin Structure

Every plugin lives in its own directory:

my-plugin/
├── plugin.json                # Required: plugin manifest
├── config.schema.json         # Optional: credential/config form definition
├── icon.svg                   # Optional: plugin icon (256x256)
├── skills/
│   └── my-plugin/
│       ├── SKILL.md           # Agent Skills standard format
│       ├── scripts/           # Optional: bundled CLI tools
│       │   └── my-script.js
│       └── references/        # Optional: deep-dive docs (loaded on demand)
│           └── advanced.md
└── tools/                     # Optional: MCP server definitions
    ├── mcp.json
    └── servers/
        └── my-server.js

Step 1: Create the Manifest

Create plugin.json:

{
  "name": "my-plugin",
  "displayName": "My Plugin",
  "version": "1.0.0",
  "description": "What this plugin does (max 200 chars)",
  "author": { "name": "your-name" },
  "license": "MIT",
  "components": {
    "skills": "./skills/"
  },
  "permissions": {
    "network": true
  }
}

Rules:

  • name: Lowercase alphanumeric + hyphens only (^[a-z0-9-]+$). Used as the unique ID and namespace.
  • components: Only include the component types your plugin actually uses. Omit the rest.
  • permissions: Declare what the plugin needs. Shown to the user before installation.

Step 2: Write the Skill

Create skills/my-plugin/SKILL.md:

---
name: my-plugin
description: >
  What this skill does and when to use it. Include trigger
  phrases the user might say. Max 1024 chars. The SDK uses
  this to decide when to load the full skill content.
allowed-tools: Bash
---

# My Plugin

## When to Use

Use this skill when the user asks about [topic].

## Core Commands

[Document complete, copy-pasteable commands with example output]

Rules:

  • Frontmatter name must match the parent directory name exactly
  • description is critical for task-matching. Include trigger words and phrases.
  • allowed-tools: Space-delimited list of tools the skill needs (e.g., Bash Read Write)
  • Body: Write as if teaching a colleague. Include complete commands with expected output.
  • Use references/ for deep-dive documentation. The SDK loads the main SKILL.md first, references on demand.

Step 3: Add Credentials (If Needed)

If your plugin needs API keys or other secrets, create config.schema.json:

{
  "fields": [
    {
      "key": "API_KEY",
      "label": "Service API Key",
      "type": "secret",
      "required": true,
      "helpText": "Get your key at https://example.com/settings/api"
    }
  ]
}

Reference it in plugin.json by adding: "configSchema": "./config.schema.json"

Available field types: text, secret (encrypted at rest with AES-256-GCM), url, number, select, text-list, toggle.

In your SKILL.md, teach the agent to use run_with_credentials to access secrets:

run_with_credentials({
  command: "node plugins/my-plugin/scripts/my-script.js --query \"...\""
  credentialRef: "my-plugin.API_KEY",
  envVar: "API_KEY"
})

This injects the decrypted credential as an environment variable in the subprocess. The agent never sees the raw value.

Step 4: Add MCP Tools (If Needed)

For plugins that need persistent connections or structured tool access, create tools/mcp.json:

{
  "my-server": {
    "command": "node",
    "args": ["${PLUGIN_ROOT}/tools/servers/my-server.js"],
    "env": {
      "API_KEY": "${config.API_KEY}"
    },
    "description": "Description of what these tools do"
  }
}

Update plugin.json to add "tools": "./tools/mcp.json" under components.

  • ${PLUGIN_ROOT} is substituted with the plugin's absolute path at load time
  • ${config.*} is substituted with decrypted config values at runtime
  • Tools are namespaced as my-plugin__my-server__tool_name

Step 5: Install and Test

Install your plugin through the web interface:

  1. Open Settings > Plugins > Add Plugin > Local Path
  2. Enter the absolute path to your plugin directory
  3. If the plugin requires configuration, it will show as "Needs Configuration" until you fill in the required fields

To verify your plugin is working:

  1. Check status shows "Active" in Settings > Plugins
  2. Send a message that should trigger your skill
  3. Check the Mind page to see if the skill was invoked

Common issues:

Problem Cause Fix
"Needs Configuration" Required config fields not filled Settings > Plugins > Configure
Skill not loading Name mismatch SKILL.md frontmatter name must match parent directory name
MCP server not starting Bad command path Check ${PLUGIN_ROOT} resolution
Credentials not injecting Wrong credentialRef format Must be plugin-name.CONFIG_KEY (dot-separated)

Packaging with anipack

When your plugin is ready for distribution, use anipack to validate and build an .anpk package:

# Install anipack
npm install -g anipack

# Validate your plugin structure
anipack validate ./my-plugin

# Build a distributable .anpk package
anipack build ./my-plugin

# Build and sign (for store publishing)
anipack build ./my-plugin --sign --key ./keys/my-key.key

The .anpk format is a signed ZIP archive containing your plugin files, checksums, and metadata. Signed packages can be published to the Animus Store:

anipack publish ./my-plugin-1.0.0.anpk

Run anipack inspect <package.anpk> to examine a built package before publishing.

Plugin Component Types

Beyond skills and tools, plugins can include up to seven component types:

Component Use Case
Skills Natural language capability descriptions
Tools MCP server definitions for structured tool access
Context Inject data into mind sessions (e.g., calendar events)
Hooks React to lifecycle events (on message, on tick)
Decisions Add decision points the agent can choose
Triggers Automated actions on conditions (time, event)
Agents Sub-agent templates for delegation

Most plugins only need skills (and optionally tools). Only include the components your plugin actually uses.

Credential Security

Plugin credentials are encrypted with AES-256-GCM before storage. The engine:

  • Never logs credential values
  • Stores encrypted credentials in the local database only
  • Decrypts only when injecting into a plugin's subprocess environment
  • Strips agent provider keys (e.g., ANTHROPIC_API_KEY) from plugin child processes