--- name: add-mcp description: Add an MCP (Model Context Protocol) server to NanoClaw so external agents can chat with the assistant via Streamable HTTP. Includes SSH tunnel setup for secure access. --- # Add MCP Server This skill adds an MCP server to NanoClaw, allowing external agents (Claude Code, pi.dev, custom clients) to have a dialog with the assistant. ## Phase 1: Pre-flight ### Check if already applied Check if `src/mcp-server.ts` exists. If it does, skip to Phase 3 (Setup). The code changes are already in place. ## Phase 2: Apply Code Changes ### Install dependencies ```bash npm install @modelcontextprotocol/sdk zod ``` ### Copy source file Copy `src/mcp-server.ts` from the skill directory into the project: ```bash cp ${CLAUDE_SKILL_DIR}/src/mcp-server.ts src/mcp-server.ts ``` ### Modify channel barrel file Read the intent file at `${CLAUDE_SKILL_DIR}/modify/src/channels/index.ts.intent.md`, then apply it to `src/channels/index.ts`. Add this import line (before or after existing channel imports): ```typescript // mcp import '../mcp-server.js'; ``` ### Modify config.ts Add `'MCP_API_KEY'` and `'MCP_PORT'` to the `readEnvFile` array in `src/config.ts`: ```typescript const envConfig = readEnvFile([ // ... existing keys ... 'MCP_API_KEY', 'MCP_PORT', ]); ``` Add these exports (before or after existing config exports): ```typescript // MCP server for external agent access export const MCP_API_KEY = process.env.MCP_API_KEY || envConfig.MCP_API_KEY || ''; export const MCP_PORT = parseInt( process.env.MCP_PORT || envConfig.MCP_PORT || '3002', 10, ); ``` ### Modify index.ts Add imports at the top: ```typescript import { startMcpServer } from './mcp-server.js'; import { MCP_API_KEY, MCP_PORT } from './config.js'; ``` Note: `MCP_API_KEY` and `MCP_PORT` should be added to the existing config import, not a separate one. In the `main()` function, after `startCredentialProxy(...)`, add: ```typescript // Start external MCP server if API key is configured let mcpServer: ReturnType | undefined; if (MCP_API_KEY) { mcpServer = startMcpServer(MCP_PORT, MCP_API_KEY, { registeredGroups: () => registeredGroups, }); } ``` In the `shutdown()` function, add before `queue.shutdown(...)`: ```typescript (await mcpServer)?.close(); ``` ### Validate ```bash npm run build ``` Build must succeed before proceeding. ## Phase 3: Setup ### Generate API Key ```bash openssl rand -base64 32 ``` ### Configure environment Add to `.env`: ``` MCP_API_KEY= MCP_PORT=3002 ``` ### Rebuild and restart ```bash npm run build ``` Then restart the service: ```bash # Linux (systemd) sudo systemctl restart nanoclaw # macOS (launchd) launchctl kickstart -k gui/$(id -u)/com.nanoclaw ``` ### Verify ```bash # Check MCP server is listening ss -tlnp | grep 3002 # Linux lsof -i :3002 # macOS # Check logs grep "MCP server listening" logs/nanoclaw.log | tail -1 grep "MCP channel connected" logs/nanoclaw.log | tail -1 ``` Both log lines should be present. ### Test ```bash export MCP_API_KEY="" curl -s -X POST http://localhost:3002/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $MCP_API_KEY" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` Should return `chat` and `list_groups` tools. ## Connecting External Agents The MCP server binds to `127.0.0.1` only (not publicly accessible). External clients connect via SSH tunnel: ```bash ssh -L 3002:localhost:3002 user@your-server -N ``` Then configure the MCP client: ```json { "mcpServers": { "nanoclaw": { "type": "streamable-http", "url": "http://localhost:3002/mcp", "headers": { "Authorization": "Bearer " } } } } ``` See the [pi.dev extension](../index.ts) in this repo for a working client implementation. ## Architecture - Registers as a NanoClaw **Channel** (like Telegram or WhatsApp) - Uses `mcp:` JID namespace — isolated from all other channels - Same container, workspace, tools, and CLAUDE.md as the target group - Container reuse via GroupQueue (cold start ~2-3 min, warm ~5-10s) - Responses debounced (3s) for multi-part agent output - Bearer token auth + SSH tunnel for security ## Troubleshooting **MCP server not starting:** Check `MCP_API_KEY` is set in `.env` and NanoClaw was rebuilt. **401 Unauthorized:** Bearer token doesn't match `MCP_API_KEY`. **Connection refused:** SSH tunnel not running, or NanoClaw not running. **Slow first response:** Expected — cold start boots a container (~2-3 min). Subsequent calls reuse it. **"(no response)":** Container timed out or crashed. Check `docker logs `.