Docs: MCP local clones and tool profiles

Document local-cloned MCP server layout, docker mounts, tool-call JSON protocol for local providers, profile routing behavior, and common gotchas. Add a brief pointer from the multi-bot Docker guide.

Made-with: Cursor
This commit is contained in:
tanyar09 2026-03-31 12:53:34 -04:00
parent 7050e032e8
commit 4b808f9a30
3 changed files with 107 additions and 0 deletions

2
.gitignore vendored
View File

@ -5,6 +5,8 @@
dist/
build/
docs/
!docs/*.md
!docs/**/*.md
*.egg-info/
*.egg
*.pyc

View File

@ -95,6 +95,16 @@ nanobot/
- Environment variables (from Docker env files)
- Config file: `/root/.nanobot/config.json` (mounted from host)
### MCP servers + tool profiles (local LLM note)
If youre using a local LLM provider with a **low tool limit** (often ~20 tools), MCP servers (which can register 30+ tools) can exceed the limit unless you use tool profiles carefully.
See `docs/mcp_local_clone_and_tool_profiles.md` for:
- Local-clone MCP layout (`./mcp-servers``/app/mcp-servers`)
- How MCP env vars like `$NANOBOT_GITLE_TOKEN` are expanded into server env
- Tool-call JSON protocol for local providers
- Profile routing behavior and when MCP servers disconnect
---
## Setup Instructions

View File

@ -0,0 +1,95 @@
# MCP local clones + tool profiles (local LLM friendly)
This documents the MCP/tool-profile work done to make MCP servers usable with local LLM providers that have **low tool limits** (e.g. ~20 tools) and/or unreliable native function-calling.
## What we changed
- **Local-clone policy for MCP servers**
- MCP servers are cloned/built locally under `./mcp-servers/` (not installed from npm/PyPI at runtime).
- Repo gitignore keeps the clones out of git while keeping `mcp-servers/README.md` and `.gitkeep`.
- Added `scripts/setup-mcp-servers.sh` to clone/build the Gitea MCP server (`mcp-servers/gitea-mcp/`) into a runnable binary.
- **Docker mounts for local MCP servers**
- Multi-bot compose files mount `./mcp-servers` into containers at `/app/mcp-servers:ro`.
- This makes config entries like `/app/mcp-servers/gitea-mcp/gitea-mcp` work inside the container.
- **MCP env var expansion**
- `nanobot/agent/tools/mcp.py` expands `$VARS` in MCP server `env` using the container environment, so configs can safely reference secrets without duplicating them:
- Example: `"GITEA_ACCESS_TOKEN": "$NANOBOT_GITLE_TOKEN"`
- **Local-provider tool calling reliability**
- `nanobot/agent/context.py` now includes a strict JSON tool-call protocol for providers that dont do native function calling.
- `nanobot/providers/custom_provider.py` was improved to parse tool calls when the model returns:
- Embedded JSON tool calls in content, or
- A message that is *only* a JSON object (`{"name": "...", "parameters": {...}}`)
- **Tool profile routing improvements**
- `nanobot/agent/tool_routing.py` includes a heuristic fast-path to pick an MCP-capable profile (e.g. `workspace_mcp`) when the user intent includes PRs/issues/repos/Gitea terms.
- The LLM router still applies for general cases; this heuristic prevents obvious “forge” intents from being routed to a no-MCP profile.
- **LLM empty-response retry**
- `nanobot/agent/loop.py` retries once if the provider returns an empty final message, nudging the model to either call a tool or respond with text.
## Where MCP servers live (host vs container)
- **Host repo path**: `./mcp-servers/`
- Example: `./mcp-servers/gitea-mcp/`
- **Inside the nanobot container**: `/app/mcp-servers/` (mounted read-only)
- Example: `/app/mcp-servers/gitea-mcp/gitea-mcp`
## Minimal Gitea MCP setup
1. Build the local server (host):
```bash
./scripts/setup-mcp-servers.sh gitea
```
2. Add to per-bot config (host) `~/.nanobot-user1/config.json`:
```jsonc
{
"tools": {
"mcpServers": {
"gitea": {
"command": "/app/mcp-servers/gitea-mcp/gitea-mcp",
"args": ["-t", "stdio", "--host", "http://10.0.30.169:3000", "-r"],
"env": {
"GITEA_ACCESS_TOKEN": "$NANOBOT_GITLE_TOKEN"
}
}
}
}
}
```
3. Ensure your compose file mounts the local MCP servers directory:
- `./mcp-servers:/app/mcp-servers:ro`
## Tool profiles: what they do (and what they dont)
- **Built-in tools** are registered once at gateway startup and do not “shut down”; profiles only control **exposure to the LLM per turn**.
- **MCP tools/servers** are connected lazily and can be **connected/disconnected per turn** based on the selected profile.
### When MCP servers disconnect
- **On a later message**, if a new profile is selected whose `mcpServers` set does not include a previously-connected server, nanobot disconnects that MCP server.
- On **gateway shutdown**, nanobot disconnects all MCP servers.
- `/new` **does not** disconnect MCP servers; it only clears session history and triggers memory consolidation.
## Common gotchas
- **Router picked `workspace` (no MCP)**
- If the router selects a profile with `"mcpServers": []`, MCP tools are hidden even if the servers are configured.
- Either improve routing, or (if acceptable) allowlist the specific MCP server in that profile (`"mcpServers": ["gitea"]`).
- **Tool count limits on local models**
- A single MCP server can register 30+ tools (Gitea MCP: ~30), which can exceed local providers tool limits.
- If you must stay under ~20 tools, prefer:
- Narrow profiles, or
- A “dispatcher tool” approach (one tool per MCP server) instead of registering every MCP tool individually.
- **Repo owner/repo mismatch**
- Errors like `GetUserByName` commonly mean the `owner` string doesnt exist or the token cannot see it.
- Resolve the canonical `owner/repo` first (search/list repos), then call list PRs with the correct pair.