nanobot Documentation

Go repository by @obot-platform. Model Context Protocol (MCP) server & tools with 1.3k stars.

1,322 stars Go Go 1 file Β· ~5,794 tokens
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy nanobot on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name nanobot -p 18790:18790 --restart always hkuds/nanobot:latest
Configured Services & Ports
βš™οΈ nanobot-gateway
image: build locally
πŸ”Œ Ports: 127.0.0.1:18790:18790, 8765:8765
βš™οΈ nanobot-api
image: build locally
πŸ”Œ Ports: 127.0.0.1:8900:8900
βš™οΈ nanobot-cli
image: build locally
docker-compose.yml
yaml (docker-compose.yml)
x-common-config: &common-config
  build:
    context: .
    dockerfile: Dockerfile
    args:
      NANOBOT_CHANNELS: ${NANOBOT_CHANNELS:-whatsapp}
  volumes:
    - ~/.nanobot:/home/nanobot/.nanobot
  cap_drop:
    - ALL
  # Entrypoint uses these to fix bind-mount ownership and drop to the nanobot user.
  cap_add:
    - CHOWN
    - SETGID
    - SETUID
  # Prevent the non-root process from regaining capabilities through setuid
  # binaries or file capabilities left inside the container image.
  security_opt:
    - no-new-privileges:true

services:
  nanobot-gateway:
    container_name: nanobot-gateway
    <<: *common-config
    command: ["gateway"]
    restart: unless-stopped
    ports:
      - 127.0.0.1:18790:18790
      - 8765:8765
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 1G
        reservations:
          cpus: "0.25"
          memory: 256M

  nanobot-api:
    container_name: nanobot-api
    <<: *common-config
    command:
      ["serve", "--host", "0.0.0.0", "-w", "/home/nanobot/.nanobot/api-workspace"]
    restart: unless-stopped
    ports:
      - 127.0.0.1:8900:8900
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 1G
        reservations:
          cpus: "0.25"
          memory: 256M

  nanobot-cli:
    <<: *common-config
    profiles:
      - cli
    command: ["status"]
    stdin_open: true
    tty: true

File: README.md

Nanobot - Build MCP Agents

Warning
This project is under heavy development and is moving away from its original design and intent. Expect significant breaking changes, architectural shifts, and evolving APIs.

Nanobot enables building agents with MCP and MCP-UI by providing a flexible MCP host.
While existing applications like VSCode, Claude, Cursor, ChatGPT, and Goose all include an MCP host,
Nanobot is designed to be a standalone, open-source MCP host that can be easily deployed or integrated into
your applications. You can use Nanobot to create your own dedicated MCP and MCP-UI powered chatbot.

What is an MCP Host?

An MCP host is
the service that combines MCP servers with an LLM and context to present an agent experience to a
consumer. The primary experience today is a chat interface, but it can be many other interfaces such
as voice, SMS, e-mail, AR/VR, Slack, MCP, or any other interface that can be used to interact with
an agent.

Examples

Here are some examples of Nanobots in action:

Installation

Nanobot can be installed via Homebrew:

bash
brew install obot-platform/tap/nanobot

This will give you the nanobot CLI, which you can use to run and manage your MCP host.


Getting Started


Configuration

Nanobot supports two configuration formats:

  1. Single File Configuration - A nanobot.yaml file
  2. Directory-Based Configuration - A directory with .md agent files

Single File Configuration

Nanobot supports the following providers:

  • OpenAI (e.g. gpt-4)
  • Anthropic (e.g. claude-3)

To use them, set the corresponding API key:

bash
# For OpenAI models
export OPENAI_API_KEY=sk-...

# For Anthropic models
export ANTHROPIC_API_KEY=sk-ant-...

Nanobot automatically selects the correct provider based on the model specified.


Create a configuration file (e.g. nanobot.yaml) that defines your agents and MCP servers.

Example:

yaml
agents:
  dealer:
    name: Blackjack Dealer
    model: gpt-4.1
    mcpServers: blackjackmcp

mcpServers:
  blackjackmcp:
    url: https://blackjack.nanobot.ai/mcp

Start Nanobot with:

bash
nanobot run ./nanobot.yaml

Nanobot serves MCP over HTTP at http://localhost:8080. Connect it to an MCP-compatible host such as Obot to interact with the agent.

Directory-Based Configuration

You can organize your configuration as a directory with a top-level nanobot.yaml for shared settings and individual .md files for each agent.

Directory Structure:

