Extension Registration
Register extensions at compile time with the register_extension! macro, which submits an ExtensionRegistration factory to the inventory crate for discovery.
On this page
Extensions register at compile time using the register_extension! macro, which leverages the inventory crate for zero-cost plugin discovery.
The register_extension! Macro
After implementing the Extension trait, register your extension:
use systemprompt::extension::prelude::*;
#[derive(Debug, Default)]
pub struct MyExtension;
impl Extension for MyExtension {
fn metadata(&self) -> ExtensionMetadata {
ExtensionMetadata {
id: "my-extension",
name: "My Extension",
version: env!("CARGO_PKG_VERSION"),
}
}
// ... other methods
}
register_extension!(MyExtension);
Macro Variants
Type Registration
For types that implement Default:
register_extension!(MyExtension);
Expands to:
inventory::submit! {
ExtensionRegistration {
factory: || Arc::new(MyExtension::default()) as Arc<dyn Extension>,
}
}
Expression Registration
For types requiring configuration:
register_extension!(MyExtension::new(config));
Expands to:
inventory::submit! {
ExtensionRegistration {
factory: || Arc::new(MyExtension::new(config)) as Arc<dyn Extension>,
}
}
ExtensionRegistration
The registration struct:
#[derive(Debug, Clone, Copy)]
pub struct ExtensionRegistration {
pub factory: fn() -> Arc<dyn Extension>,
}
inventory::collect!(ExtensionRegistration);
Preventing Linker Stripping
Rust's linker removes unused code. Extensions in separate crates may be stripped if not directly referenced. The template's src/lib.rs prevents this by re-exporting each extension crate:
pub use systemprompt::{cli, *};
pub use systemprompt_web_extension as web;
The re-export keeps the crate reachable from the binary, so its inventory registrations survive linking.
Adding a New Extension
- Create extension crate:
# extensions/my-extension/Cargo.toml
[package]
name = "my-extension"
version = "0.1.0"
edition.workspace = true
[dependencies]
systemprompt = { workspace = true, features = ["full"] }
- Implement and register:
// extensions/my-extension/src/lib.rs
use systemprompt::extension::prelude::*;
#[derive(Debug, Default)]
pub struct MyExtension;
impl Extension for MyExtension {
fn metadata(&self) -> ExtensionMetadata {
ExtensionMetadata {
id: "my-extension",
name: "My Extension",
version: env!("CARGO_PKG_VERSION"),
}
}
}
register_extension!(MyExtension);
pub const PREFIX: &str = "my-extension";
- Add to workspace:
# Cargo.toml (root)
[workspace]
members = [
"extensions/my-extension",
# ...
]
- Add the dependency and re-export it so the linker keeps it:
# Cargo.toml (root)
[dependencies]
my-extension = { workspace = true }
// src/lib.rs
pub use my_extension;
The Inventory Crate
The inventory crate provides compile-time plugin registration:
- Zero runtime cost - Registration happens at compile time
- No global mutable state - Type-safe collection
- Automatic discovery - No manual registration list
- Linker-based - Uses linker sections for collection
Verification
Check registered extensions:
systemprompt plugins list
Use --type compiled to show only compile-time extensions, or systemprompt plugins show <id> for details on one.