mimiclaw Documentation

MimiClaw: Personal Agent on a $5 chip. No OS(Linux). No Node.js. No Mac mini. No Raspberry Pi. No VPS. Hardware agents OS. On device Muse.

RAW Doc

Technical Documentation: memovai/mimiclaw

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

MimiClaw: Pocket AI Assistant on a $5 Chip

The world's first AI assistant(OpenClaw) on a $5 chip. No Linux. No Node.js. Just pure C

MimiClaw turns a tiny ESP32-S3 board into a personal AI assistant. Plug it into USB power, connect to WiFi, and talk to it through Telegram โ€” it handles any task you throw at it and evolves over time with local memory โ€” all on a chip the size of a thumb.

Meet MimiClaw

  • Tiny โ€” No Linux, no Node.js, no bloat โ€” just pure C
  • Handy โ€” Message it from Telegram, it handles the rest
  • Loyal โ€” Learns from memory, remembers across reboots
  • Energetic โ€” USB power, 0.5 W, runs 24/7
  • Lovable โ€” One ESP32-S3 board, $5, nothing else

How It Works

You send a message on Telegram. The ESP32-S3 picks it up over WiFi, feeds it into an agent loop โ€” the LLM thinks, calls tools, reads memory โ€” and sends the reply back. Supports both Anthropic (Claude) and OpenAI (GPT) as providers, switchable at runtime. Everything runs on a single $5 chip with all your data stored locally on flash.

Quick Start

What You Need

  • An ESP32-S3 dev board with 16 MB flash and 8 MB PSRAM (e.g. Xiaozhi AI board, ~$10)
  • A USB Type-C cable
  • A Telegram bot token โ€” talk to @BotFather on Telegram to create one
  • An Anthropic API key โ€” from console.anthropic.com, or an OpenAI API key โ€” from platform.openai.com

Install

bash
# You need ESP-IDF v5.5+ installed first:
# https://docs.espressif.com/projects/esp-idf/en/v5.5.2/esp32s3/get-started/

git clone https://github.com/memovai/mimiclaw.git
cd mimiclaw

idf.py set-target esp32s3

Ubuntu Install

Recommended baseline:

  • Ubuntu 22.04/24.04
  • Python >= 3.10
  • CMake >= 3.16
  • Ninja >= 1.10
  • Git >= 2.34
  • flex >= 2.6
  • bison >= 3.8
  • gperf >= 3.1
  • dfu-util >= 0.11
  • libusb-1.0-0, libffi-dev, libssl-dev

Install and build on Ubuntu:

bash
sudo apt-get update
sudo apt-get install -y git wget flex bison gperf python3 python3-pip python3-venv \
  cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0

./scripts/setup_idf_ubuntu.sh
./scripts/build_ubuntu.sh

macOS Install

Recommended baseline:

  • macOS 12/13/14
  • Xcode Command Line Tools
  • Homebrew
  • Python >= 3.10
  • CMake >= 3.16
  • Ninja >= 1.10
  • Git >= 2.34
  • flex >= 2.6
  • bison >= 3.8
  • gperf >= 3.1
  • dfu-util >= 0.11
  • libusb, libffi, openssl

Install and build on macOS:

bash
xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

./scripts/setup_idf_macos.sh
./scripts/build_macos.sh

Configure

MimiClaw uses a two-layer config system: build-time defaults in mimi_secrets.h, with runtime overrides via the serial CLI. CLI values are stored in NVS flash and take priority over build-time values.

bash
cp main/mimi_secrets.h.example main/mimi_secrets.h

Edit main/mimi_secrets.h:

c
#define MIMI_SECRET_WIFI_SSID       "YourWiFiName"
#define MIMI_SECRET_WIFI_PASS       "YourWiFiPassword"
#define MIMI_SECRET_TG_TOKEN        "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
#define MIMI_SECRET_API_KEY         "sk-ant-api03-xxxxx"
#define MIMI_SECRET_MODEL_PROVIDER  "anthropic"     // "anthropic" or "openai"
#define MIMI_SECRET_SEARCH_KEY      ""              // optional: Brave Search API key
#define MIMI_SECRET_TAVILY_KEY      ""              // optional: Tavily API key (preferred)
#define MIMI_SECRET_PROXY_HOST      ""              // optional: e.g. "10.0.0.1"
#define MIMI_SECRET_PROXY_PORT      ""              // optional: e.g. "7897"

Then build and flash:

bash
# Clean build (required after any mimi_secrets.h change)
idf.py fullclean && idf.py build

# Find your serial port
ls /dev/cu.usb*          # macOS
ls /dev/ttyACM*          # Linux

# Flash and monitor (replace PORT with your port)
# USB adapter: likely /dev/cu.usbmodem11401 (macOS) or /dev/ttyACM0 (Linux)
idf.py -p PORT flash monitor

Important: Plug into the correct USB port! Most ESP32-S3 boards have two USB-C ports. You must use the one labeled USB (native USB Serial/JTAG), not the one labeled COM (external UART bridge). Plugging into the wrong port will cause flash/monitor failures.

Show reference photo

CLI Commands (via UART/COM port)

Connect via serial to configure or debug. Config commands let you change settings without recompiling โ€” just plug in a USB cable anywhere.

Runtime config (saved to NVS, overrides build-time defaults):

text
mimi> wifi_set MySSID MyPassword   # change WiFi network
mimi> set_tg_token 123456:ABC...   # change Telegram bot token
mimi> set_api_key sk-ant-api03-... # change API key (Anthropic or OpenAI)
mimi> set_model_provider openai    # switch provider (anthropic|openai)
mimi> set_model gpt-4o             # change LLM model
mimi> set_proxy 127.0.0.1 7897  # set HTTP proxy
mimi> clear_proxy                  # remove proxy
mimi> set_search_key BSA...        # set Brave Search API key
mimi> set_tavily_key tvly-...      # set Tavily API key (preferred)
mimi> config_show                  # show all config (masked)
mimi> config_reset                 # clear NVS, revert to build-time defaults

Debug & maintenance:

text
mimi> wifi_status              # am I connected?
mimi> memory_read              # see what the bot remembers
mimi> memory_write "content"   # write to MEMORY.md
mimi> heap_info                # how much RAM is free?
mimi> session_list             # list all chat sessions
mimi> session_clear 12345      # wipe a conversation
mimi> heartbeat_trigger           # manually trigger a heartbeat check
mimi> cron_start                  # start cron scheduler now
mimi> restart                     # reboot

