Kotoba

Where to start

What Kotoba isWho she is and where she runs, in one page
InstallingOne package, two commands
First runA key, a model, her first words
The approval gateHow she asks before she acts
The two voice modesLocal voice, or the agent tunnel
The soul fileChange who she is
RoadmapWhat grows next, and what was cut on purpose

Or a section

↑↓ move openesc close124 pages
All pages

Plugins1 of 7

Plugins: your own tools, without touching the repository

About 3 minutes to read

On this page

A plugin is a Python module that gives Kotoba a tool she did not ship with. You write it on your own machine, in your own directory or your own package. You do not fork the repository, you do not open a pull request, and you do not wait for a release.

The loader is core/plugins.py. It runs once, when the process starts, and it is the only supported extension point for tools outside the tree.

Adding a tool inside the repository is a different job with different rules — it goes in api/src/kotoba/tools/, gets registered by an explicit import in tools/registry.py, and is reviewed like any other change. That belongs to the contributing guide. This section is for the other case: your code, your machine, no repository involved.


The two install paths

A folder pluginAn entry-point plugin
Where it lives~/.kotoba/plugins/<name>/anywhere pip can install it
What the user typescopy a folder in, restartpip install <package>, restart
Shapeone or more .py files, no packaginga real distribution with a pyproject.toml
Tools per pluginone per .py fileexactly one
Multi-file codeawkward — see Installing and shippingnatural
Dependenciesyou install them yourselfdeclared, and pip resolves them
Best foryour own hack, a single file, trying something outanything you give to somebody else

Both end up in the same place: a tool registered under the toolset plugin:<name>, listed in Settings, switchable on and off.


What a plugin can do

Everything a built-in tool can do. The loader hands your module to the same registry the built-ins use (tools/registry.py), and the agent loop calls your execute() exactly the way it calls shell or web_search. You get:

  • The tool schema the model sees, written by you.
  • The working directory (ctx.workdir), the database (ctx.db), the execution sandbox (ctx.sandbox) and the approval gate (ctx.approval).
  • Her spoken lines around your tool — before, during, after, and on failure.
  • Her face: which expression she wears while your tool runs, and which one when it fails.
  • A row in the work panel, if your tool's risk makes it an action.

What a plugin cannot do

It cannot join a conversation. This is the one limit that surprises people, so it is first.

schemas_for() offers a companion turn only the toolsets in COMPANION_TOOLSETS (tools/registry.py), and that set is a hard-coded literal:

python
COMPANION_TOOLSETS = {"web", "memory", "core", "skills", "cron", "terminal", "code", "report",
                      "file", "discord"}

plugin:<anything> is not in it, and no setting, environment variable or TOOLSET value you declare changes that — the loader overwrites your TOOLSET with plugin:<name> before it registers (core/plugins.py, _register_plugin_module). So a plugin tool is work-mode only: it is offered when she is running a background job, and never while you are simply talking to her. If she calls it by name in a conversation anyway, the dispatcher refuses and tells her it belongs to a background job.

In practice that means: to use your plugin, ask her for something that becomes a job. She calls start_work, the job's loop sees your tool in its schema list, and it runs there.

It cannot replace a built-in. Plugins are registered with allow_override=False, so a plugin declaring a tool named shell is refused and the real shell survives. See How the loader works — there is a sharp edge attached to this.

It cannot be delegated to. delegate's toolset parameter is a fixed enum (research, web, file, code, browser), so a subagent cannot be restricted to your plugin's toolset.

It is not sandboxed. A plugin is Python running inside her process, as your user account. Read The security position before you install anybody else's.


Where to go next

  1. Your first plugin — a complete, working tool, written out in full.
  2. The tool module contract — every attribute the loader and the loop read, enumerated from the code.
  3. Installing and shipping — the folder path, the package path, and what survives an upgrade.
  4. How the loader works — validity, rejection, isolation, load order.
  5. The security position — stated plainly.
  6. Testing your plugin — a test suite you can run today.