This guide helps contributors add new plugins quickly with the runtime contract used in this repo.
§1) Scaffold a new plugin
npm run plugin:new -- my-plugin-idThis generates:
src/plugins/community/my-plugin-id.tstest/community/my-plugin-id.test.ts
§2) Implement actions
Use invoke(action, input) as your action router and return PluginResult.
Return
{ ok: true, data }for success.Return
{ ok: false, error }for handled errors.
§3) Declare capabilities and secrets
In your plugin class:
Set
capabilitiesto the minimum required (seeCAPABILITIES.md).Set
requiredSecretswhen needed so runtime bootstrap can fail fast on missing credentials.
§4) Register plugin
Add your plugin to src/plugins/registry.ts.
§5) Enable plugin
Add plugin ID to ENABLED_PLUGINS and required outbound domains to ALLOWED_HOSTS.
§6) Test plugin
npm testFor constrained environments, run static checks first:
npm run typecheck§7) Expose via skill catalog (optional)
If your plugin has an opinionated default action, add an entry to SKILL_CATALOG in src/core/skills.ts. Skills are the human-facing equivalent of plugin actions — they are what /playground lists and what /skills/invoke/{skillId} routes through.
§8) Durable state (optional)
Need per-agent state? Use the existing AgentSessionDO via env.AGENT_SESSIONS:
const id = this.ctx.env.AGENT_SESSIONS!.idFromName("my-session");
const stub = this.ctx.env.AGENT_SESSIONS!.get(id);
const response = await stub.fetch("https://do/messages", {
method: "POST",
body: JSON.stringify({ role: "tool", content: JSON.stringify(result) })
});§Design guidelines
Keep plugins focused and single-purpose.
Do not hardcode credentials; use
ctx.envandctx.config.Use the runtime's restricted fetch (
ctx.fetch) or the sharedJsonHttpConnector.Add at least one happy-path and one failure-path test per action.
Emit structured logs via
console.log(JSON.stringify(...))so they show up inwrangler tail.Treat every
input: unknownas untrusted — parse + validate before use.