Files
nanoclaw-mcp-ext/nanoclaw-skill/SKILL.md
T
gurixs_carsonandClaude Opus 4.6 3fa0ec8f71 feat: add NanoClaw server-side skill and update docs
Adds the complete server-side MCP implementation as a NanoClaw
feature skill (nanoclaw-skill/). Includes source code, modification
intents, and step-by-step SKILL.md for installation. README updated
to cover both server (NanoClaw) and client (pi.dev) setup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 22:38:56 +02:00

4.7 KiB

name, description
name description
add-mcp 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

npm install @modelcontextprotocol/sdk zod

Copy source file

Copy src/mcp-server.ts from the skill directory into the project:

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):

// mcp
import '../mcp-server.js';

Modify config.ts

Add 'MCP_API_KEY' and 'MCP_PORT' to the readEnvFile array in src/config.ts:

const envConfig = readEnvFile([
  // ... existing keys ...
  'MCP_API_KEY',
  'MCP_PORT',
]);

Add these exports (before or after existing config exports):

// 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:

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:

// Start external MCP server if API key is configured
let mcpServer: ReturnType<typeof startMcpServer> | undefined;
if (MCP_API_KEY) {
  mcpServer = startMcpServer(MCP_PORT, MCP_API_KEY, {
    registeredGroups: () => registeredGroups,
  });
}

In the shutdown() function, add before queue.shutdown(...):

(await mcpServer)?.close();

Validate

npm run build

Build must succeed before proceeding.

Phase 3: Setup

Generate API Key

openssl rand -base64 32

Configure environment

Add to .env:

MCP_API_KEY=<generated-key>
MCP_PORT=3002

Rebuild and restart

npm run build

Then restart the service:

# Linux (systemd)
sudo systemctl restart nanoclaw

# macOS (launchd)
launchctl kickstart -k gui/$(id -u)/com.nanoclaw

Verify

# 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

export MCP_API_KEY="<your-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:

ssh -L 3002:localhost:3002 user@your-server -N

Then configure the MCP client:

{
  "mcpServers": {
    "nanoclaw": {
      "type": "streamable-http",
      "url": "http://localhost:3002/mcp",
      "headers": {
        "Authorization": "Bearer <MCP_API_KEY>"
      }
    }
  }
}

See the pi.dev extension 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 <container-name>.