text
my-config/
β”œβ”€β”€ nanobot.yaml         # Shared config: mcpServers, llmProviders, env, etc.
└── agents/              # Agent definitions directory
    β”œβ”€β”€ main.md          # Main agent (auto-set as entrypoint)
    └── helper.md        # Additional agent

LLM Providers

openai and anthropic are built-in providers β€” set OPENAI_API_KEY or ANTHROPIC_API_KEY and they work with no additional config. Use the {provider}/{model} format in the model field to select a provider.

Additional providers (Azure, Bedrock, Ollama, etc.) can be configured in nanobot.yaml under llmProviders.

Each entry specifies a dialect (the API protocol), credentials, and base URL. The dialect field accepts:

Dialect Description
OpenAIResponses OpenAI Responses API (default)
OpenAIChatCompletions OpenAI Chat Completions API
AnthropicMessages Anthropic Messages API
OpenResponses Generic OpenResponses-compatible endpoint

Example llmProviders config

yaml
llmProviders:
  # Built-in providers β€” shown here for reference or to override baseURL/headers
  openai:
    dialect: OpenAIResponses
    apiKey: ${OPENAI_API_KEY}
    baseURL: ${OPENAI_BASE_URL}  # optional, default: https://api.openai.com/v1

  anthropic:
    dialect: AnthropicMessages
    apiKey: ${ANTHROPIC_API_KEY}
    baseURL: ${ANTHROPIC_BASE_URL}  # optional, default: https://api.anthropic.com/v1

  # Custom providers pointing at any compatible endpoint
  azureOpenAI:
    dialect: OpenAIResponses
    apiKey: ${AZURE_API_KEY}
    baseURL: https://<your-resource>.cognitiveservices.azure.com/openai/v1

  azureAnthropic:
    dialect: AnthropicMessages
    apiKey: ${AZURE_ANTHROPIC_KEY}
    baseURL: https://<your-resource>.services.ai.azure.com/anthropic/v1

  bedrockOpenAI:
    dialect: OpenAIResponses
    apiKey: ${BEDROCK_API_KEY}
    baseURL: https://bedrock-mantle.us-east-1.api.aws/v1

  ollama:
    dialect: OpenResponses
    baseURL: http://localhost:11434/v1

nanobot.yaml defines shared resources available to all agents:

yaml
llmProviders:
  ollama:
    dialect: OpenResponses
    baseURL: http://localhost:11434/v1

mcpServers:
  store:
    url: https://example.com/mcp
    headers:
      Authorization: Bearer ${MY_TOKEN}

Agent File Format (agents/main.md):

markdown
---
name: Shopping Assistant
model: anthropic/claude-3-7-sonnet-latest
mcpServers:
  - store
temperature: 0.7
---

You are a helpful shopping assistant.

Help users find products and answer their questions.

The YAML front-matter supports all agent configuration fields (model, name, mcpServers, tools, temperature, etc.), and the markdown body becomes the agent's instructions. Markdown agents take precedence over any agents defined in nanobot.yaml with the same name.

Usage:

bash
nanobot run ./my-config/

The default config path is .nanobot/, so nanobot run with no arguments will use that directory if it exists.

