1. What the current Add-on API can do
- Register additional agent tools.
- Register namespaced slash commands.
- Subscribe to lifecycle events.
- Register actions shown in the Add-ons page.
- Contribute CustomTkinter widgets to supported UI locations.
- Register an implementation for a named application service, where the core integration explicitly resolves that service.
- Declare capability labels.
The Add-on API is an explicit extension API, not unrestricted monkey-patching. A service replacement works only when the relevant Agent Code core code calls resolve_service() for that service name. The currently wired integration in this source includes the Skill Store service. Do not assume every URL or backend (Skills Forge, updates, repositories, etc.) is replaceable unless the host release exposes that service point.
2. Install location and folder structure
~/.local/share/agent-code/addons/
└── sample-addon/
├── addon.json
├── addon.py
├── README.md
└── helpers.pyWindows: %LOCALAPPDATA%\AgentCode\addons\. Add-ons are per-user. The Add-ons page can create a starter template and discover installed add-on folders.
3. Manifest
{
"name": "sample-addon",
"version": "1.0.0",
"description": "Adds a project utility panel",
"author": "Your Name"
}The folder must contain addon.json and addon.py. The manifest name must be a valid identifier (letters, numbers, dot, underscore or hyphen; it must begin with a letter or number).
4. Minimal Add-on
def register(api):
api.register_action(
"Say hello",
lambda: "Hello from my add-on",
description="A simple action"
)
api.register_command(
"status",
lambda args, log: log("Add-on is active", "dim"),
"Show add-on status"
)Commands are namespaced automatically. For an add-on named sample-addon, invoke /sample-addon:status.
5. Add an agent tool
def get_project_label(label="my project"):
return {"label": label, "source": "sample-addon"}
def register(api):
api.register_tool({
"type": "function",
"function": {
"name": "sample_project_label",
"description": "Return a project label.",
"parameters": {
"type": "object",
"properties": {
"label": {"type": "string"}
}
}
}
}, get_project_label)Tool names must be unique across loaded Add-ons. Tools are added to the agent tool registry and follow the application's existing permission and Plan Mode handling.
6. Add UI components
def build_workspace_panel(parent, app):
import customtkinter as ctk
frame = ctk.CTkFrame(parent)
title = ctk.CTkLabel(frame, text="My workspace panel")
title.pack(padx=12, pady=8)
button = ctk.CTkButton(
frame,
text="Show project path",
command=lambda: app.log_line(str(app.project_dir), "dim")
)
button.pack(padx=12, pady=8)
return frame
def register(api):
api.register_ui(
"workspace-panel",
build_workspace_panel,
location="workspace",
label="Workspace panel",
description="An example workspace widget"
)The API accepts sidebar, addons, settings, toolbar and workspace as location labels. In this release, the host currently mounts registered widgets in the sidebar and addons locations; the other labels are accepted by the registration API but are not yet mounted by the main UI. A builder receives (parent, app) and must return a Tk/CustomTkinter widget. Keep widget construction quick; do not perform blocking network or filesystem work on the UI thread.
7. Replace a supported application service
class AlternateSkillStore:
base_url = "https://store.example.invalid/api"
def list_skills(self):
# Implement the interface expected by the host's Skill Store caller.
return []
def register(api):
api.register_service(
"skill_store",
AlternateSkillStore(),
replaces=True,
priority=50,
description="Use our compatible Skill Store backend"
)Service resolution selects the registered implementation with the highest replacement/priority ordering. The implementation must match the interface expected by the specific core integration. Read the host source for the current service contract before replacing it.
8. Lifecycle hooks
def task_started(app=None, task=None, **context):
# Keep this fast. Do not block the GUI thread.
return None
def register(api):
api.on("task_started", task_started)Events currently emitted include tools_loaded, task_started, task_finished, before_tool_call and after_tool_call. Hook callback exceptions are isolated by the manager. Do not assume event payload fields beyond those supplied by the host implementation; inspect the matching call site.
9. Helper modules and dependencies
Place helper modules beside addon.py. The loader adds the add-on folder to Python's import path while loading the add-on, so local imports can work. The current manager does not provide a dependency resolver or install dependencies automatically. Document dependencies in your README and install them in a controlled environment before enabling the add-on.
10. Test and package
- Run
python -m py_compile addon.py. - Install the folder in the Add-ons directory.
- Restart/reload the application as required by the current UI; then inspect the Add-ons page for load errors.
- Verify each UI location, command, tool and service replacement you registered.
- Test with the default service selected and with the add-on disabled to confirm fallback behavior.
cd ~/.local/share/agent-code/addons
zip -r sample-addon-1.0.0.zip sample-addon \
-x '*/__pycache__/*' '*.pyc' '*.env' '*.key'
11. Trust, compatibility and limitations
- Review source code before enabling.
- Do not bundle credentials or private keys.
- Use explicit timeouts and avoid blocking the UI thread.
- Declare Agent Code version compatibility in your release notes.
- Back up user data before testing service replacements.
- Do not modify private core attributes unless the add-on specifically targets that exact version and documents the risk.
The current framework does not include a public signed marketplace, automatic dependency management, arbitrary core monkey-patching, or universal replacement points for every server. Those require additional host APIs and security work.