No description
Find a file
Mikkel Georgsen a71595b9d8 feat: replace custom OAuth with FastMCP built-in OAuthProvider
FastMCP's OAuthProvider handles the full OAuth flow including DCR
(Dynamic Client Registration), authorization code + PKCE, token
issuance, and refresh tokens. No more custom auth code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:18:16 +00:00
docs feat: add ingest API + health endpoint, fix bot-to-bot logging 2026-03-30 08:39:44 +00:00
mcp_bridge feat: replace custom OAuth with FastMCP built-in OAuthProvider 2026-03-30 11:18:16 +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