Templates
The repo ships two extension templates as starting points. Copy one, rename it, fill in the tools, done.
Location: extensions/template-rust/ and extensions/template-python/.
What's shared
Both templates follow the same wire protocol and directory shape:
<your-ext>/
├── plugin.toml # manifest (see ./manifest.md)
├── README.md # what the extension does
├── <binary or script> # stdio-RPC entry point
└── ... # build files specific to the language
The agent talks to both in the same JSON-RPC 2.0 shape:
initialize— handshake; returns{server_version, tools, hooks}tools/<name>— tool invocation; returns the tool's resulthooks/<name>— hook invocation (when any hook is declared)
Line-delimited JSON over stdin/stdout. stderr is forwarded to the agent's tracing output — that's your debug log.
Rust template (extensions/template-rust/)
Standalone Cargo project outside the agent workspace — its own
Cargo.toml, own Cargo.lock, own target/. Keeps your extension's
deps independent of the agent's.
template-rust/
├── Cargo.toml
├── Cargo.lock
├── plugin.toml
├── README.md
├── src/
│ └── main.rs # JSON-RPC loop
└── target/ # (gitignore)
src/main.rs implements:
#![allow(unused)] fn main() { // pseudocode loop { let line = read_line_from_stdin(); let req: JsonRpcRequest = parse(line); let result = match req.method.as_str() { "initialize" => handshake_info(), "tools/ping" => ping(req.params), "tools/add" => add(req.params), "hooks/before_message" => pass(), _ => method_not_found(), }; write_line_to_stdout(json!({ "jsonrpc": "2.0", "id": req.id, "result": result })); } }
Build with cargo build --release; the release binary at
./target/release/template-rust is what plugin.toml::transport.command
points at.
Python template (extensions/template-python/)
template-python/
├── plugin.toml
├── main.py # #!/usr/bin/env python3
└── README.md
stdlib only (no pip install). Same JSON-RPC loop over stdin/stdout.
Logs to stderr via print(..., file=sys.stderr).
Good for quick extensions where starting a Python interpreter per tool call is acceptable (batch workloads, cron-ish tasks, one-off scripting).
Promoting a template to your own extension
flowchart LR
COPY[copy template-rust<br/>to my-extension] --> EDIT[edit plugin.toml<br/>id, version, tools]
EDIT --> CODE[implement tools/...]
CODE --> BUILD[cargo build --release]
BUILD --> VAL[agent ext validate<br/>./my-extension/plugin.toml]
VAL --> INSTALL[agent ext install<br/>./my-extension --link --enable]
INSTALL --> DOCTOR[agent ext doctor<br/>--runtime]
Conventions in the shipped templates
plugin.tomldeclares the minimum required capabilities — no phantom hooks or toolsrequires.bins/requires.envleft empty; add your own[context] passthrough = false— opt in explicitly when you need per-agent / per-session state- License left blank — pick one and add it to
[meta]
Gotchas
- Rust template builds in its own workspace. Don't
cargo addfrom the repo root — that edits the agent workspace, not the extension. - Python template spawns a new interpreter per extension, not per
tool call. Stdin/stdout stay open for the life of the process.
Don't
exitafter one tool call. - JSON-RPC ids must echo back. If your handler drops the
idfield, the agent can't correlate the reply.