nanobot/docs/mcp_local_clone_and_tool_profiles.md
tanyar09 4b808f9a30 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
2026-03-31 12:53:34 -04:00

4.4 KiB
Raw Blame History

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):
./scripts/setup-mcp-servers.sh gitea
  1. Add to per-bot config (host) ~/.nanobot-user1/config.json:
{
  "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"
        }
      }
    }
  }
}
  1. 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.