Skip to content

Plugins

The plugin system lets you package tools as a Toolkit class, distribute it as a Python package, and have Cyclops discover it automatically via entry points. No manual registration needed.

Subclass Toolkit and add BaseTool instances as class attributes. The get_tools() method scans the class for any attribute that is a BaseTool instance and returns them as a list.

from cyclops.toolkit.plugins import Toolkit
from cyclops.toolkit.tool import BaseTool
import random
class WeatherTool(BaseTool):
def __init__(self):
super().__init__(name="get_weather", description="Get weather for a location.")
async def execute(self, location: str) -> str:
temps = {"london": 61, "tokyo": 74, "new york": 68}
temp = temps.get(location.lower(), random.randint(55, 85))
return f"{location}: {temp}F"
class ForecastTool(BaseTool):
def __init__(self):
super().__init__(name="get_forecast", description="Get a 3-day forecast.")
async def execute(self, location: str) -> str:
return f"Forecast for {location}: Day 1: 70F, Day 2: 68F, Day 3: 72F"
class WeatherToolkit(Toolkit):
"""Provides weather and forecast tools."""
weather = WeatherTool()
forecast = ForecastTool()

Tools are discovered by attribute inspection. There is no registration call needed inside the class.

PluginManager orchestrates loading and registration. It holds a ToolRegistry and a dictionary of named Toolkit instances.

Manually register a toolkit. This is useful in scripts and tests:

from cyclops.toolkit.plugins import PluginManager
from cyclops.toolkit.registry import ToolRegistry
registry = ToolRegistry()
manager = PluginManager(registry)
manager.register(WeatherToolkit())

An optional name argument overrides the class name used as the plugin key. A name already in use raises ValueError, so pass name to register two toolkits of the same class.

Discovers, instantiates and registers every toolkit under the "cyclops.toolkits" entry-point group:

manager.load_plugins()

Call this once per manager at startup, since a second call raises ValueError on the names it already registered.

After loading, push all tool instances into the registry:

manager.register_tools()
print(registry.list_tools()) # ["get_weather", "get_forecast"]
from cyclops import Agent, AgentConfig
from cyclops.toolkit.plugins import PluginManager
from cyclops.toolkit.registry import ToolRegistry
def build_agent() -> Agent:
registry = ToolRegistry()
manager = PluginManager(registry)
# Auto-discover all installed toolkit plugins.
manager.load_plugins()
manager.register_tools()
tools = [registry.get_tool(name) for name in registry.list_tools()]
config = AgentConfig(model="groq/llama-3.1-8b-instant")
return Agent(config, tools=tools)
agent = build_agent()
print(agent.run("What is the weather in London?"))

@tool turns a function into a Tool instance, so decorated functions in the class body are class attributes like any other tool:

from cyclops.toolkit import tool
from cyclops.toolkit.plugins import Toolkit
class MathToolkit(Toolkit):
@tool
def add(a: float, b: float) -> float:
"""Add two numbers."""
return a + b
@tool
def multiply(a: float, b: float) -> float:
"""Multiply two numbers."""
return a * b

The functions take no self: each attribute is a Tool, not a method.

To make a toolkit auto-discoverable, add it to the [project.entry-points."cyclops.toolkits"] section of your package’s pyproject.toml:

[project.entry-points."cyclops.toolkits"]
weather = "my_weather_package.plugin:WeatherToolkit"

The key (weather) becomes the plugin name reported by get_plugin_names(). The value is a dotted import path to the Toolkit subclass, or to a factory function that returns a Toolkit instance.

After installing the package (pip install my-weather-package), calling manager.load_plugins() will find and instantiate WeatherToolkit automatically.

my_weather_toolkit/
├── pyproject.toml
└── my_weather_toolkit/
├── __init__.py
├── plugin.py <- WeatherToolkit class lives here
└── tools.py <- WeatherTool, ForecastTool classes
pyproject.toml
[project]
name = "my-weather-toolkit"
version = "0.1.0"
dependencies = ["cyclops-ai>=0.1.0"]
[project.entry-points."cyclops.toolkits"]
weather = "my_weather_toolkit.plugin:WeatherToolkit"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
examples/plugin_system.py
"""Plugin system example - Discover and load toolkit plugins
This example shows how to manually load a toolkit plugin.
In production, plugins would be installed via uv and auto-discovered.
To create a toolkit plugin:
1. Create a class that inherits from Toolkit
2. Implement get_tools() to return your tools
3. Register it via entry points or manually with PluginManager
"""
import sys
from pathlib import Path
# Add example_toolkit_plugin to path for this demo
sys.path.insert(0, str(Path(__file__).parent))
from cyclops import Agent, AgentConfig
from cyclops.toolkit.plugins import PluginManager
from cyclops.toolkit.registry import ToolRegistry
from example_toolkit_plugin import WeatherToolkit
def main():
# Create plugin manager
registry = ToolRegistry()
plugin_manager = PluginManager(registry)
# Manually register the toolkit plugin (in production, this would be via entry points)
plugin_manager.register(WeatherToolkit())
# Load tools from plugins
plugin_manager.register_tools()
print(f"Loaded plugins: {plugin_manager.get_plugin_names()}")
print(f"Available tools: {registry.list_tools()}\n")
# Get tools from registry for Agent
tools = [registry.get_tool(name) for name in registry.list_tools()]
# Create agent with plugin-provided tools
config = AgentConfig(model="ollama/qwen3:4b")
agent = Agent(config, tools=tools)
print("Agent with plugin-provided tools:\n")
# Use weather tool
response = agent.run("What's the weather in Seattle?")
print("Q: What's the weather in Seattle?")
print(f"A: {response}\n")
# Use forecast tool
response = agent.run("Give me a 3-day forecast for Portland")
print("Q: Give me a 3-day forecast for Portland")
print(f"A: {response}\n")
if __name__ == "__main__":
main()