Features:

  • Agent directory: All agent .md files must be in the agents/ subdirectory
  • Auto-entrypoint: If agents/main.md exists, it's automatically set as the default agent
  • Markdown precedence: Agents defined in agents/*.md override same-named agents in nanobot.yaml
  • README.md: Automatically ignored in the agents/ directory (use it for documentation)

Deprecated: mcp-servers.yaml and mcp-servers.json are still supported for backwards compatibility but are deprecated. Define mcpServers in nanobot.yaml instead.

See the directory-config example for a complete working example.


Development & Contribution

Contributions are welcome! Nanobot is still in alpha, so expect active development and rapid changes.

Build from Source

bash
make

Features & Roadmap

Nanobot aims to be a fully compliant MCP Host and support all MCP + MCP-UI features.

Feature Category Feature Status
MCP Core TODO βœ… Implemented
TODO 🚧 Partial
TODO ❌ Not yet
TODO βœ… Implemented
MCP-UI TODO 🚧 Partial
TODO βœ… Implemented
TODO ❌ Not yet

βœ… = Implementedβ€ƒπŸš§ = Partial / WIPβ€ƒβŒ = Not yet ⏳ = Planned

Roadmap

  • Full MCP + MCP-UI compliance
  • More robust multi-agent support
  • Expanded model provider support
  • Expanded authentication and security features
  • Frontend integrations (Slack, SMS, email, embedded web agents)
  • Easy embedding into existing apps and websites

License

Nanobot is licensed under the Apache 2.0 License.



File: examples/directory-config/README.md

Directory-Based Configuration Example

This directory demonstrates the directory-based configuration format for nanobot.

Structure

text
directory-config/
β”œβ”€β”€ agents/              # Agent definitions directory
β”‚   β”œβ”€β”€ main.md          # Main agent (default entrypoint)
β”‚   └── helper.md        # Additional helper agent
β”œβ”€β”€ mcp-servers.yaml     # MCP server definitions
└── README.md            # This file

Agent Files (.md)

Agent .md files are placed in the agents/ subdirectory. Each file defines one agent with YAML front-matter:

Front-Matter Fields

All HookAgent fields are supported in the front-matter:

  • id - Agent ID (defaults to filename without .md)
  • name - Display name for the agent
  • model - LLM model to use (e.g., gpt-4, claude-3-7-sonnet-latest)
  • mcpServers - List of MCP servers this agent can access
  • tools - Specific tools to expose to the agent
  • agents - Sub-agents this agent can delegate to
  • temperature - Sampling temperature (0.0 to 1.0)
  • topP - Top-p sampling parameter
  • maxTokens - Maximum tokens in response
  • starterMessages - Suggested prompts for users
  • description - Short description of the agent
  • icon - Icon URL (light mode)
  • iconDark - Icon URL (dark mode)
  • And all other HookAgent fields...

Markdown Body = Instructions

The markdown content after the front-matter becomes the agent's instructions.

MCP Servers File

Define MCP servers in mcp-servers.yaml ormcp-servers.json:

yaml
myserver:
  url: https://example.com/mcp
  headers:
    Authorization: Bearer ${MY_TOKEN}

Note: You can only have ONE of mcp-servers.yaml or mcp-servers.json, not both.

Usage

Run this directory-based config with:

bash
nanobot run ./examples/directory-config/

Entrypoint Behavior

If a main.md file exists in the agents/ directory, it will automatically be set as the default published entrypoint agent. In this example, the Shopping Assistant is the main agent.


File: integration_test/README.md

Integration Tests

End-to-end tests that run against a real LLM. Excluded from go test ./... by default via the //go:build integration build tag.

Running

bash
ANTHROPIC_API_KEY=... go test -tags integration ./integration_test/ -runs 5
  • -tags integration enables compilation of the tests (required; without it they are completely skipped).
  • -runs controls how many times each prompt is run per test (default: 5).

Agent Configuration

The harness loads the builtin nanobot agent via config.Load(".nanobot/", true). If a .nanobot/ directory exists in the working directory when the tests run, its config is merged in β€” allowing you to override the agent's instructions, add MCP servers, or adjust other settings without changing test code.

If no .nanobot/ directory exists, the tests run against the default builtin agent.

Adding a Test

Tests use shared tooling:

  • newTestRuntime(t, completer, recorder) β€” creates a Runtime wired with a recording/intercepting system server and returns a context and agent service ready for execution.
  • runAgent(ctx, svc, prompt) β€” runs the agent with the given user prompt.
  • newRecorder(handlers) β€” creates a toolCallRecorder with custom ToolHandlers for specific tools. By default, config and getSkill always pass through to the real server; all other tools return an error unless a handler is registered.
  • recorder.find(name) β€” returns the first recorded call for a tool, or nil.
  • recorder.summary() β€” returns a formatted list of all tool calls with arguments, useful in failure messages.

Example

go
func TestMySkillBehavior(t *testing.T) {
    apiKey := os.Getenv("ANTHROPIC_API_KEY")
    if apiKey == "" {
        t.Fatal("ANTHROPIC_API_KEY must be set when running integration tests")
    }

    completer := llm.NewClient(llm.Config{
        DefaultModel: "claude-sonnet-4-6",
        Anthropic:    anthropic.Config{APIKey: apiKey},
    })

    recorder := newRecorder(map[string]ToolHandler{
        // Register tools the agent is expected to call with mock responses.
        "bash": func(req mcp.CallToolRequest) *mcp.CallToolResult {
            return &mcp.CallToolResult{
                Content: []mcp.Content{{Type: "text", Text: "mock output"}},
            }
        },
    })

    ctx, svc := newTestRuntime(t, completer, recorder)
    if err := runAgent(ctx, svc, "my prompt"); err != nil {
        t.Fatalf("Complete() failed: %v", err)
    }

    call := recorder.find("bash")
    if call == nil {
        t.Fatalf("expected bash call\nall tool calls:\n%s", recorder.summary())
    }
    // assert call.Arguments as needed
}