USB (JTAG) vs UART: Which Port for What

Most ESP32-S3 dev boards expose two USB-C ports:

Port Use for
USB (JTAG) idf.py flash, JTAG debugging
COM (UART) REPL CLI, serial console

REPL requires the UART (COM) port. The USB (JTAG) port does not support interactive REPL input.

Port details & recommended workflow

Port Label Protocol
USB USB / JTAG Native USB Serial/JTAG
COM UART / COM External UART bridge (CP2102/CH340)

The ESP-IDF console/REPL is configured to use UART by default (CONFIG_ESP_CONSOLE_UART_DEFAULT=y).

If you have both ports connected simultaneously:

  • USB (JTAG) handles flash/download and provides secondary serial output
  • UART (COM) provides the primary interactive console for the REPL
  • macOS: both appear as /dev/cu.usbmodem* or /dev/cu.usbserial-* โ€” run ls /dev/cu.usb* to identify
  • Linux: USB (JTAG) โ†’ /dev/ttyACM0, UART โ†’ /dev/ttyUSB0

Recommended workflow:

bash
# Flash via USB (JTAG) port
idf.py -p /dev/cu.usbmodem11401 flash

# Open REPL via UART (COM) port
idf.py -p /dev/cu.usbserial-110 monitor
# or use any serial terminal: screen, minicom, PuTTY at 115200 baud

Memory

MimiClaw stores everything as plain text files you can read and edit:

File What it is
SOUL.md The bot's personality โ€” edit this to change how it behaves
USER.md Info about you โ€” name, preferences, language
MEMORY.md Long-term memory โ€” things the bot should always remember
HEARTBEAT.md Task list the bot checks periodically and acts on autonomously
cron.json Scheduled jobs โ€” recurring or one-shot tasks created by the AI
2026-02-05.md Daily notes โ€” what happened today
tg_12345.jsonl Chat history โ€” your conversation with the bot

Tools

MimiClaw supports tool calling for both Anthropic and OpenAI โ€” the LLM can call tools during a conversation and loop until the task is done (ReAct pattern).

Tool Description
web_search Search the web via Tavily (preferred) or Brave for current information
get_current_time Fetch current date/time via HTTP and set the system clock
cron_add Schedule a recurring or one-shot task (the LLM creates cron jobs on its own)
cron_list List all scheduled cron jobs
cron_remove Remove a cron job by ID

To enable web search, set a Tavily API key via MIMI_SECRET_TAVILY_KEY (preferred), or a Brave Search API key via MIMI_SECRET_SEARCH_KEY in mimi_secrets.h.

Cron Tasks

MimiClaw has a built-in cron scheduler that lets the AI schedule its own tasks. The LLM can create recurring jobs ("every N seconds") or one-shot jobs ("at unix timestamp") via the cron_add tool. When a job fires, its message is injected into the agent loop โ€” so the AI wakes up, processes the task, and responds.

Jobs are persisted to SPIFFS (cron.json) and survive reboots. Example use cases: daily summaries, periodic reminders, scheduled check-ins.

Heartbeat

