Extension development

The first step in extending nanobot is not writing code. It is deciding which boundary should own the capability. A misplaced capability is often harder to maintain than an imperfect implementation.

Corresponding source

Extension typeSource
Toolnanobot/agent/tools/base.py, loader.py, registry.py
Skillnanobot/agent/skills.py, nanobot/skills/, nanobot/agent/context.py
MCPnanobot/agent/tools/mcp.py, nanobot/config/schema.py, nanobot/agent/loop.py
Providernanobot/providers/base.py, registry.py, factory.py
Channelnanobot/channels/base.py, registry.py, manager.py
Command, API, SDK, and WebUInanobot/command/, api/, sdk/, webui/, webui/src/

Learning objectives

After this section, you should be able to:

  • choose Tool, Skill, MCP, Provider, Channel, or a runtime surface from the requirement;
  • locate discovery, registration, configuration, testing, and security contracts for each extension type;
  • keep the modification scope small and prevent platform or UI details from entering the Agent core;
  • design an implementation sequence that is verifiable before adding complexity.

Prerequisites

1. Choose the boundary first

RequirementPreferReason
Teach the model how to combine existing capabilitiesSkillthe main change is instructions and workflow
Give the model a new local Python actionToolrequires structured arguments and deterministic execution
Reuse an existing external tool serviceMCPthe protocol already exists; no core client needed
Integrate a new model APIProvidernormalize upstream responses into LLMResponse
Integrate a new chat platformChannelhandle inbound/outbound messages and platform lifecycle
Add a user control commandCommandthe model should not decide whether it runs
Expose capability to a Python applicationSDKtyped, in-process calls
Expose a compatible interface to HTTP clientsAPIout-of-process request/response protocol
Add browser interactionWebUIfrontend state plus WebSocket/HTTP protocol

2. Three commonly misplaced features

“Calls an external API” is not automatically a Provider

It is a Provider only when the API is an LLM inference backend that must produce LLMResponse. Weather, search, and database queries are more likely a Tool or MCP integration.

“A workflow” is not automatically a Tool

If existing Tools can already complete the task but the model does not know the right sequence, write a Skill first. A Skill adds no new execution permission.

“The browser needs a state” does not automatically require AgentRunner

The core should publish a general runtime event. WebSocket envelopes, replay sequences, component state, and styles belong in the coordinator, Channel, or WebUI.

3. Complexity ladder

flowchart LR
    A["Skill<br/>instructions only"] --> B["Tool<br/>local contract"]
    B --> C["MCP<br/>external process/service"]
    C --> D["Provider<br/>model protocol"]
    D --> E["Channel<br/>long connection and platform semantics"]
    E --> F["API / SDK / WebUI<br/>multi-consumer contract"]

This is not a value ranking. Test and failure surfaces commonly increase from left to right. Beginners should start with a Skill and a read-only Tool.

  1. Add a Tool
  2. Add a Skill
  3. Integrate MCP
  4. Add a Provider
  5. Add a Channel
  6. Command, API, SDK, and WebUI extension boundaries

5. Nine questions for every extension

  1. Who discovers or registers it?
  2. What is its stable name?
  3. Where is its input contract defined?
  4. How does output return to the Turn?
  5. Where is configuration loaded and validated?
  6. Who starts, cancels, and closes its lifecycle?
  7. Which side effects can it produce?
  8. How does the minimum test avoid real networks and credentials?
  9. Which documentation, UI, or indexes must change with it?

Each lesson uses these questions to bound the implementation.

Read-only source baseline

example/nanobot/code-EN remains a read-only fact baseline throughout the learning path. Hands-on changes belong only in learning-EN/examples, a separate temporary workspace, or an explicitly copied isolated source tree.

Next: Add a Tool