# Writing custom plugins

> Package resources and startup wiring in a small, testable plugin.

A plugin supplies capabilities to a server. Use one to register per-run resources, shared services, APIs or tools; keep the agent’s control flow in its graph.

## Start with one resource

This complete program uses the dependencies from [Installation](/docs/polaris/getting-started/installation.md). Replace `src/main.rs` with:

```rust
use polaris_ai::system::plugin::{Plugin, Version};
use polaris_ai::system::resource::LocalResource;
use polaris_ai::system::server::Server;


#[derive(Default)]
struct Scratchpad {
    notes: Vec<String>,
}
impl LocalResource for Scratchpad {}


struct ScratchpadPlugin;
impl Plugin for ScratchpadPlugin {
    const ID: &'static str = "hello::scratchpad";
    const VERSION: Version = Version::new(0, 1, 0);
    fn build(&self, server: &mut Server) {
        server.register_local(Scratchpad::default);
    }
}


#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut server = Server::new();
    server.add_plugins(ScratchpadPlugin);
    server.finish().await?;
    let ctx = server.create_context();
    assert!(ctx.get_resource::<Scratchpad>()?.notes.is_empty());
    println!("Scratchpad ready");
    Ok(())
}
```

Run `cargo run`. It prints `Scratchpad ready` after checking that the execution context has an empty scratchpad.

`register_local` installs a factory: each new context receives its own `Scratchpad`. Use a local resource for per-run state. Shared immutable services instead belong in global resources; see [Resources](/docs/polaris/core-concepts/resources.md).

## Understand the lifecycle

* `build` performs synchronous registration and assembly.
* `ready` performs asynchronous finalisation after plugins have been built.
* `server.finish().await` resolves dependencies and awaits startup.
* `server.run().await` runs the managed lifecycle when you need scheduling and shutdown.

A `ready` override must be `async fn ready(&self, server: &mut Server)`. A cleanup override is asynchronous too. Do not copy older synchronous lifecycle signatures into this revision.

## Declare what your plugin needs

If your plugin reads or extends a capability supplied by another plugin, declare that relationship. The current API supports typed `Requires<T>`, `Extends<T>` and `Optional<T>` build parameters through `#[plugin]`, plus explicit `PluginAccess` declarations for manual implementations. Capability types implement `Contract` to carry their version.

Use `dependencies()` for an explicit plugin ordering requirement where appropriate. The repository’s [FileToolsPlugin](https://github.com/RadiantAILabs/polaris/blob/5ff84646db63589d58bf87956976d749b8c754de/examples/src/plugins/file_tools.rs) shows the small manual pattern: depend on `ToolsPlugin`, then register tools during `build`.

See [Plugins](/docs/polaris/core-concepts/plugins.md) for typed capability registration and [the public plugin reference](https://github.com/RadiantAILabs/polaris/blob/5ff84646db63589d58bf87956976d749b8c754de/docs/reference/plugins.md/) for the full contract API. Declaring a provider dependency does not itself add that provider to the server.

## Keep assembly visible

Add plugins in one place near application startup. Give each plugin a stable, namespaced `ID`, an explicit `VERSION` and documentation listing its resources, APIs and dependencies. Test it with only its declared dependencies so unrelated default plugins do not hide missing wiring.

When a plugin owns a long-lived service, provide shutdown behaviour as well as startup. Await the managed lifecycle in the application; a discarded future does not run it.
