Skip to main content

Plugins

Plugins are drop-in bundles that contribute hooks, skills, commands, agents, and MCP servers to an agent without modifying its code. A plugin lives in a directory with a .agentix-plugin/ subdirectory containing a plugin.json manifest.

Plugin directory layout

my-plugin/
└── .agentix-plugin/
├── plugin.json ← manifest (required)
├── hooks/
│ ├── audit.py ← each file must export register(hooks_dict)
│ └── metrics.py
├── skills/ ← SKILL.md files
├── commands/ ← *.md slash command definitions
├── agents/ ← *.md sub-agent definitions
└── .mcp.json ← MCP server configs { "mcpServers": { ... } }

plugin.json (manifest)

{
"name": "my-plugin",
"version": "1.0.0",
"description": "Audit logging and metrics for Agentix.",
"author": "Your Name",
"hooks": true,
"skills": true,
"commands": true,
"agents": true,
"mcp_servers": true,
"file_hashes": {
"hooks/audit.py": "a3f5c2...sha256hex...",
"hooks/metrics.py": "b7e1d9...sha256hex..."
}
}

Manifest fields

FieldTypeDefaultDescription
namestrrequiredUnique plugin identifier
versionstr"0.1.0"Semver version string
descriptionstr""Human-readable description
authorstr | dict""Author name or {"name": ..., "email": ...}
hooksbool | list[str]truetrue = auto-discover all files in hooks/; list of filenames to load specific files only
skillsbool | list[str]truetrue = auto-discover all skills
commandsbool | list[str]truetrue = auto-discover all commands
agentsbool | list[str]truetrue = auto-discover all agent definitions
mcp_serversbool | list[str]truetrue = load all servers from .mcp.json
file_hashesdict[str, str]{}SHA-256 hashes for integrity verification of hook files

Hook file format

Each hook file must export a register(hooks_dict) function that adds HookMatcher instances to the provided dict:

# .agentix-plugin/hooks/audit.py
from agentix import HookMatcher
import logging

async def log_tool(hook_input: dict, context) -> None:
logging.info("AUDIT tool=%s input=%s", hook_input["tool_name"], hook_input.get("tool_input"))

def register(hooks: dict) -> None:
hooks.setdefault("PreToolUse", []).append(
HookMatcher(matcher=None, hooks=[log_tool])
)

Using a plugin

from agentix import AgentixAgentOptions

options = AgentixAgentOptions(
plugins=[
{"path": "./plugins/my-plugin"},
]
)

Multiple plugins are loaded in order; later plugins override earlier ones when asset names conflict.

Hash verification

Agentix verifies the SHA-256 digest of each hook file listed in file_hashes before loading it. If a file's content doesn't match, PluginIntegrityError is raised and the plugin is not loaded.

from agentix.plugin import PluginIntegrityError

try:
options = AgentixAgentOptions(plugins=[{"path": "./plugins/my-plugin"}])
except PluginIntegrityError as e:
print(f"Plugin tampered: {e}")

Generate hashes for a new plugin:

python -c "
import hashlib, pathlib
for f in pathlib.Path('.agentix-plugin/hooks').glob('*.py'):
h = hashlib.sha256(f.read_bytes()).hexdigest()
print(f' \"{f.relative_to(\".\")}\": \"{h}\"')
"

Trusted plugin paths

Skip hash verification for first-party or vendored plugins:

options = AgentixAgentOptions(
plugins=[{"path": "./plugins/my-plugin"}],
trusted_plugin_paths=["./plugins/internal-plugin"],
)

Discovering plugins programmatically

from agentix.plugin import discover_plugins, PluginManager

# Find all plugins in a directory
plugin_paths = discover_plugins(["./plugins", "~/.agentix/plugins"])

manager = PluginManager()
manager.load_from_paths(plugin_paths)
print(manager.get_status())