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>
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>.