# Plugins

> Plugins add capabilities to the server — LLM providers, tools, tracing, and anything else your agent needs.

Plugins add capabilities to the server. Every piece of functionality in Polaris — LLM providers, tool registries, tracing, persistence — is delivered through a plugin. The server itself ships with no built-in behavior. You assemble what you need.

```rust
use polaris_ai::system::server::Server;
use polaris_ai::system::plugin::PluginGroup;
use polaris_ai::plugins::DefaultPlugins;
use polaris_ai::models::ModelsPlugin;
use polaris_ai::models::AnthropicPlugin;


Server::new()
    .add_plugins(DefaultPlugins::new().build())
    .add_plugins(ModelsPlugin)
    .add_plugins(AnthropicPlugin::from_env("ANTHROPIC_API_KEY"))
    .run().await?;
```

This server has tracing, time utilities, a model registry, and the Anthropic LLM provider. Each `add_plugins` call registers one plugin (or a group of plugins) and its resources.

## The Plugin trait

A plugin supplies a synchronous `build` method. `ready` and `cleanup` are asynchronous; optional `update` and `tick_schedules` handle scheduled work.

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


pub struct MetricsPlugin;


impl Plugin for MetricsPlugin {
    const ID: &'static str = "myapp::metrics";
    const VERSION: Version = Version::new(0, 1, 0);


    fn build(&self, server: &mut Server) {
        // Insert resources, register local resource factories
    }


    async fn ready(&self, server: &mut Server) {
        // Cross-plugin setup after all plugins are built
    }


    async fn cleanup(&self, server: &mut Server) {
        // Graceful shutdown (flush buffers, close connections)
    }


    fn dependencies(&self) -> Vec<PluginId> {
        vec![]
    }
}
```

Every plugin has a unique `ID` string and a semantic `VERSION`. The server uses the ID for duplicate detection and dependency resolution.

## build()

The `build` method is where you insert resources and register APIs. It runs once, in dependency order, when the server starts. Keep it lightweight — heavy initialization belongs in `ready` or in systems.

```rust
fn build(&self, server: &mut Server) {
    server.insert_global(MetricsConfig { interval_secs: 30 });
    server.register_local(MetricsBuffer::new);
}
```

If your plugin calls `server.add_plugins(...)` inside `build`, the sub-plugin is built immediately. This is how plugin groups compose.

## ready()

The `ready` method runs after every plugin has been built, also in dependency order. Use it when your plugin needs resources that another plugin provides during its `build` phase.

```rust
async fn ready(&self, server: &mut Server) {
    let config = server.get_resource::<MetricsConfig>()
        .expect("MetricsConfig must be inserted during build");
    // Validate, connect, or perform cross-plugin setup
}
```

## cleanup()

The `cleanup` method runs when the server shuts down. Unlike `build` and `ready`, cleanup runs in **reverse** dependency order — dependents clean up before their dependencies. This means your plugin can still use the resources it depends on during cleanup.

## Dependencies

A plugin declares its dependencies by returning a list of `PluginId` values from the `dependencies` method. The server validates that all dependencies are present and topologically sorts plugins so that dependencies are always built first.

```rust
use polaris_ai::models::ModelsPlugin;


fn dependencies(&self) -> Vec<PluginId> {
    vec![PluginId::of::<ModelsPlugin>()]
}
```

`server.finish().await` returns a `ServerBuildError` when dependencies are missing or cyclic. Handle the error before creating contexts or accepting work.

## Capability contracts

Current Polaris plugins can declare the capabilities they provide, require or extend through `PluginAccess`. A capability is a resource or API type with a `Contract` version. This lets a consumer depend on `ToolRegistry`, for example, rather than a particular provider plugin.

* `provides::<T>(version)` declares a capability this plugin inserts.
* `requires::<T>(version_req)` declares read access to a required capability.
* `extends::<T>(version_req)` declares a contribution to a capability, such as registering tools.
* `optionally_requires::<T>(version_req)` allows an integration when a compatible provider exists.

The `#[plugin]` macro derives these declarations from typed `Requires<T>`, `Extends<T>` and `Optional<T>` build parameters. `dependencies()` remains available for pure ordering and ready-phase relationships. See [capability contracts and the plugin macro](https://github.com/RadiantAILabs/polaris/blob/main/docs/reference/plugins.md/) for the full API.

## Adding plugins to the server

You add plugins with `server.add_plugins(...)`. This accepts a single plugin or a `PluginGroupBuilder` containing multiple plugins.

```rust
let mut server = Server::new();
server
    .add_plugins(DefaultPlugins::new().build())
    .add_plugins(ModelsPlugin)
    .add_plugins(AnthropicPlugin::from_env("ANTHROPIC_API_KEY"));
server.run().await?;
```

Await `finish()` to build the server and then run work yourself. Await `run()` for a long-running service; it waits for shutdown. Await `cleanup()` after manually managed work to flush exporters and release plugin resources.

## DefaultPlugins and MinimalPlugins

Polaris ships two plugin groups for common setups.

`DefaultPlugins` includes `ServerInfoPlugin`, `TimePlugin`, and `TracingPlugin`. It covers what most applications need for logging, timing, and server metadata.

```rust
use polaris_ai::plugins::DefaultPlugins;


server.add_plugins(DefaultPlugins::new().build());
```

You can customize it before building:

```rust
use polaris_ai::plugins::{DefaultPlugins, FmtConfig, TracingFormat};
use tracing::Level;


server.add_plugins(
    DefaultPlugins::new()
        .with_log_level(Level::DEBUG)
        .with_fmt(FmtConfig::default().format(TracingFormat::Json))
        .build()
);
```

`MinimalPlugins` includes only `ServerInfoPlugin` and `TimePlugin`. It is useful for tests where you do not want tracing output.

```rust
use polaris_ai::plugins::MinimalPlugins;


server.add_plugins(MinimalPlugins.build());
```

Both groups return a `PluginGroupBuilder`, which lets you disable individual plugins with `.disable::<T>()` or insert plugins at specific positions with `.add_before::<P, Target>(plugin)` and `.add_after::<P, Target>(plugin)`.

## Writing a plugin

Here is a complete plugin that provides application configuration as a global resource.

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


pub struct AppConfig {
    pub model_name: String,
    pub max_turns: usize,
}


impl GlobalResource for AppConfig {}


pub struct AppConfigPlugin {
    pub model_name: String,
    pub max_turns: usize,
}


impl Plugin for AppConfigPlugin {
    const ID: &'static str = "myapp::config";
    const VERSION: Version = Version::new(0, 1, 0);


    fn build(&self, server: &mut Server) {
        server.insert_global(AppConfig {
            model_name: self.model_name.clone(),
            max_turns: self.max_turns,
        });
    }
}
```

Systems can then access `AppConfig` through `Res<AppConfig>`. The plugin owns the configuration; the systems consume it. This separation keeps your server assembly in one place and your agent logic in another.

```rust
Server::new()
    .add_plugins(DefaultPlugins::new().build())
    .add_plugins(AppConfigPlugin {
        model_name: "claude-sonnet-4-20250514".into(),
        max_turns: 10,
    })
    .run().await?;
```

For plugins that need to interact with other plugins during `build` through shared registries, see [APIs](/docs/polaris/advanced-topics/apis.md). For a full walkthrough of building a plugin with `ready`, `cleanup`, and local resources, see the [Writing custom plugins](/docs/polaris/guides/writing-custom-plugins.md) guide.
