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.
Toolkit base class
Section titled “Toolkit base class”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 Toolkitfrom cyclops.toolkit.tool import BaseToolimport 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
Section titled “PluginManager”PluginManager orchestrates loading and registration. It holds a ToolRegistry and a dictionary of named Toolkit instances.
register
Section titled “register”Manually register a toolkit. This is useful in scripts and tests:
from cyclops.toolkit.plugins import PluginManagerfrom 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.
load_plugins
Section titled “load_plugins”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.
register_tools
Section titled “register_tools”After loading, push all tool instances into the registry:
manager.register_tools()
print(registry.list_tools()) # ["get_weather", "get_forecast"]Full workflow
Section titled “Full workflow”from cyclops import Agent, AgentConfigfrom cyclops.toolkit.plugins import PluginManagerfrom 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?"))Defining tools with the @tool decorator
Section titled “Defining tools with the @tool decorator”@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 toolfrom 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 * bThe functions take no self: each attribute is a Tool, not a method.
Entry-point registration
Section titled “Entry-point registration”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.
Example toolkit package layout
Section titled “Example toolkit package layout”my_weather_toolkit/├── pyproject.toml└── my_weather_toolkit/ ├── __init__.py ├── plugin.py <- WeatherToolkit class lives here └── tools.py <- WeatherTool, ForecastTool classes[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"Full example
Section titled “Full example”"""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 Toolkit2. Implement get_tools() to return your tools3. Register it via entry points or manually with PluginManager"""
import sysfrom pathlib import Path
# Add example_toolkit_plugin to path for this demosys.path.insert(0, str(Path(__file__).parent))
from cyclops import Agent, AgentConfigfrom cyclops.toolkit.plugins import PluginManagerfrom cyclops.toolkit.registry import ToolRegistryfrom 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()