No description
Find a file
Mikkel Georgsen d60b0208db fix: use internal Forgejo URL for token exchange and verification
Public git.georgsen.dk unreachable from LAN due to hairpin NAT.
Authorization endpoint stays public (browser redirect), but
token exchange and token verification use internal 10.5.0.14:3000.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:44:39 +00:00
docs feat: add ingest API + health endpoint, fix bot-to-bot logging 2026-03-30 08:39:44 +00:00
mcp_bridge fix: use internal Forgejo URL for token exchange and verification 2026-03-30 11:44:39 +00:00
.gitignore feat: MCP bridge - Telegram group logger + FastMCP HTTP server 2026-03-29 23:56:05 +00:00
credentials.example feat: add OAuth client credentials auth to MCP server 2026-03-30 09:45:04 +00:00
mcp-bridge.service feat: MCP bridge - Telegram group logger + FastMCP HTTP server 2026-03-29 23:56:05 +00:00
README.md feat: MCP bridge - Telegram group logger + FastMCP HTTP server 2026-03-29 23:56:05 +00:00
requirements.txt feat: MCP bridge - Telegram group logger + FastMCP HTTP server 2026-03-29 23:56:05 +00:00

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

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

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

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

cp mcp-bridge.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now mcp-bridge
systemctl --user status mcp-bridge

View logs:

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

{"message": "Fix the nexus.mg DNS record to 100.79.65.206"}

pull_updates

Get conversation messages with cursor-based pagination.

{"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