//  AUTHORING

Writing a plugin

From cxagent plugin new to an entry in the catalog. Everything here is in the SDK; this page is the way in.

What a plugin is

A plugin is a library cxagent did not ship, loaded into a session at run time. It can add tools the model may call, commands a person types, and a block of guidance to the system prompt. It declares all of this in a sidecar file that cxagent reads before loading it, so the load prompt can show what a plugin claims without running any of its code.

Two kinds: managed, a .NET assembly that runs inside cxagent's own process and is portable across every platform cxagent runs on; and native, a C library built per platform, which runs in a separate host process. Neither is sandboxed — a plugin can do what its process can. What cxagent enforces is the decision to load it, asked every time with a hash of everything it would load.

The path

  1. cxagent plugin new weather --lang csharp — a working project: a sidecar, one tool with a per-call gate, the plugin, a README and a test project. --lang c gives the same in C, with the C SDK.
  2. cxagent plugin check — every load-time check, with no session and no model
  3. cxagent plugin call — one real call through load, start, the gate and invoke
  4. Test it — the same from xunit, NUnit or MSTest, with CxAgent.Plugins.Testing
  5. cxagent plugin pack — the zips, their hashes and the catalog entry
  6. cxagent plugin verify — what a user's install would get, with no plugin code run
  7. Submit it — paste the entry into the "Submit a plugin" issue; a bot verifies it and opens the pull request

The C# tutorial and the C tutorial walk this whole path with a real plugin.

The sidecar

What plugin new writes for a plugin called weather:

{
  "pluginContract": 4,
  "kind": "managed",
  "name": "weather",
  "version": "1.0.0",
  "spawns": false,
  "instructions": "weather_greet greets someone by name.",
  "tools": [
    {
      "name": "weather_greet",
      "description": "Greets someone by name. Asks before greeting anyone called admin.",
      "inputSchema": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"] },
      "gated": "dynamic"
    }
  ]
}

pluginContract is the version of the plugin interface it was written against; cxagent loads a range of them and refuses one outside it. Every field is in the contract.

Deciding what needs gating

Each tool declares whether a call needs a person:

gatedWhat it meansUse it when
trueevery call asks the tool can always do something the user would want to see first
falseno call asks the tool only ever reads, or only ever touches your own state
"dynamic"your code decides, per call, from the arguments the tool is dangerous only sometimes

The shipped csharp-lsp tools are "dynamic": they run unasked inside the folder you started in, and ask before reading a file outside it. Declaring them true would make every lookup a prompt, which trains a user to click through them. Permission covers the rest.

Going deeper