Architecture — nanoclaw-extras¶
Component view¶
flowchart LR
subgraph Host["Host machine"]
Ollama["Ollama\n(local models,\nport 11434)"]
end
subgraph Container["NanoClaw agent container"]
Agent["NanoClaw agent\n(reads MCP tool list)"]
Bridge["extras/ollama-mcp.mjs\n(stdio MCP server,\nzero deps)"]
Agent -- "stdio JSON-RPC\n(tools/list, tools/call)" --> Bridge
end
subgraph CLI["Host CLI"]
Wire["extras/wire-ollama.sh"]
NanoCLI["src/cli/client.ts\n(NanoClaw's own CLI)"]
Wire -- "groups config add-mcp-server" --> NanoCLI
Wire -- "cp ollama-mcp.mjs" --> Container
end
Bridge -- "HTTP: GET /api/tags\nPOST /api/generate" --> Ollama
NanoCLI -. "registers Bridge as an MCP server\nfor each agent group" .-> Container
wire-ollama.sh is a one-time (or re-run-on-demand) setup step run on the host. It never
talks to Ollama directly — it only copies the bridge file into each agent group's folder
and tells NanoClaw's own CLI to register it as an MCP server, with OLLAMA_HOST and
DEFAULT_MODEL baked into the registered environment. From then on, the bridge process
(spawned by NanoClaw inside the container) is the only thing that talks to Ollama, over
plain HTTP to host.docker.internal:11434.
Sequence: an agent calls ollama_generate¶
sequenceDiagram
participant Agent as NanoClaw agent
participant Bridge as ollama-mcp.mjs
participant Ollama as Host Ollama (:11434)
Agent->>Bridge: stdin: {"method":"tools/call","params":{"name":"ollama_generate","arguments":{"prompt":"..."}}}
Bridge->>Bridge: build request body (model = arg or DEFAULT_MODEL, stream=false)
Bridge->>Ollama: POST /api/generate
Ollama-->>Bridge: 200 {"response": "..."}
Bridge-->>Agent: stdout: {"result":{"content":[{"type":"text","text":"..."}]}}
If Ollama returns a non-2xx status, the bridge catches the error inside tools/call
handling and replies with isError: true and the error message as text content — it never
crashes the stdio loop, so one bad call does not kill the MCP server for the rest of the
session.
Why stdio + zero dependencies¶
The bridge is intentionally a single file with no package.json dependency tree: it is
copied directly into each NanoClaw agent group's folder and spawned as node
/workspace/agent/ollama-mcp.mjs inside the container (see extras/wire-ollama.sh), so
there is no install step inside the container image — only Node's own runtime (18+, for
built-in fetch) is required.