Skip to main content
Plugins add cross-cutting behavior and can contribute components. Typical uses: auth/session helpers, PII filtering, tracing, logging, caching, error policy, rate-limits.

Basic Plugin

A simple plugin uses the @Plugin decorator:

Using Plugins

Attach plugins at app scope:

Dynamic Plugin with Options

For plugins that need runtime configuration, extend DynamicPlugin:

Type-Safe Options with Zod

For complex options with defaults, use Zod schemas with two types:
my-plugin.types.ts
my-plugin.plugin.ts

Option-Derived Providers and Nested Plugins

The providers a plugin derives from its options (dynamicProviders(options) and init({ providers })) are registered before the plugin’s nested plugins are built. A nested plugin can therefore inject them, for example through init({ inject, useFactory }):
This works for both MyPlugin.init(options) and MyPlugin.init({ inject, useFactory }). In the factory form, the factory runs first, and the providers derived from the options it returns are registered before the nested plugins.
Because option-derived providers are registered first, they cannot inject a provider that a nested plugin exports. Keep those dependencies in the nested plugin, or read them at runtime with this.get(...).

Installing a Plugin in Several Apps

Each app that installs a plugin gets its own copy of the plugin’s providers, CONTEXT-scoped ones included. Tools, resources and prompts resolve the providers of the plugins their own app installed, so this.approval, this.featureFlags or this.remember in one app never resolve to another app’s configuration:
A grant made through this.approval inside RefundTool is written to billingStorage, and one made inside DeployTool is written to opsStorage.

Adding Hooks

Plugins can intercept flow stages using hooks. Use this.get(Token) to access providers:

Contributing Skills

Plugins can contribute skills that teach AI how to perform workflows using the plugin’s tools:
Plugin skills are automatically adopted into the app’s skill registry when the plugin is attached. They appear in searchSkills results alongside app-level skills and can be loaded with loadSkill.
Use plugin skills to bundle workflow knowledge with the tools they use. This creates self-contained, reusable functionality modules.

Available Hooks

ToolHook (tools:call-tool)

Intercept tool execution flow:

ListToolsHook (tools:list-tools)

Intercept tool listing flow:

Hook Timing

  • .Will(stage) - runs before the stage
  • .Did(stage) - runs after the stage

DynamicPlugin API

The init() method accepts your plugin’s input options type and returns a provider configuration that the framework uses internally. You don’t need to import or reference the return type—just pass the result directly to the plugins array.

Extending Tool Metadata

Plugins can extend the global tool metadata interface:
Tools can then use this metadata:

Plugin Scope

By default, plugins operate at the app scope - their hooks only fire for requests to that specific app. For cross-app functionality, you can use server scope.

App Scope (Default)

Hooks fire only for requests to the app where the plugin is registered:

Server Scope

Hooks fire at the gateway level for all apps in the server:
Server-scoped plugins can only be used in non-standalone apps (standalone: false). Using scope: 'server' in a standalone app will throw an InvalidPluginScopeError.

When to Use Each Scope

Accessing Other Apps (Server Scope)

Server-scoped plugins can access other apps via scope.apps:

Composition

Plugins compose depth-first at the app level. Later plugins can depend on providers exported by earlier ones.
Put organization-wide concerns (auth, audit, tracing) in plugins so all generated and inline components inherit the behavior without boilerplate.