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
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 cgives the same in C, with the C SDK.cxagent plugin check— every load-time check, with no session and no modelcxagent plugin call— one real call through load, start, the gate and invoke- Test it — the same from xunit, NUnit or MSTest, with CxAgent.Plugins.Testing
cxagent plugin pack— the zips, their hashes and the catalog entrycxagent plugin verify— what a user's install would get, with no plugin code run- 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:
gated | What it means | Use it when |
|---|---|---|
true | every call asks | the tool can always do something the user would want to see first |
false | no 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
- The contract — the lifecycle, the sidecar, permission, commands, settings, and starting work in your own session
- The command line
— every option of
new,check,call,packandverify - The C SDK
— the
cxpAPI and building for every platform - The C ABI
— what crosses each call, for a plugin written without
cxp - Publishing — upload, verify, submit, and what review checks