PluginRegistry
Discovers, manages, and exposes tool plugins. The registry acts as the central hub for all plugin operations, controlling which tools are available to the model.
- Discover - Find available plugins
- Expose - Enable specific plugins
- Configure - Pass to JaatoClient
from jaato import PluginRegistry
# Create registry with model name
registry = PluginRegistry(
model_name="claude-sonnet-4-20250514"
)
# Discover available plugins
registry.discover()
# List what's available
print(registry.list_available())
# ['cli', 'mcp', 'file_edit', 'todo', ...]
# Expose specific plugins
registry.expose_tool("cli")
registry.expose_tool("file_edit")
# Check what's exposed
print(registry.list_exposed())
# ['cli', 'file_edit']
# Get tools for the model
schemas = registry.get_exposed_tool_schemas()
executors = registry.get_exposed_executors()
Constructor
__init__
Creates a new plugin registry. The model name is used to check plugin compatibility via model requirements.
-
model_name
str
optional
Model name for checking plugin requirements. Some plugins only work with specific models.
# Without model (no requirement checking)
registry = PluginRegistry()
# With model name
registry = PluginRegistry(
model_name="claude-sonnet-4-20250514"
)
# Update model later
registry.set_model_name("gemini-2.5-flash")
Discovery
discover
plugin_kind: str = "tool",
include_directory: bool = True
) -> List[str]
Scans for available plugins and returns their names. Does not expose them automatically.
-
plugin_kind
str
optional
Type of plugins to discover:
"tool","gc", or"model_provider"Default:"tool" -
include_directory
bool
optional
Include plugins from the plugins directoryDefault:
True
list_available
Returns names of all discovered (available) plugins.
list_exposed
Returns names of all currently exposed plugins.
is_exposed
Checks if a plugin is currently exposed.
registry = PluginRegistry(model_name="gemini-2.5-flash")
# Discover tool plugins
tools = registry.discover(plugin_kind="tool")
print(f"Found {len(tools)} tool plugins:")
for name in tools:
print(f" - {name}")
# Discover GC plugins
gc_plugins = registry.discover(plugin_kind="gc")
print(f"Found {len(gc_plugins)} GC plugins")
# Check availability
available = registry.list_available()
print(f"Available: {available}")
# After exposing some
registry.expose_tool("cli")
print(f"Exposed: {registry.list_exposed()}")
print(f"CLI exposed: {registry.is_exposed('cli')}")
Exposure
Exposing a plugin makes its tools available to the model. You can expose plugins individually or all at once.
expose_tool
name: str,
config: Optional[Dict] = None
) -> bool
Exposes a single plugin by name with optional configuration.
-
name
str
required
Name of the plugin to expose
-
config
Dict
optional
Plugin-specific configuration
unexpose_tool
Stops exposing a plugin's tools.
expose_all
Exposes all discovered plugins, optionally with per-plugin config.
unexpose_all
Stops exposing all plugins.
register_plugin
plugin: ToolPlugin,
expose: bool = False,
enrichment_only: bool = False,
config: Optional[Dict] = None
) -> None
Manually registers a plugin instance (for custom plugins).
registry = PluginRegistry(model_name="gemini-2.5-flash")
registry.discover()
# Expose individual plugins
registry.expose_tool("cli")
registry.expose_tool("file_edit")
# Expose with configuration
registry.expose_tool("cli", config={
"extra_paths": ["/usr/local/bin"],
"max_output_chars": 10000
})
# Unexpose a plugin
registry.unexpose_tool("cli")
# Expose all at once
registry.expose_all()
# Expose all with per-plugin config
registry.expose_all(config={
"cli": {"max_output_chars": 5000},
"mcp": {"servers": ["Atlassian"]}
})
# Unexpose everything
registry.unexpose_all()
from my_plugins import CustomPlugin
# Create and register
plugin = CustomPlugin()
registry.register_plugin(
plugin,
expose=True, # Expose immediately
config={"api_key": "..."}
)
# Register for enrichment only
# (no tools, just prompt modification)
registry.register_plugin(
enrichment_plugin,
expose=False,
enrichment_only=True
)
Retrieval
After exposing plugins, use these methods to get tool schemas and executors for the client.
get_exposed_tool_schemas
Returns all tool schemas from exposed plugins.
get_exposed_executors
Returns a mapping of tool names to executor functions.
get_plugin
Returns a plugin instance by name.
get_plugin_for_tool
Returns the plugin that provides a specific tool.
get_system_instructions
Returns combined system instructions from all exposed plugins.
get_auto_approved_tools
Returns list of tools that don't require permission approval.
get_exposed_user_commands
Returns user-facing commands from exposed plugins.
registry.expose_tool("cli")
registry.expose_tool("file_edit")
# Get all tool schemas
schemas = registry.get_exposed_tool_schemas()
for schema in schemas:
print(f"{schema.name}: {schema.description}")
# Get executors
executors = registry.get_exposed_executors()
print(f"Tools: {list(executors.keys())}")
# Get system instructions
instructions = registry.get_system_instructions()
if instructions:
print(f"System: {instructions[:100]}...")
# Get auto-approved tools
auto_approved = registry.get_auto_approved_tools()
print(f"Auto-approved: {auto_approved}")
# Get plugin by name
cli_plugin = registry.get_plugin("cli")
if cli_plugin:
print(f"CLI plugin: {cli_plugin.name}")
# Find which plugin provides a tool
plugin = registry.get_plugin_for_tool("execute_command")
if plugin:
print(f"execute_command from: {plugin.name}")
# Get user commands
commands = registry.get_exposed_user_commands()
for cmd in commands:
print(f"/{cmd.name}: {cmd.description}")
Enrichment Pipelines
The framework provides three enrichment pipelines that allow plugins to process content at different stages:
| Pipeline | When Called | Use Cases |
|---|---|---|
| Prompt | Before user message sent | Expand @references |
| System Instruction | After collecting instructions | Extract templates |
| Tool Result | After tool execution | Extract from file reads |
Prompt Enrichment
Plugins subscribe to modify user prompts before they're sent to the model.
System Instruction Enrichment
Plugins subscribe to modify combined system instructions (including MODULE.md content).
Tool Result Enrichment
Plugins subscribe to modify tool execution results before they're sent to the model.
Plugins are called in priority order (lower = earlier):
| Priority | Plugin | Pipelines |
|---|---|---|
| 20 | references | Prompt, Tool Result |
| 40 | template | System Instructions, Tool Result |
| 60 | multimodal | Prompt |
| 80 | memory | Prompt |
| 90 | session | Prompt |
class TemplatePlugin:
name = "template"
def get_system_instruction_enrichment_priority(self):
return 40
def subscribes_to_system_instruction_enrichment(self):
return True
def enrich_system_instructions(self, instructions):
# Detect Jinja2 templates in MODULE.md content
# Extract to .jaato/templates/
return SystemInstructionEnrichmentResult(
instructions=enriched,
metadata={"extracted_count": 2}
)
# Also subscribe to tool result enrichment
def subscribes_to_tool_result_enrichment(self):
return True
def enrich_tool_result(self, tool_name, result):
# Same logic for file reads
return ToolResultEnrichmentResult(
result=enriched,
metadata={"extracted_count": 1}
)
class ReferencesPlugin:
name = "references"
def get_enrichment_priority(self):
return 20 # Run first
def subscribes_to_prompt_enrichment(self):
return True
def enrich_prompt(self, prompt):
# Detect @mod-code-015 mentions
# Expand with reference content
return PromptEnrichmentResult(
prompt=expanded,
metadata={"mentioned_references": ["mod-code-015"]}
)
Model Requirements
Plugins can declare model requirements (glob patterns) to indicate which models they support. The registry tracks skipped plugins.
set_model_name
Updates the model name for requirement checking.
get_model_name
Returns the current model name.
list_skipped_plugins
Returns plugins that were skipped due to model requirements, with their required patterns.
# Set model during creation
registry = PluginRegistry(model_name="gemini-2.5-flash")
# Or update later
registry.set_model_name("gemini-2.5-flash")
# Check current model
print(f"Model: {registry.get_model_name()}")
# After discovery, check skipped plugins
registry.discover()
skipped = registry.list_skipped_plugins()
for plugin, requirements in skipped.items():
print(f"{plugin} requires: {requirements}")
class GeminiOnlyPlugin:
name = "gemini_only"
def get_model_requirements(self):
# Only works with Gemini models
return ["gemini-*"]
def get_tool_schemas(self):
return [...]
# This plugin will be skipped if model
# doesn't match "gemini-*" pattern