Writing extensions¶
An extension is an importable Python module with a module-level register(mb)
function. It can add LLM tools, observe MiniBot events, and register services.
Configure its import name in config.toml; a local module only needs to be on
PYTHONPATH.
Minimal useful extension¶
This extension gives the model a validated github_repository tool. It reads a
repository through GitHub’s API and returns only useful repository metadata.
[extensions]
modules = ["my_github_extension"]
[extensions.config.my_github_extension]
token_env = "GITHUB_TOKEN"
Set the token outside config.toml before starting MiniBot. A fine-grained token with
read-only access to the repositories you need is enough:
$ export GITHUB_TOKEN=github_pat_...
# my_github_extension.py
from __future__ import annotations
import json
import os
from typing import Any
from urllib.parse import quote
import aiosonic
from pydantic import BaseModel, Field
from minibot.app.extensions import ExtensionContext
from minibot.llm.tools.base import ToolContext
class GitHubRepositoryArgs(BaseModel):
owner: str = Field(min_length=1, description="GitHub organization or user name.")
repository: str = Field(min_length=1, description="Repository name.")
def register(mb: ExtensionContext) -> None:
token_env = str(mb.config.get("token_env", "GITHUB_TOKEN"))
token = os.environ.get(token_env, "").strip()
if not token:
raise ValueError(f"GitHub token is missing from ${token_env}")
# One async client per extension; every tool call reuses its connection pool.
client = aiosonic.HTTPClient()
@mb.tool
async def github_repository(args: GitHubRepositoryArgs, _: ToolContext) -> dict[str, Any]:
"""Read metadata for a GitHub repository. Use this to check a repository's
description, visibility, default branch, issue count, and last update time.
"""
owner = quote(args.owner, safe="")
repository = quote(args.repository, safe="")
response = await client.request(
f"https://api.github.com/repos/{owner}/{repository}",
method="GET",
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
},
)
if response.status_code != 200:
raise RuntimeError(f"GitHub repository request failed: HTTP {response.status_code}")
repository_data = json.loads((await response.content()).decode("utf-8"))
return {
"ok": True,
"full_name": repository_data.get("full_name"),
"description": repository_data.get("description"),
"private": repository_data.get("private"),
"default_branch": repository_data.get("default_branch"),
"open_issues_count": repository_data.get("open_issues_count"),
"updated_at": repository_data.get("updated_at"),
"html_url": repository_data.get("html_url"),
}
Run it from the directory containing my_github_extension.py:
$ PYTHONPATH=. poetry run minibot console --once "What is the default branch of sonic182/minibot?"
@mb.tool uses the function name as the tool name, the docstring as the model-facing
description, and the first argument’s Pydantic model as the JSON schema. Invalid arguments
return invalid_tool_arguments to the model instead of reaching the handler.
Extension API¶
mb.configThe extension’s
[extensions.config.<module>]dictionary. Prefer it for extension settings;mb.settingsexposes the complete validated application configuration.mb.tool/mb.add_tool()Add a typed decorator-based tool or explicit
ToolBindinginstances. Useadd_toolfor a custom tool name or hand-written JSON schema.mb.on(EventType)/mb.on(EventType, handler)Observe internal events such as
TurnCompletedEvent. Handlers are async and lossy: slow handlers drop events rather than blocking MiniBot.mb.add_service(service)Register an object with async
start()andstop()methods for work that belongs to the daemon or console lifecycle.
Operational rules¶
Extension import and registration failures stop startup. Tool names must be unique.
Extensions are trusted in-process code. Treat adding a module to
config.tomllike installing a dependency.Define Pydantic argument models at module scope; with postponed annotations, models defined inside
register()cannot be resolved.Check
mb.entrypointbefore adding channel services. Only the daemon runs channels.Configured extension tools are available to task workers, but worker registries do not start extension services or event subscriptions.
For the full type signatures behind mb, see Extension API Reference.
See minibot_ext_demo.py for a tool plus a TurnCompletedEvent subscriber.