Home / Docs / SDK & Addons

SDK & Addons

OpenCaddis SDK & Addons

Build custom addons that extend OpenCaddis with new services, settings UI, and configuration — auto-discovered at startup with zero registration code.

Initial Release

The OpenCaddis SDK is in its initial release. The addon interfaces are stable and usable today, but we're actively expanding the surface area. Our goal is to build a full addon marketplace where the community can share and discover addons. Expect the SDK to grow significantly in the coming releases.

Overview

OpenCaddis has two extensibility layers:

LayerPackagePurposeInterface
FabrCore Plugins Fabr.Sdk Add tools (functions) that agents can call — web browsing, file access, API calls, etc. IFabrPlugin
OpenCaddis Addons OpenCaddis.Sdk Extend the application itself — register services, provide settings UI, store addon configuration. ICaddisAddon

This page covers OpenCaddis Addons — the application-level extensibility system. For adding agent tools, see the Plugins documentation.

How It Works

At startup, OpenCaddis scans all assemblies in the output directory for classes that:

  1. Have the [CaddisAddon] attribute
  2. Implement the ICaddisAddon interface

When a match is found, OpenCaddis instantiates the addon and calls ConfigureServices(), giving it full access to the DI container. No manual registration is needed — just drop your compiled DLL into the output directory and restart.

Discovery (from Program.cs)
// Force-load assemblies from the output directory
foreach (var dll in Directory.GetFiles(AppContext.BaseDirectory, "*.dll"))
{
    try { Assembly.LoadFrom(dll); } catch { }
}

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    foreach (var type in assembly.GetTypes())
    {
        if (type.GetCustomAttribute<CaddisAddonAttribute>() is not null
            && typeof(ICaddisAddon).IsAssignableFrom(type))
        {
            var addon = (ICaddisAddon)Activator.CreateInstance(type)!;
            addon.ConfigureServices(builder.Services);
        }
    }
}

SDK Interfaces

ICaddisAddon

The core interface every addon implements.

OpenCaddis.Sdk/ICaddisAddon.cs
public interface ICaddisAddon
{
    string Name { get; }
    void ConfigureServices(IServiceCollection services);
    Type? SettingsComponentType => null;
}
MemberDescription
NameDisplay name for the addon. Used in the UI and logs.
ConfigureServicesCalled at startup. Register your services into the DI container here.
SettingsComponentTypeOptional. Return a Blazor component Type to render addon-specific settings in the OpenCaddis UI. Defaults to null (no settings UI).

CaddisAddonAttribute

Marks a class for auto-discovery. Required alongside ICaddisAddon.

OpenCaddis.Sdk/CaddisAddonAttribute.cs
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class CaddisAddonAttribute : Attribute
{
    public string Name { get; }
    public CaddisAddonAttribute(string name) => Name = name?.Trim() ?? string.Empty;
}

ICaddisConfigService

Inject this service to read and write addon-specific configuration. Configuration is persisted to opencaddis.json as extension data alongside agent definitions.

OpenCaddis.Sdk/ICaddisConfigService.cs
public interface ICaddisConfigService
{
    bool ConfigurationExists();
    Task<T?> GetAddonConfigAsync<T>(string sectionName) where T : class;
    Task SetAddonConfigAsync<T>(string sectionName, T config) where T : class;
}
MethodDescription
ConfigurationExists()Returns true if opencaddis.json exists on disk.
GetAddonConfigAsync<T>Reads a named section from the config file and deserializes it to T. Returns null if the section doesn't exist.
SetAddonConfigAsync<T>Serializes T and writes it as a named section in the config file. Creates the file if it doesn't exist.

Building an Addon

1. Create the project

Terminal
dotnet new classlib -n MyAddon -f net10.0
cd MyAddon
dotnet add reference ../OpenCaddis.Sdk/OpenCaddis.Sdk.csproj

2. Implement ICaddisAddon

