Single-process Python app that: - Runs a Telegram bot in a group chat, logging all messages/files to libsql - Exposes send_message, pull_updates, queue_status MCP tools over HTTP - Downloads and stores file attachments with Telegram file_id + local path - Accessible via NetBird mesh at mgmt.mg:8321 (no auth needed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
165 lines
5 KiB
Markdown
165 lines
5 KiB
Markdown
# Nexus MCP Bridge
|
|
|
|
MCP server that bridges claude.ai to a homelab Telegram group chat. Logs all messages and files to libsql, exposes three MCP tools over HTTP on the NetBird mesh.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
claude.ai ──HTTP──► MCP Bridge (mgmt.mg:8321) ──Telegram API──► Group Chat
|
|
│ │
|
|
libsql (local) Mikkel + Homelab Bot + MCP Bot
|
|
media/ (files)
|
|
```
|
|
|
|
Single Python process running:
|
|
- **Telegram bot** — polls group chat, logs everything to libsql, sends outbound messages
|
|
- **FastMCP HTTP server** — exposes `send_message`, `pull_updates`, `queue_status` tools
|
|
|
|
## Setup Guide
|
|
|
|
### Step 1: Create a new Telegram bot
|
|
|
|
1. Open Telegram, message [@BotFather](https://t.me/BotFather)
|
|
2. Send `/newbot`
|
|
3. Name: `Nexus MCP Bridge` (or whatever you like)
|
|
4. Username: `nexus_mcp_bot` (must be unique, pick something available)
|
|
5. Copy the **bot token** BotFather gives you
|
|
|
|
### Step 2: Create the Telegram group
|
|
|
|
1. In Telegram, create a **new group**
|
|
2. Name: `Homelab Bridge` (or your preference)
|
|
3. Add members:
|
|
- Your existing homelab bot (`@georgsen_homelab_bot`)
|
|
- The new MCP bot (search by the username you chose)
|
|
4. **Important:** Make both bots **group admins** so they can read all messages
|
|
- Tap group name → Edit → Administrators → Add both bots
|
|
- Bots need at minimum: "Read messages" permission (enabled by default for admins)
|
|
|
|
### Step 3: Get the group chat ID
|
|
|
|
Option A — Send a message in the group, then check:
|
|
```bash
|
|
curl -s "https://api.telegram.org/bot<MCP_BOT_TOKEN>/getUpdates" | python3 -m json.tool
|
|
```
|
|
Look for `"chat": {"id": -100XXXXXXXXXX}` — the negative number is the group chat ID.
|
|
|
|
Option B — The bridge logs the chat ID on startup. You can start it once, send a message in the group, and check the logs.
|
|
|
|
### Step 4: Configure credentials
|
|
|
|
```bash
|
|
cd ~/repos/telegram-bot-mcp
|
|
cp credentials.example credentials
|
|
```
|
|
|
|
Edit `credentials`:
|
|
```
|
|
MCP_BOT_TOKEN=<token from BotFather>
|
|
GROUP_CHAT_ID=<negative number from step 3>
|
|
HOMELAB_BOT_ID=8521598773
|
|
```
|
|
|
|
### Step 5: Install and test
|
|
|
|
```bash
|
|
cd ~/repos/telegram-bot-mcp
|
|
|
|
# Create venv (if not already done)
|
|
python3 -m venv .venv
|
|
|
|
# Install dependencies
|
|
.venv/bin/pip install -r requirements.txt
|
|
|
|
# Test run (Ctrl+C to stop)
|
|
.venv/bin/python -m mcp_bridge
|
|
```
|
|
|
|
You should see:
|
|
```
|
|
Database initialized at /home/mikkel/repos/telegram-bot-mcp/data/bridge.db
|
|
MCP server starting on 0.0.0.0:8321
|
|
Telegram bot polling started
|
|
MCP Bridge bot started as @nexus_mcp_bot (ID: XXXXXXX)
|
|
Monitoring group chat: -100XXXXXXXXXX
|
|
```
|
|
|
|
Send a message in the group — you should see it logged.
|
|
|
|
### Step 6: Install systemd service
|
|
|
|
```bash
|
|
cp mcp-bridge.service ~/.config/systemd/user/
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now mcp-bridge
|
|
systemctl --user status mcp-bridge
|
|
```
|
|
|
|
View logs:
|
|
```bash
|
|
journalctl --user -u mcp-bridge -f
|
|
```
|
|
|
|
### Step 7: Configure claude.ai MCP connection
|
|
|
|
In claude.ai settings, add a new MCP server:
|
|
|
|
- **URL:** `http://mgmt.mg:8321/mcp`
|
|
- **Transport:** Streamable HTTP
|
|
|
|
This works because mgmt.mg resolves via NetBird mesh — no public exposure needed.
|
|
|
|
## MCP Tools
|
|
|
|
### send_message
|
|
Send a message to the group, attributed as `[claude.ai]`.
|
|
|
|
```json
|
|
{"message": "Fix the nexus.mg DNS record to 100.79.65.206"}
|
|
```
|
|
|
|
### pull_updates
|
|
Get conversation messages with cursor-based pagination.
|
|
|
|
```json
|
|
{"since_id": 0, "limit": 50}
|
|
```
|
|
|
|
Returns messages from all participants with attachment metadata. Use the `cursor` value from the response as `since_id` in the next call.
|
|
|
|
### queue_status
|
|
Quick summary: total messages, last activity, pending outbound count.
|
|
|
|
## File Structure
|
|
|
|
```
|
|
├── mcp_bridge/
|
|
│ ├── __main__.py # Entry point (bot + MCP server)
|
|
│ ├── config.py # Configuration loader
|
|
│ ├── db.py # libsql database layer
|
|
│ ├── telegram_bot.py # Telegram bot (logging + sending)
|
|
│ ├── mcp_server.py # FastMCP tool definitions
|
|
│ └── models.py # Data models
|
|
├── data/ # libsql database (auto-created)
|
|
├── media/ # Downloaded attachments (auto-created)
|
|
├── credentials # Bot token + chat ID (not in git)
|
|
├── credentials.example # Template
|
|
├── requirements.txt
|
|
└── mcp-bridge.service # systemd unit file
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
**Bot doesn't see group messages:**
|
|
- Ensure bot is a group admin
|
|
- Check BotFather: `/mybots` → Bot Settings → Group Privacy → Turn OFF
|
|
(bots have "privacy mode" ON by default — they only see commands unless it's disabled)
|
|
|
|
**Can't get group chat ID:**
|
|
- Make sure you sent a message AFTER adding the bot
|
|
- For supergroups, the ID starts with `-100`
|
|
|
|
**MCP server unreachable from claude.ai:**
|
|
- Verify NetBird is connected: `netbird status`
|
|
- Test locally: `curl http://mgmt.mg:8321/mcp`
|
|
- Check firewall: port 8321 must be open
|