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
namemust match the parent directory name exactly descriptionis 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:
- Open Settings > Plugins > Add Plugin > Local Path
- Enter the absolute path to your plugin directory
- If the plugin requires configuration, it will show as "Needs Configuration" until you fill in the required fields
To verify your plugin is working:
- Check status shows "Active" in Settings > Plugins
- Send a message that should trigger your skill
- 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
Related
- Channels and Plugins for the extension architecture
- Getting Started for engine setup