MyAddon.cs
using Microsoft.Extensions.DependencyInjection;
using OpenCaddis.Sdk;

[CaddisAddon("MyAddon")]
public class MyAddon : ICaddisAddon
{
    public string Name => "My Custom Addon";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<MyCustomService>();
    }

    // Optional: return a Blazor component for settings UI
    public Type? SettingsComponentType => null;
}

3. Use configuration (optional)

If your addon needs persistent configuration, inject ICaddisConfigService into your services.

MyCustomService.cs
public class MyCustomService
{
    private readonly ICaddisConfigService _config;

    public MyCustomService(ICaddisConfigService config)
    {
        _config = config;
    }

    public async Task<MySettings> GetSettingsAsync()
    {
        return await _config.GetAddonConfigAsync<MySettings>("MyAddon")
            ?? new MySettings();
    }

    public async Task SaveSettingsAsync(MySettings settings)
    {
        await _config.SetAddonConfigAsync("MyAddon", settings);
    }
}

public class MySettings
{
    public string ApiEndpoint { get; set; } = "";
    public bool Enabled { get; set; } = true;
}

The configuration is stored as a named section in opencaddis.json:

opencaddis.json
{
  "Agents": [ ... ],
  "MyAddon": {
    "ApiEndpoint": "https://api.example.com",
    "Enabled": true
  }
}

4. Deploy

Build your addon and copy the output DLL (and any dependencies) into the OpenCaddis output directory. Restart OpenCaddis — the addon is discovered and registered automatically.

Terminal
dotnet build -c Release
cp bin/Release/net10.0/MyAddon.dll /path/to/opencaddis/

Addons + FabrCore Plugins

Addons and FabrCore plugins work together. A common pattern is to create an addon that registers services, then create a FabrCore plugin that uses those services to provide agent tools.

Example: Addon registers a service, plugin exposes it as a tool
// Addon: registers shared services at startup
[CaddisAddon("Weather")]
public class WeatherAddon : ICaddisAddon
{
    public string Name => "Weather";
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient<WeatherApiClient>();
    }
}

// FabrCore Plugin: gives agents a weather tool
[PluginAlias("Weather")]
public class WeatherPlugin : IFabrPlugin
{
    private WeatherApiClient _client = default!;

    public Task InitializeAsync(
        AgentConfiguration config, IServiceProvider sp)
    {
        _client = sp.GetRequiredService<WeatherApiClient>();
        return Task.CompletedTask;
    }

    [Description("Get the current weather for a city")]
    public async Task<string> GetWeather(
        string city)
    {
        return await _client.GetForecastAsync(city);
    }
}
Tip

When combining addons and plugins in a single assembly, both are discovered automatically — the addon via [CaddisAddon] and the plugin via [PluginAlias]. No extra wiring needed.

What's Coming

The SDK is in its initial release and we have plans to expand it significantly:

Addon Marketplace

A community marketplace where developers can publish, discover, and install addons. Browse, install, and update addons directly from the OpenCaddis UI.

NuGet Distribution

Publish OpenCaddis.Sdk as a NuGet package so addon authors can reference it without cloning the repo.

Richer UI Hooks

Beyond settings components — addons will be able to contribute sidebar panels, chat decorators, and custom pages to the OpenCaddis UI.

Addon Sandboxing

Security boundaries for addons — permission scoping, resource limits, and trust levels for community-published addons.

We want the addon ecosystem to be the primary way OpenCaddis grows. The built-in plugins demonstrate what's possible; addons let the community build what's needed.

Quick Reference

ItemDetails
SDK ProjectOpenCaddis.Sdk
Target Frameworknet10.0
Framework ReferenceMicrosoft.AspNetCore.App
Addon InterfaceICaddisAddon
Discovery Attribute[CaddisAddon("Name")]
Config ServiceICaddisConfigService
Config Storageopencaddis.json (extension data)
DeploymentCopy DLL to output directory, restart
Documentation