The heartbeat service periodically reads HEARTBEAT.md from SPIFFS and checks for actionable tasks. If uncompleted items are found (anything that isn't an empty line, a header, or a checked - [x] box), it sends a prompt to the agent loop so the AI can act on them autonomously.

This turns MimiClaw into a proactive assistant โ€” write tasks to HEARTBEAT.md and the bot will pick them up on the next heartbeat cycle (default: every 30 minutes).

Also Included

  • WebSocket gateway on port 18789 โ€” connect from your LAN with any WebSocket client
  • OTA updates โ€” flash new firmware over WiFi, no USB needed
  • Dual-core โ€” network I/O and AI processing run on separate CPU cores
  • HTTP proxy โ€” CONNECT tunnel support for restricted networks
  • Multi-provider โ€” supports both Anthropic (Claude) and OpenAI (GPT), switchable at runtime
  • Cron scheduler โ€” the AI can schedule its own recurring and one-shot tasks, persisted across reboots
  • Heartbeat โ€” periodically checks a task file and prompts the AI to act autonomously
  • Tool use โ€” ReAct agent loop with tool calling for both providers

For Developers

Technical details live in the docs/ folder:

Contributing

Please read CONTRIBUTING.md before opening issues or pull requests.

Contributors

Thanks to everyone who has contributed to MimiClaw.

License

MIT

Acknowledgments

Inspired by OpenClaw and Nanobot. MimiClaw reimplements the core AI agent architecture for embedded hardware โ€” no Linux, no server, just a $5 chip.

Star History


Overview (`docs/im-integration/README.md`)

IM Integration Guides

Configuration guides for MimiClaw's instant messaging channel integrations.

Guides

Guide Service Description
Feishu Setup Feishu / Lark Feishu bot channel โ€” receive and send messages via Feishu

Overview

MimiClaw supports multiple IM channels for interacting with the AI agent. Each guide below walks through obtaining API credentials, configuring MimiClaw (build-time or runtime), and verifying the integration.

All credentials can be set in two ways:

  1. Build-time โ€” define in main/mimi_secrets.h and rebuild
  2. Runtime โ€” use serial CLI commands (saved to NVS flash, no rebuild needed)

See mimi_secrets.h.example for the full list of configurable secrets.


Overview (`docs/tool-setup/README.md`)

Tool Setup Guides

Configuration guides for MimiClaw's external service integrations.

Related local setup guide:

  • Wi-Fi AP Onboarding Guide โ€” configure firmware builds that expose the local MimiClaw-XXXX onboarding/admin access point

Guides

Guide Service Description
Tavily Setup Tavily Web search API โ€” preferred search provider for the web_search tool

Overview

MimiClaw integrates with external services to extend its capabilities. Each guide below walks through obtaining API credentials, configuring MimiClaw (build-time or runtime), and verifying the integration.

All credentials can be set in two ways:

  1. Build-time โ€” define in main/mimi_secrets.h and rebuild
  2. Runtime โ€” use serial CLI commands (saved to NVS flash, no rebuild needed)

See mimi_secrets.h.example for the full list of configurable secrets.


Overview (`main/channels/feishu/README.md`)

Feishu/Lark Bot Integration

This directory contains the Feishu bot integration for MimiClaw.

Features

  • Send text messages to Feishu chats
  • Receive messages via WebSocket persistent connection (long-connection mode)
  • Automatic message chunking (4096 chars per message)
  • Tenant access token management with auto-refresh
  • Message deduplication
  • Reply to specific messages
  • Support for both DM (p2p) and group chats

Configuration

Option 1: Build-time Configuration

  1. Copy the secrets template:
bash
cp main/mimi_secrets.h.example main/mimi_secrets.h
  1. Edit main/mimi_secrets.h:
c
#define MIMI_SECRET_FEISHU_APP_ID     "cli_xxxxxxxxxxxxxx"
#define MIMI_SECRET_FEISHU_APP_SECRET "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  1. Rebuild:
bash
idf.py fullclean && idf.py build

Option 2: Runtime Configuration (CLI)

text
mimi> set_feishu_creds cli_xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Feishu App Setup

  1. Go to Feishu Open Platform
  2. Create an app and get App ID / App Secret
  3. Enable permissions:
    • im:message - Send and receive messages
    • im:message:send_as_bot - Send messages as bot
  4. Configure Event Subscription:
    • Set subscription mode to Persistent connection (้•ฟ่ฟžๆŽฅ)
    • Subscribe to: im.message.receive_v1
  5. The ESP32 will connect to Feishu automatically on boot

Architecture

text
Feishu Server (wss://open.feishu.cn)
    ^
    |  (WebSocket persistent connection, ESP32 initiates)
[feishu_ws_task]
    |
    v  (message_bus_push_inbound)
[Message Bus] --> [Agent Loop] --> [Message Bus]
    |                                    |
    v  (outbound dispatch)               |
[feishu_send_message] <-----------------+
    |
    v  (POST /im/v1/messages)
Feishu API

API Reference

Function Description
feishu_bot_init() Load credentials from NVS/build-time
feishu_bot_start() Start WebSocket persistent connection task
feishu_send_message(chat_id, text) Send text message
feishu_reply_message(message_id, text) Reply to a specific message
feishu_set_credentials(app_id, secret) Save credentials to NVS

References


ARCHITECTURE (`docs/ARCHITECTURE.md`)

MimiClaw Architecture

ESP32-S3 AI Agent firmware โ€” C/FreeRTOS implementation running on bare metal (no Linux).


System Overview

text
/* Detailed source-code truncated for AI context efficiency. */

Data Flow

text
1. User sends message on Telegram (or WebSocket)
2. Channel poller receives message, wraps in mimi_msg_t
3. Message pushed to Inbound Queue (FreeRTOS xQueue)
4. Agent Loop (Core 1) pops message:
   a. Load session history from SPIFFS (JSONL)
   b. Build system prompt (SOUL.md + USER.md + MEMORY.md + recent notes + tool guidance)
   c. Build cJSON messages array (history + current message)
   d. ReAct loop (max 10 iterations):
      i.   Call Claude API via HTTPS (non-streaming, with tools array)
      ii.  Parse JSON response โ†’ text blocks + tool_use blocks
      iii. If stop_reason == "tool_use":
           - Execute each tool (e.g. web_search โ†’ Brave Search API)
           - Append assistant content + tool_result to messages
           - Continue loop
      iv.  If stop_reason == "end_turn": break with final text
   e. Save user message + final assistant text to session file
   f. Push response to Outbound Queue
5. Outbound Dispatch (Core 0) pops response:
   a. Route by channel field ("telegram" โ†’ sendMessage, "websocket" โ†’ WS frame)
6. User receives reply

Module Map

text
/* Detailed source-code truncated for AI context efficiency. */

FreeRTOS Task Layout

Task Core Priority Stack Description
tg_poll 0 5 12 KB Telegram long polling (30s timeout)
agent_loop 1 6 12 KB Message processing + Claude API call
outbound 0 5 8 KB Route responses to Telegram / WS
serial_cli 0 3 4 KB USB serial console REPL
httpd (internal) 0 5 โ€” WebSocket server (esp_http_server)
wifi_event (IDF) 0 8 โ€” WiFi event handling (ESP-IDF)

Core allocation strategy: Core 0 handles I/O (network, serial, WiFi). Core 1 is dedicated to the agent loop (CPU-bound JSON building + waiting on HTTPS).


Memory Budget

Purpose Location Size
FreeRTOS task stacks Internal SRAM ~40 KB
WiFi buffers Internal SRAM ~30 KB
TLS connections x2 (Telegram + Claude) PSRAM ~120 KB
JSON parse buffers PSRAM ~32 KB
Session history cache PSRAM ~32 KB
System prompt buffer PSRAM ~16 KB
LLM response stream buffer PSRAM ~32 KB
Remaining available PSRAM ~7.7 MB

Large buffers (32 KB+) are allocated from PSRAM via heap_caps_calloc(1, size, MALLOC_CAP_SPIRAM).


Flash Partition Layout

text
Offset      Size      Name        Purpose
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
0x009000    24 KB     nvs         ESP-IDF internal use (WiFi calibration etc.)
0x00F000     8 KB     otadata     OTA boot state
0x011000     4 KB     phy_init    WiFi PHY calibration
0x020000     2 MB     ota_0       Firmware slot A
0x220000     2 MB     ota_1       Firmware slot B
0x420000    12 MB     spiffs      Markdown memory, sessions, config
0xFF0000    64 KB     coredump    Crash dump storage

Total: 16 MB flash.


Storage Layout (SPIFFS)

SPIFFS is a flat filesystem โ€” no real directories. Files use path-like names.

text
/spiffs/config/SOUL.md          AI personality definition
/spiffs/config/USER.md          User profile
/spiffs/memory/MEMORY.md        Long-term persistent memory
/spiffs/memory/2026-02-05.md    Daily notes (one file per day)
/spiffs/sessions/tg_12345.jsonl Session history (one file per Telegram chat)

Session files are JSONL (one JSON object per line):

json
{"role":"user","content":"Hello","ts":1738764800}
{"role":"assistant","content":"Hi there!","ts":1738764802}

Configuration

All configuration is done exclusively through mimi_secrets.h at build time. There is no runtime configuration โ€” changing any setting requires idf.py fullclean && idf.py build.

Define Description
MIMI_SECRET_WIFI_SSID WiFi SSID
MIMI_SECRET_WIFI_PASS WiFi password
MIMI_SECRET_TG_TOKEN Telegram Bot API token
MIMI_SECRET_API_KEY Anthropic API key
MIMI_SECRET_MODEL Model ID (default: claude-opus-4-6)
MIMI_SECRET_PROXY_HOST HTTP proxy hostname/IP (optional)
MIMI_SECRET_PROXY_PORT HTTP proxy port (optional)
MIMI_SECRET_SEARCH_KEY Brave Search API key (optional)

NVS is still initialized (required by ESP-IDF WiFi internals) but is not used for application configuration.


Message Bus Protocol

The internal message bus uses two FreeRTOS queues carrying mimi_msg_t:

c
typedef struct {
    char channel[16];   // "telegram", "websocket", "cli"
    char chat_id[32];   // Telegram chat ID or WS client ID
    char *content;      // Heap-allocated text (ownership transferred)
} mimi_msg_t;
  • Inbound queue: channels โ†’ agent loop (depth: 8)
  • Outbound queue: agent loop โ†’ dispatch โ†’ channels (depth: 8)
  • Content string ownership is transferred on push; receiver must free().

WebSocket Protocol

Port: 18789. Max clients: 4.

Client โ†’ Server:

json
{"type": "message", "content": "Hello", "chat_id": "ws_client1"}

Server โ†’ Client:

json
{"type": "response", "content": "Hi there!", "chat_id": "ws_client1"}

Client chat_id is auto-assigned on connection (ws_<fd>) but can be overridden in the first message.


Claude API Integration

Endpoint: POST https://api.anthropic.com/v1/messages

Request format (Anthropic-native, non-streaming, with tools):

json
{
  "model": "claude-opus-4-6",
  "max_tokens": 4096,
  "system": "<system prompt>",
  "tools": [
    {
      "name": "web_search",
      "description": "Search the web for current information.",
      "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}
    }
  ],
  "messages": [
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hi!"},
    {"role": "user", "content": "What's the weather today?"}
  ]
}

Key difference from OpenAI: system is a top-level field, not inside the messages array.

Non-streaming JSON response:

json
{
  "id": "msg_xxx",
  "type": "message",
  "role": "assistant",
  "content": [
    {"type": "text", "text": "Let me search for that."},
    {"type": "tool_use", "id": "toolu_xxx", "name": "web_search", "input": {"query": "weather today"}}
  ],
  "stop_reason": "tool_use"
}

When stop_reason is "tool_use", the agent loop executes each tool and sends results back:

json
{"role": "assistant", "content": [<text + tool_use blocks>]}
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_xxx", "content": "..."}]}

The loop repeats until stop_reason is "end_turn" (max 10 iterations).


Startup Sequence

text
app_main()
  โ”œโ”€โ”€ init_nvs()                    NVS flash init (erase if corrupted)
  โ”œโ”€โ”€ esp_event_loop_create_default()
  โ”œโ”€โ”€ init_spiffs()                 Mount SPIFFS at /spiffs
  โ”œโ”€โ”€ message_bus_init()            Create inbound + outbound queues
  โ”œโ”€โ”€ memory_store_init()           Verify SPIFFS paths
  โ”œโ”€โ”€ session_mgr_init()
  โ”œโ”€โ”€ wifi_manager_init()           Init WiFi STA mode + event handlers
  โ”œโ”€โ”€ http_proxy_init()             Load proxy config from build-time secrets
  โ”œโ”€โ”€ telegram_bot_init()           Load bot token from build-time secrets
  โ”œโ”€โ”€ llm_proxy_init()              Load API key + model from build-time secrets
  โ”œโ”€โ”€ tool_registry_init()          Register tools, build tools JSON
  โ”œโ”€โ”€ agent_loop_init()
  โ”œโ”€โ”€ serial_cli_init()             Start REPL (works without WiFi)
  โ”‚
  โ”œโ”€โ”€ wifi_manager_start()          Connect using build-time credentials
  โ”‚   โ””โ”€โ”€ wifi_manager_wait_connected(30s)
  โ”‚
  โ””โ”€โ”€ [if WiFi connected]
      โ”œโ”€โ”€ telegram_bot_start()      Launch tg_poll task (Core 0)
      โ”œโ”€โ”€ agent_loop_start()        Launch agent_loop task (Core 1)
      โ”œโ”€โ”€ ws_server_start()         Start httpd on port 18789
      โ””โ”€โ”€ outbound_dispatch task    Launch outbound task (Core 0)

If WiFi credentials are missing or connection times out, the CLI remains available for diagnostics.


Serial CLI Commands

The CLI provides debug and maintenance commands only. All configuration is done via mimi_secrets.h.

Command Description
wifi_status Show connection status and IP
memory_read Print MEMORY.md contents
memory_write <CONTENT> Overwrite MEMORY.md
session_list List all session files
session_clear <CHAT_ID> Delete a session file
heap_info Show internal + PSRAM free bytes
restart Reboot the device
help List all available commands

Nanobot Reference Mapping

Nanobot Module MimiClaw Equivalent Notes
agent/loop.py agent/agent_loop.c ReAct loop with tool use
agent/context.py agent/context_builder.c Loads SOUL.md + USER.md + memory + tool guidance
agent/memory.py memory/memory_store.c MEMORY.md + daily notes
session/manager.py memory/session_mgr.c JSONL per chat, ring buffer
channels/telegram.py telegram/telegram_bot.c Raw HTTP, no python-telegram-bot
bus/events.py + queue.py bus/message_bus.c FreeRTOS queues vs asyncio
providers/litellm_provider.py llm/llm_proxy.c Direct Anthropic API only
config/schema.py mimi_config.h + mimi_secrets.h Build-time secrets only
cli/commands.py cli/serial_cli.c esp_console REPL
agent/tools/* tools/tool_registry.c + tool_web_search.c web_search via Brave API
agent/subagent.py (not yet implemented) See TODO.md
agent/skills.py (not yet implemented) See TODO.md
cron/service.py (not yet implemented) See TODO.md
heartbeat/service.py (not yet implemented) See TODO.md

TODO (`docs/TODO.md`)

MimiClaw vs Nanobot โ€” Feature Gap Tracker

Comparing against nanobot/ reference implementation. Tracks features MimiClaw has not yet aligned with.
Priority: P0 = Core missing, P1 = Important enhancement, P2 = Nice to have


P0 โ€” Core Agent Capabilities

[x] ~~Tool Use Loop (multi-turn agent iteration)~~

  • Implemented: agent_loop.c ReAct loop with llm_chat_tools(), max 10 iterations, non-streaming JSON parsing

[ ] Memory Write via Tool Use (agent-driven memory persistence)

  • openclaw: Agent uses standard write/edit tools to write MEMORY.md and memory/YYYY-MM-DD.md; system prompt instructs agent to persist important information; pre-compaction memory flush triggers a silent agent turn to save durable memories before context window limit
  • MimiClaw: memory_write_long_term and memory_append_today exist but are only called from CLI; agent loop never writes memory
  • Scope: Expose memory_write and memory_append_today as tool_use tools for Claude; add system prompt guidance on when to persist memory; optionally add pre-compaction flush (trigger memory save when session history nears MIMI_SESSION_MAX_MSGS)
  • Depends on: Tool Use Loop

[x] ~~Tool Registry + web_search Tool~~

  • Implemented: tools/tool_registry.c โ€” tool registration, JSON schema builder, dispatch by name
  • Implemented: tools/tool_web_search.c โ€” Brave Search API via HTTPS (direct + proxy support)

[ ] More Built-in Tools

  • nanobot built-in tools not yet ported: read_file, write_file, edit_file, list_dir, message
  • Recommendation: Reasonable tool subset for ESP32: read_file, write_file, list_dir (SPIFFS), message, memory_write

[ ] Subagent / Spawn Background Tasks

  • nanobot: subagent.py โ€” SubagentManager spawns independent agent instances with isolated tool sets and system prompts, announces results back to main agent via system channel
  • MimiClaw: Not implemented
  • Recommendation: ESP32 memory is limited; simplify to a single background FreeRTOS task for long-running work, inject result into inbound queue on completion

P1 โ€” Important Features

[ ] Telegram User Allowlist (allow_from)

  • nanobot: channels/base.py L59-82 โ€” is_allowed() checks sender_id against allow_list
  • MimiClaw: No authentication; anyone can message the bot and consume API credits
  • Recommendation: Store allow_from list in mimi_secrets.h as a build-time define, filter in process_updates()

[ ] Telegram Markdown to HTML Conversion

  • nanobot: channels/telegram.py L16-76 โ€” _markdown_to_telegram_html() full converter: code blocks, inline code, bold, italic, links, strikethrough, lists
  • MimiClaw: Uses parse_mode: Markdown directly; special characters can cause send failures (has fallback to plain text)
  • Recommendation: Implement simplified Markdown-to-HTML converter, or switch to parse_mode: HTML

[ ] Telegram /start Command

  • nanobot: telegram.py L183-192 โ€” handles /start command, replies with welcome message
  • MimiClaw: Not handled; /start is sent to Claude as a regular message

[ ] Telegram Media Handling (photos/voice/files)

  • nanobot: telegram.py L194-289 โ€” handles photo, voice, audio, document; downloads files; transcribes voice
  • MimiClaw: Only processes message.text, ignores all media messages
  • Recommendation: Images can be base64-encoded for Claude Vision; voice requires Whisper API (extra HTTPS request)

[ ] Skills System (pluggable capabilities)

  • nanobot: agent/skills.py โ€” loads skills from SKILL.md files, supports always-loaded and on-demand, frontmatter metadata, requirements checking
  • MimiClaw: Not implemented
  • Recommendation: Simplified version: store SKILL.md files on SPIFFS, load into system prompt via context_builder

[ ] Full Bootstrap File Alignment

  • nanobot: Loads AGENTS.md, SOUL.md, USER.md, TOOLS.md, IDENTITY.md (5 files)
  • MimiClaw: Only loads SOUL.md and USER.md
  • Recommendation: Add AGENTS.md (behavior guidelines) and TOOLS.md (tool documentation)

[ ] Longer Memory Lookback

  • nanobot: memory.py L56-80 โ€” get_recent_memories(days=7) defaults to 7 days
  • MimiClaw: context_builder.c only reads last 3 days
  • Recommendation: Make configurable, but mind token budget

[x] ~~System Prompt Tool Guidance~~

  • Implemented: context_builder.c includes tool usage guidance in system prompt

[ ] Message Metadata (media, reply_to, metadata)

  • nanobot: bus/events.py โ€” InboundMessage has media, metadata fields; OutboundMessage has reply_to
  • MimiClaw: mimi_msg_t only has channel + chat_id + content
  • Recommendation: Extend msg struct, add media_path and metadata fields

[ ] Outbound Subscription Pattern

  • nanobot: bus/queue.py L41-49 โ€” supports subscribe_outbound(channel, callback) subscription model
  • MimiClaw: Hardcoded if-else dispatch
  • Recommendation: Current approach is simple and reliable; not worth changing with few channels

P2 โ€” Advanced Features

[ ] Cron Scheduled Task Service

  • nanobot: cron/service.py โ€” full cron scheduler supporting at/every/cron expressions, persistent storage, timed agent triggers
  • MimiClaw: Not implemented
  • Recommendation: Use FreeRTOS timer for simplified version, support "every N minutes" only

[ ] Heartbeat Service

  • nanobot: heartbeat/service.py โ€” reads HEARTBEAT.md every 30 minutes, triggers agent if tasks are found
  • MimiClaw: Not implemented
  • Recommendation: Simple FreeRTOS timer that periodically checks HEARTBEAT.md

[ ] Multi-LLM Provider Support

  • nanobot: providers/litellm_provider.py โ€” supports OpenRouter, Anthropic, OpenAI, Gemini, DeepSeek, Groq, Zhipu, vLLM via LiteLLM
  • MimiClaw: Hardcoded to Anthropic Messages API
  • Recommendation: Abstract LLM interface, support OpenAI-compatible API (most providers are compatible)

[ ] Voice Transcription

  • nanobot: providers/transcription.py โ€” Groq Whisper API
  • MimiClaw: Not implemented
  • Recommendation: Requires extra HTTPS request to Whisper API: download Telegram voice -> forward -> get text

[x] ~~Build-time Config File + Runtime NVS Override~~

  • Implemented: mimi_secrets.h as build-time defaults, NVS as runtime override via CLI
  • Two-layer config: build-time secrets โ†’ NVS fallback, CLI commands to set/show/reset

[ ] WebSocket Gateway Protocol Enhancement

  • nanobot: Gateway port 18790 + richer protocol
  • MimiClaw: Basic JSON protocol, lacks streaming token push
  • Recommendation: Add {"type":"token","content":"..."} streaming push

[ ] Multi-Channel Manager

  • nanobot: channels/manager.py โ€” unified lifecycle management for multiple channels
  • MimiClaw: Hardcoded in app_main()
  • Recommendation: Not worth abstracting with few channels

[ ] WhatsApp / Feishu Channels

  • nanobot: channels/whatsapp.py, channels/feishu.py
  • MimiClaw: Only Telegram + WebSocket
  • Recommendation: Low priority, Telegram is sufficient

[x] ~~Telegram Proxy Support (HTTP CONNECT)~~

  • Implemented: HTTP CONNECT tunnel via proxy/http_proxy.c, configurable via mimi_secrets.h (MIMI_SECRET_PROXY_HOST/MIMI_SECRET_PROXY_PORT)

[ ] Session Metadata Persistence

  • nanobot: session/manager.py L136-153 โ€” session file includes metadata line (created_at, updated_at)
  • MimiClaw: JSONL only stores role/content/ts, no metadata header
  • Recommendation: Low priority

Completed Alignment

  • Telegram Bot long polling (getUpdates)
  • Message Bus (inbound/outbound queues)
  • Agent Loop with ReAct tool use (multi-turn, max 10 iterations)
  • Claude API (Anthropic Messages API, non-streaming, tool_use protocol)
  • Tool Registry + web_search tool (Brave Search API)
  • Context Builder (system prompt + bootstrap files + memory + tool guidance)
  • Memory Store (MEMORY.md + daily notes)
  • Session Manager (JSONL per chat_id, ring buffer history)
  • WebSocket Gateway (port 18789, JSON protocol)
  • Serial CLI (esp_console, debug/maintenance commands)
  • HTTP CONNECT Proxy (Telegram + Claude API + Brave Search via proxy tunnel)
  • OTA Update
  • WiFi Manager (build-time credentials, exponential backoff)
  • SPIFFS storage
  • Build-time config (mimi_secrets.h) + runtime NVS override via CLI

Suggested Implementation Order

text
1. [done] Tool Use Loop + Tool Registry + web_search
2. Memory Write via Tool Use         <- makes the agent actually remember
3. Built-in Tools (read_file, write_file, message)
4. Telegram Allowlist (allow_from)   <- security essential
5. Bootstrap File Completion (AGENTS.md, TOOLS.md)
6. Subagent (simplified)
7. Telegram Markdown -> HTML
8. Media Handling
9. Cron / Heartbeat
10. Other enhancements

WIFI ONBOARDING AP (`docs/WIFI_ONBOARDING_AP.md`)

Wi-Fi AP Onboarding Guide

This guide documents the Wi-Fi onboarding flow for firmware builds that include the MimiClaw onboarding portal.

What It Does

When onboarding is enabled, MimiClaw can expose a local Wi-Fi access point such as MimiClaw-XXXX and serve a configuration page at http://192.168.4.1.

Typical uses:

  • first-time Wi-Fi setup without editing firmware
  • updating saved Wi-Fi, model, proxy, or bot credentials later
  • recovering a device that can no longer join the previous router

Requirements

  • a firmware build with the Wi-Fi onboarding portal enabled
  • an ESP32-S3 board powered on and booted normally
  • a phone or laptop that can join the temporary MimiClaw-XXXX network

First-Time Setup

  1. Power on the device.
  2. Wait for the onboarding hotspot MimiClaw-XXXX to appear.
  3. Join that hotspot from your phone or laptop.
  4. Open http://192.168.4.1 if the captive page does not open automatically.
  5. Fill in at least:
    • SSID
    • Password
  6. Add optional settings if needed:
    • API Key
    • Model
    • Provider
    • Telegram
    • Feishu
    • Proxy
    • Search
  7. Click Save & Restart.
  8. Wait for the device to reboot and join your normal Wi-Fi network.

Updating Settings Later

If the firmware keeps the admin AP online after normal Wi-Fi connection, you can reconnect to MimiClaw-XXXX later and open http://192.168.4.1 again.

The page should prefill the currently effective configuration so you can edit only the fields you want to change.

Config Priority

The onboarding page follows the same config priority as the firmware:

  1. saved NVS values
  2. build-time defaults from main/mimi_secrets.h

That means:

  • if a field exists in NVS, it overrides the build-time default
  • if you clear a saved field and reboot, the device falls back to main/mimi_secrets.h
  • if both NVS and build-time values are empty, the field stays empty

Clearing Saved Values

On firmware versions that support clearing fields from the portal:

  • leaving a field blank and saving removes the corresponding NVS key
  • after reboot, the page shows the build-time fallback if one exists

If you need to wipe all saved runtime settings, use:

text
mimi> config_reset

Troubleshooting

No `MimiClaw-XXXX` hotspot appears

  • verify that the running firmware actually includes the onboarding portal
  • if the device already connects successfully and does not keep the admin AP online, clear Wi-Fi config and reboot
  • confirm the board has finished booting before scanning for Wi-Fi

The page still shows old values

  • refresh the page manually
  • reconnect to the AP and open http://192.168.4.1 again
  • if needed, restart the browser once to clear stale captive portal state

The build-time provider/model do not show up

If the device already has model or provider saved in NVS, the saved values win over main/mimi_secrets.h.

To return to build-time defaults:

  • clear those fields in the portal and save, or
  • run config_reset

Notes

  • The onboarding AP is typically local-only and intended for nearby configuration.
  • Current onboarding implementations may use an open AP for simplicity, so avoid leaving it exposed longer than necessary.
  • If your deployment needs stronger local protection, add an AP password before using the flow in production.

Im Integration FEISHU SETUP (`docs/im-integration/FEISHU_SETUP.md`)

Feishu / Lark Bot Configuration Guide

This guide walks through setting up a Feishu (or Lark) bot to work with MimiClaw, turning your ESP32-S3 into a Feishu-connected AI assistant.

Table of Contents

Overview

MimiClaw supports Feishu as a messaging channel alongside Telegram and WebSocket. The Feishu integration uses:

  • Webhook receiver โ€” the ESP32 runs an HTTP server on port 18790 to receive messages from Feishu
  • Send API โ€” MimiClaw sends replies via Feishu's REST API (/im/v1/messages)
  • Tenant access token โ€” automatic token management with background refresh

Both direct messages (P2P) and group chats are supported.

Prerequisites

Step 1: Create a Feishu App

  1. Go to Feishu Open Platform and sign in
  2. Click Create Custom App (or "Create App" on Lark)
  3. Fill in the app details:
    • App Name: Choose a name (e.g., "MimiClaw Bot")
    • App Description: Brief description of your bot
    • App Icon: Upload an icon (optional)
  4. After creation, you will see your App ID and App Secret on the app's Credentials & Basic Info page

Important: Save the App ID (cli_xxxxxxxxxxxxxx) and App Secret (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx). You will need these to configure MimiClaw.

Step 2: Configure App Permissions

In your app's settings, go to Permissions & Scopes and add these required permissions:

Permission Scope ID Description
Read/Send messages im:message Receive and send messages
Send messages as bot im:message:send_as_bot Send messages as the bot identity

To add permissions:

  1. Navigate to Permissions & Scopes in the left sidebar
  2. Search for each scope ID listed above
  3. Click Add next to each permission
  4. The permissions will take effect after you publish or update the app version

Note: On Lark (international version), the permission names may differ slightly, but the scope IDs are the same.

Step 3: Set Up Event Subscription

Event subscription allows Feishu to push new messages to your ESP32 in real-time.

Configure the Webhook URL

  1. In your app settings, go to Event Subscriptions (or "Events & Callbacks")
  2. Set the Request URL to:
text
http://<ESP32_IP>:18790/feishu/events

Replace <ESP32_IP> with your ESP32's public IP or domain name.

  1. Click Save โ€” Feishu will send a verification challenge to the URL
  2. MimiClaw automatically responds to the URL verification challenge, so this should succeed if the ESP32 is reachable

Subscribe to Events

Add the following event:

Event Event ID Description
Receive messages im.message.receive_v1 Triggered when users send messages to the bot

To add events:

  1. In the Event Subscriptions page, click Add Event
  2. Search for im.message.receive_v1
  3. Select it and click Confirm

Encryption Settings (Optional)

In the event subscription settings, you can optionally configure:

  • Verification Token โ€” used to verify that events come from Feishu
  • Encrypt Key โ€” encrypts event payloads

MimiClaw currently does not verify these tokens, so you can leave them empty for simplicity. For production use, consider implementing verification.

Step 4: Configure MimiClaw

You need to provide the App ID and App Secret to MimiClaw.

Option 1: Build-time Configuration

  1. Copy the secrets template if you haven't already:
bash
cp main/mimi_secrets.h.example main/mimi_secrets.h
  1. Edit main/mimi_secrets.h:
c
#define MIMI_SECRET_FEISHU_APP_ID     "cli_xxxxxxxxxxxxxx"
#define MIMI_SECRET_FEISHU_APP_SECRET "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  1. Rebuild and flash:
bash
idf.py fullclean && idf.py build
idf.py -p PORT flash monitor

Option 2: Runtime Configuration via Serial CLI

Connect to the UART (COM) port and run:

text
mimi> set_feishu_creds cli_xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

This saves credentials to NVS flash immediately โ€” no rebuild needed.

Verify Configuration

text
mimi> config_show

You should see feishu_app_id: cli_**** and feishu_app_secret: **** in the output.

Step 5: Network Setup

Feishu's servers need to reach your ESP32's webhook endpoint. There are several approaches:

Option A: Direct Public IP

If your ESP32 is directly accessible from the internet:

text
Webhook URL: http://<PUBLIC_IP>:18790/feishu/events

Option B: Port Forwarding

If the ESP32 is behind a router:

  1. Log into your router's admin panel
  2. Forward external port (e.g., 18790) to <ESP32_LOCAL_IP>:18790
  3. Use your router's public IP in the webhook URL

Option C: Reverse Proxy / Tunnel

For development or when port forwarding isn't possible:

  • ngrok: ngrok http <ESP32_IP>:18790
  • frp: Configure frpc.toml to proxy to the ESP32
  • Cloudflare Tunnel: Route traffic through Cloudflare

Example with ngrok:

bash
ngrok http 192.168.1.100:18790
# Use the generated URL: https://xxxx.ngrok.io/feishu/events

Option D: Cloud Server Relay (Production)

For reliable production setups, deploy a lightweight reverse proxy on a cloud server (e.g., Volcengine ECS, AWS EC2) that forwards requests to your ESP32 via a VPN or WireGuard tunnel. This is the approach described in the Volcengine OpenClaw deployment guide.

Note: Feishu requires the webhook URL to be accessible and respond within 3 seconds. Ensure your network path has low latency.

Step 6: Publish and Test

Enable the Bot

  1. In your app settings, go to Bot in the left sidebar
  2. Toggle Enable Bot to ON

Publish the App

  1. Go to App Release (or "Version Management & Release")
  2. Click Create Version
  3. Set a version number and description
  4. Click Submit for Review (for enterprise apps) or Publish (for personal testing apps)

Tip: For testing, you can use the app in "development mode" without publishing โ€” just add yourself as a test user.

Test the Bot

  1. Open Feishu and search for your bot by name
  2. Send a message to the bot in a direct chat
  3. Check the ESP32 serial output โ€” you should see the message being received and processed
  4. The bot should reply through Feishu

For group chats:

  1. Add the bot to a group
  2. Mention the bot with @BotName followed by your message
  3. The bot will process and reply in the group

Architecture

text
Feishu Cloud
    |
    |  HTTP POST /feishu/events
    |  (im.message.receive_v1)
    v
[ESP32 Webhook Server :18790]
    |
    |  message_bus_push_inbound()
    v
[Message Bus] โ”€โ”€> [Agent Loop] โ”€โ”€> [Message Bus]
                   (Claude/GPT)         |
                                        |  outbound dispatch
                                        v
                              [feishu_send_message()]
                                        |
                                        |  POST /im/v1/messages
                                        v
                                   Feishu API

Key Components

Component Description
Webhook Server HTTP server on port 18790, handles event callbacks
Token Manager Manages tenant access tokens, auto-refreshes before expiry
Message Sender Sends text messages via Feishu REST API with auto-chunking
Deduplication Prevents processing duplicate events from Feishu retries

Configuration Constants

These can be found in main/mimi_config.h:

Constant Default Description
MIMI_FEISHU_MAX_MSG_LEN 4096 Max message length per chunk
MIMI_FEISHU_WEBHOOK_PORT 18790 Webhook HTTP server port
MIMI_FEISHU_WEBHOOK_PATH /feishu/events Webhook endpoint path
MIMI_FEISHU_WEBHOOK_MAX_BODY 16 KB Max webhook request body size

CLI Commands

Command Description
set_feishu_creds <app_id> <app_secret> Save Feishu credentials to NVS
config_show Show all configuration (including Feishu, masked)
config_reset Clear all NVS config, revert to build-time defaults

Troubleshooting

Webhook URL verification fails

  • Ensure the ESP32 is running and connected to WiFi: wifi_status
  • Verify the webhook URL is reachable from the internet
  • Check that port 18790 is not blocked by firewalls
  • Look at ESP32 serial output for incoming HTTP requests

Bot doesn't respond to messages

  1. Check credentials: config_show should show Feishu app_id and app_secret
  2. Check event subscription: Ensure im.message.receive_v1 is subscribed in the Feishu app settings
  3. Check permissions: Both im:message and im:message:send_as_bot must be granted
  4. Check serial output: Look for message processing logs on the ESP32

"Tenant access token" errors

  • Verify your App ID and App Secret are correct
  • The token auto-refreshes every 2 hours โ€” if you just set credentials, wait a moment for the first token fetch
  • Ensure the ESP32 can reach https://open.feishu.cn (check proxy settings if needed)

Messages are truncated

Feishu has a 4096-character limit per message. MimiClaw automatically chunks long messages, but if you see issues, check the serial output for chunking errors.

Bot works in DM but not in groups

  • Ensure the bot is added to the group
  • Users must @mention the bot in group chats for it to receive messages
  • Check that group messaging permissions are enabled in the Feishu app settings

Event subscription shows errors in Feishu console

  • Feishu retries failed events up to 5 times with exponential backoff
  • MimiClaw deduplicates retried events, so duplicate processing is not a concern
  • If events consistently fail, check the ESP32's network connectivity

References


Tool Setup TAVILY SETUP (`docs/tool-setup/TAVILY_SETUP.md`)

Tavily Web Search Configuration Guide

Tavily is a real-time search engine designed for AI agents and RAG workflows. MimiClaw uses Tavily as the preferred web search provider (with Brave Search as a fallback).

Table of Contents

Overview

MimiClaw's web_search tool allows the AI agent to search the internet for real-time information. When a Tavily API key is configured, MimiClaw will use Tavily as the search provider. If no Tavily key is set but a Brave Search key exists, it falls back to Brave Search.

Why Tavily?

  • Purpose-built for AI agents โ€” returns clean, structured results
  • Includes relevance scoring for each result
  • Supports advanced search depth for higher quality results
  • 1,000 free API credits per month (no credit card required)

Getting an API Key

  1. Sign up at app.tavily.com
  2. Create an account โ€” you can sign in with Google or email
  3. Copy your API key โ€” it starts with tvly- and is available on the dashboard immediately
  4. The free Researcher plan gives you 1,000 API credits per month with no credit card required

Your API key will look like: tvly-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Configuring MimiClaw

There are two ways to configure the Tavily API key:

  1. Copy the secrets template if you haven't already:
bash
cp main/mimi_secrets.h.example main/mimi_secrets.h
  1. Edit main/mimi_secrets.h and set your Tavily key:
c
#define MIMI_SECRET_TAVILY_KEY      "tvly-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  1. Rebuild the firmware:
bash
idf.py fullclean && idf.py build
  1. Flash to your ESP32-S3:
bash
idf.py -p PORT flash monitor

Option 2: Runtime Configuration via Serial CLI

Connect to the UART (COM) port and run:

text
mimi> set_tavily_key tvly-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

This saves the key to NVS flash and takes effect immediately โ€” no rebuild needed. Runtime values override build-time defaults.

Verify Configuration

Check that the key is set:

text
mimi> config_show

You should see tavily_key: tvly-**** (masked) in the output.

How It Works in MimiClaw

When the AI agent needs to search the web, it calls the web_search tool. MimiClaw selects the search provider with this priority:

text
1. Tavily (if MIMI_SECRET_TAVILY_KEY is set)  <-- preferred
2. Brave Search (if MIMI_SECRET_SEARCH_KEY is set)
3. No search available (tool returns error)

The search flow:

text
User asks a question requiring web info
        |
        v
Agent decides to call web_search tool
        |
        v
MimiClaw sends POST to https://api.tavily.com/search
  - Authorization: Bearer tvly-xxxxx
  - Body: {"query": "...", "max_results": 5, "search_depth": "basic"}
        |
        v
Tavily returns structured results
        |
        v
Agent incorporates results into response

Each basic search costs 1 API credit. With the free plan's 1,000 credits/month, that's about 33 searches per day.

API Details

MimiClaw uses the Tavily Search API endpoint:

Parameter Value
Endpoint POST https://api.tavily.com/search
Auth Authorization: Bearer <API_KEY>
Content-Type application/json

Request Body (as sent by MimiClaw)

Field Type Value Description
query string (from agent) The search query
max_results integer 5 Number of results to return
search_depth string "basic" Search depth (basic = 1 credit)

Response Fields

Field Type Description
query string The executed search query
results array Ranked search results
results[].title string Result title
results[].url string Source URL
results[].content string Summary snippet
results[].score float Relevance score (0-1)
response_time float Execution time in seconds

HTTP Status Codes

Code Meaning
200 Success
400 Bad Request โ€” invalid parameters
401 Unauthorized โ€” missing or invalid API key
429 Rate Limit โ€” too many requests
432 Plan Limit โ€” monthly quota exceeded
500 Server Error

Pricing

Tavily offers a generous free tier and several paid plans:

Plan Monthly Credits Cost Per-Credit Rate
Researcher 1,000 Free --
Project 4,000 $30 $0.0075
Bootstrap 15,000 $100 $0.0067
Startup 38,000 $220 $0.0058
Growth 100,000 $500 $0.005
Pay-as-you-go Variable $0.008/credit $0.008

Credit costs per search:

  • basic search depth: 1 credit
  • advanced search depth: 2 credits

MimiClaw uses basic by default, so each search = 1 credit.

Rate Limits

Environment Limit
Development 100 requests per minute
Production 1,000 requests per minute

For an ESP32-based agent, you will never hit these limits in practice.

Troubleshooting

"No search provider configured"

The agent returns this when neither Tavily nor Brave Search keys are set.

Fix: Set a Tavily API key via set_tavily_key CLI command or in mimi_secrets.h.

Search returns empty results

  • Verify your API key is correct: config_show should show tavily_key: tvly-****
  • Check your monthly credit balance at app.tavily.com
  • Ensure the ESP32 has internet access: wifi_status

HTTP 401 Unauthorized

Your API key is invalid or expired. Generate a new one at app.tavily.com.

HTTP 432 Plan Limit

You've exceeded your monthly credit quota. Wait for the next billing cycle or upgrade your plan.

Search works but results are poor

Tavily's basic search depth is optimized for speed. The quality is generally sufficient for an AI agent's needs. If you need higher quality, you can modify SEARCH_RESULT_COUNT in tool_web_search.c.

References


--- METRICS ---