Ida-Telegram
An MCP server that sends and receives Telegram messages to a fixed contact, and triggers claude.ai routines for automatic replies.
README
Ida-Telegram
Ein eigenständiger MCP-Server (Model Context Protocol), getrennt von Ida-Untis und ohne Verbindung dazu. Zwei Dinge in einem Container:
- Zwei MCP-Werkzeuge für Claude: einer fest konfigurierten Person eine
Telegram-Nachricht schicken (
nachricht_senden), und lesen, was sie gerade geschrieben hat (neue_nachrichten_abrufen) -- Fotos kommen dabei als echter Bildinhalt mit, den die Routine wirklich "sehen" kann. - Ein Hintergrund-Loop, der neue Telegram-Nachrichten erkennt (Text, Fotos, Sprachnachrichten) und dafür eine claude.ai Routine triggert -- die Routine liest die Nachricht dann selbst über die MCP-Tools oben und antwortet.
Läuft als Docker-Container und wird über einen bestehenden Cloudflare Tunnel unter einer eigenen Domain erreichbar gemacht.
Architektur
claude.ai Routine (Cloud-Agent)
| ^
(per API-Trigger) (MCP: nachricht_senden,
| neue_nachrichten_abrufen)
| |
| v
Claude (MCP-Client) --https--> Cloudflare Tunnel (öffentliche Domain)
|
v
127.0.0.1:4567 auf deinem Server
|
v
Docker-Container "ida-telegram-mcp"
^
| (long-polling, ausgehend)
Telegram Bot HTTP-API
^
Telegram-Nutzer schreibt dem Bot
Wichtig: Dieser Container ruft selbst nie eine Claude-API auf. Er macht nur zwei Dinge -- Telegram per Long-Polling nach neuen Nachrichten fragen, und bei neuen Nachrichten eine claude.ai Routine über deren eigenen API-Trigger anstoßen. Die eigentliche "Intelligenz" (Nachricht lesen, Antwort formulieren) läuft komplett in der Routine bei Anthropic, die sich dafür ganz normal als MCP-Client mit diesem Server verbindet -- genau wie Claude Code oder claude.ai es auch tun.
Der Container published seinen Port nur auf 127.0.0.1 -- von außen
nicht direkt erreichbar, nur über den bereits laufenden cloudflared-Prozess.
Zusätzlich verlangt der Server bei jeder Anfrage ein geheimes Token
(MCP_AUTH_TOKEN).
Wichtigste Design-Entscheidung: Es gibt keinen Empfänger-Parameter.
TELEGRAM_CHAT_ID in der .env legt die einzige Person fest, an die dieser
Server jemals schreiben kann -- weder Claude noch sonst jemand kann über die
Tools eine andere chat_id angeben. Eine Nachricht erreicht eine echte Person
und lässt sich nicht zurückholen, deshalb ist der Empfänger bewusst fest
verdrahtet statt frei wählbar.
Voraussetzungen
- Docker + Docker Compose auf dem Server
- Ein bereits eingerichteter und verbundener Cloudflare Tunnel auf diesem Server
- Ein Telegram-Bot (in wenigen Minuten selbst erstellt, siehe unten)
- Ein claude.ai-Account, um die Routine anzulegen
1. Telegram-Bot erstellen
- In Telegram den Chat mit @BotFather öffnen.
/newbotsenden, Namen vergeben -- du bekommst einen Token wie123456789:ABC-DEF1234ghIkl-zyx57W2v1u123ew11.- Die Person, die die Nachrichten empfangen soll, schreibt dem neuen Bot einmal eine beliebige Nachricht (z.B. "hi"). Wichtig: Ein Bot kann niemanden anschreiben, der ihm nicht vorher selbst geschrieben hat.
- Im Browser aufrufen (mit echtem Token):
https://api.telegram.org/bot<TOKEN>/getUpdatesIm JSON nach"chat":{"id": ...}suchen -- das ist diechat_id.
2. Einrichten, bauen, starten
git clone https://github.com/<dein-user>/Ida-Telegram.git
cd Ida-Telegram
cp .env.example .env
.env erstmal mit TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID und
MCP_AUTH_TOKEN ausfüllen (siehe Tabelle unten) -- ROUTINE_ID
und ROUTINE_API_KEY folgen in Schritt 5, dafür muss der Server erst
erreichbar sein.
Image bauen lassen: Bei jedem Push auf main baut
.github/workflows/docker-publish.yml das Image automatisch nach
ghcr.io/<dein-user>/ida-telegram:latest. Einmalig auf öffentlich stellen
(GitHub -> Profil -> Packages -> ida-telegram -> Package settings ->
Change visibility -> Public), damit docker compose es ohne Login ziehen
kann.
docker compose pull
docker compose up -d
docker compose logs -f
(Mit AUTOREPLY_ENABLED=true als Standard startet der Container zunächst
mit Fehler, weil ROUTINE_ID/ROUTINE_API_KEY noch fehlen --
das ist normal, kommt in Schritt 5. Alternativ jetzt schon AUTOREPLY_ENABLED=false
setzen und später wieder auf true.)
3. An den bestehenden Cloudflare Tunnel anbinden
Analog zu Ida-Untis, nur mit eigenem Hostname und Port 4567:
ingress:
- hostname: telegram.deine-domain.de
service: http://localhost:4567
- service: http_status:404
(Bzw. im Zero-Trust-Dashboard unter Public Hostname eintragen.) Danach
cloudflared neu laden.
4. Als claude.ai Connector hinzufügen
Genau wie bei Ida-Untis: claude.ai -> Einstellungen -> Connectors -> Add
custom connector -> als URL
https://telegram.deine-domain.de/mcp?token=<MCP_AUTH_TOKEN> eintragen.
5. Routine anlegen (der eigentliche Auto-Antwort-Teil)
- Auf claude.ai/code/routines -> Neue Routine.
- Name: z.B. "Ida Telegram Autoreply".
- Anweisungen: (setzt voraus, dass die Routine sowohl den
Ida-Telegram- als auch den Ida-Memory-Connector
hat, siehe Schritt 5)
Du bist der Telegram-Assistent fuer die eine fest konfigurierte Person.
-
Neue Nachricht lesen: Ruf ueber den Ida-Telegram-Connector
neue_nachrichten_abrufenauf. Das koennen Text, Fotos (die du dir direkt anschauen kannst) oder Hinweise auf Sprachnachrichten sein. Liefert das Tool nichts, mach nichts und brich ab. -
Kontext holen: Ruf zusaetzlich
chat_verlaufauf, um die letzten Nachrichten (beide Richtungen) zu sehen -- hilft z.B. zu verstehen, worauf sich eine kurze Nachricht bezieht. -
Gedaechtnis pruefen, BEVOR du antwortest -- aber nur wenn noetig: Wenn du die Antwort schon aus der Nachricht selbst oder aus
chat_verlaufkennst, ueberspring diesen Schritt. Sonst nutze den Ida-Memory-Connector:search_nodesmit den wichtigsten Stichworten aus der Nachricht (Namen, Projekte, Themen). Bei relevanten Treffern mitopen_nodesdie genauen Eintraege nachladen. Keinread_graphbenutzen -- das laedt unnoetig den kompletten Bestand. Findest du nichts und weisst es auch sonst nicht -- sag ehrlich, dass du es nicht weisst, statt etwas zu erfinden. -
Antworten: Kurze, freundliche, hilfreiche Antwort auf Deutsch ueber
nachricht_senden. Relevante Infos aus Schritt 2/3 einbauen, wenn sie zur Nachricht passen. Bei Sprachnachrichten zuerstsprachnachricht_transkribieren(voice_id)mit der id ausneue_nachrichten_abrufenaufrufen, um den Inhalt zu verstehen. -
Essensplan-Sonderfall: Wenn ein Foto ein Essensplan fuer die Woche ist, merke dir NUR das Mittagessen pro Tag (z.B. als Entity "Essensplan KW<Nummer>" mit einer observation pro Tag, "Montag: Spaghetti Bolognese"). Fruehstueck und Abendessen auf demselben Bild ignorierst du, auch wenn sie draufstehen. Wird spaeter nach dem Essen an einem bestimmten Tag gefragt: wie in Schritt 3 im Gedaechtnis nachschauen.
-
Gedaechtnis aktualisieren, NACH der Antwort: Enthaelt die Nachricht sonst einen dauerhaft nuetzlichen Fakt (Vorliebe, laufendes Projekt, wiederkehrende Info, Korrektur zu etwas Gespeichertem) -- ueber Ida-Memory speichern:
create_entitiesnur fuer neue Personen/Projekte/ Themen,add_observationsfuer neue Fakten zu bestehenden Entities (nur an die, zu der sie wirklich gehoeren),create_relationsfuer dauerhafte Zusammenhaenge. NICHT jede Kleinigkeit speichern -- im Zweifel lieber nichts speichern als zu viel.
-
- Trigger: "API" auswählen (nicht Zeitplan). claude.ai zeigt dir danach
einmalig einen API-Token an (
sk-ant-oat01-...) -- sofort notieren, er wird danach nicht mehr im Klartext angezeigt. - Bei Konnektoren den gerade hinzugefügten
Ida-Telegram-Connector auswählen. - Routine speichern.
- Die Routine-ID aus der URL ablesen, die claude.ai beim Bearbeiten der
Routine anzeigt (
claude.ai/code/routines/trig_...-- der Teil abtrig_ist die ID), und zusammen mit dem Token in.enveintragen:
ROUTINE_ID=<trig_...>
ROUTINE_API_KEY=<der-notierte-api-token>
Technischer Hintergrund: der Server ruft dafür
POST https://api.anthropic.com/v1/claude_code/routines/<ROUTINE_ID>/fire
auf (offizieller Endpunkt für Routinen-Trigger, siehe
Doku) --
ROUTINE_ID und ROUTINE_API_KEY sind alles, was dafür gebraucht wird.
- Neu starten:
docker compose up -d
Ab jetzt: schreibt die konfigurierte Person dem Telegram-Bot, triggert der Container die Routine, die Routine liest die Nachricht über MCP und antwortet über MCP zurück auf Telegram.
Verfügbare MCP-Tools
| Tool | Zweck |
|---|---|
nachricht_senden(text) |
Schickt text an die fest konfigurierte Person. Stoppt dabei automatisch die "tippt..."-Anzeige und trägt die Antwort in chat_verlauf ein |
neue_nachrichten_abrufen() |
Gibt zurück, was den aktuellen Routine-Lauf ausgelöst hat (jeweils nur einmal): Text als String, Fotos als echten Bildinhalt, Bildunterschriften als eigener Text, Sprachnachrichten als Hinweistext mit voice_id |
sprachnachricht_transkribieren(voice_id) |
Transkribiert eine zwischengespeicherte Sprachnachricht zu Text -- läuft lokal in diesem Container (faster-whisper), keine Audiodaten verlassen die eigene Infrastruktur |
chat_verlauf() |
Letzte CHAT_HISTORY_LENGTH Nachrichten (Standard 5, beide Richtungen) als leichtgewichtiger Text -- fürs Gesprächsgedächtnis über den aktuellen Lauf hinaus. Nicht destruktiv, beliebig oft abrufbar. Fotos nur als [Foto]-Platzhalter, keine Bilddaten |
bot_status() |
Prüft nur, ob Token/Bot erreichbar sind (sendet nichts) |
Während ein Routine-Lauf auf eine Antwort wartet, zeigt der Bot in Telegram automatisch "tippt..." an (aktualisiert alle 4s, damit es nicht ausblendet) -- kein eigenes Tool dafür nötig, das läuft im Hintergrund mit.
Persistentes Gedächtnis (über einzelne Routine-Läufe hinweg, gemeinsam nutzbar von mehreren KIs/Connectors) liegt bewusst nicht hier, sondern im separaten Ida-Memory-Projekt -- der Routine dafür zusätzlich diesen Connector geben.
Unterstützte Nachrichtentypen:
| Typ | Verhalten |
|---|---|
| Text (auch formatiert, z.B. fett) | Wird 1:1 als Text an die Routine weitergegeben |
| Foto | Wird heruntergeladen und als echter Bildinhalt weitergegeben -- die Routine kann es tatsächlich "sehen" (Claude-Vision über MCP-Bildinhalte) |
| Sprachnachricht | Wird heruntergeladen und zwischengespeichert (Hinweis mit voice_id). Keine automatische Transkription -- die Routine ruft bei Bedarf gezielt sprachnachricht_transkribieren(voice_id) auf (lokal per Whisper, siehe unten) |
| Sticker, Videos, Dokumente | Werden aktuell ignoriert |
Lokale Sprachnachrichten-Transkription (Whisper)
sprachnachricht_transkribieren läuft direkt in diesem Container --
kein externer Dienst, keine Audiodaten verlassen die eigene Infrastruktur.
Technisch: faster-whisper
(CTranslate2-Engine statt des originalen openai-whisper/PyTorch-Stacks --
deutlich schnellere und speicherschonendere CPU-Inferenz, wichtig auf einem
VPS ohne GPU).
- Auf Abruf, nicht automatisch: Eine Sprachnachricht wird beim Empfang
nur heruntergeladen und mit einer
voice_idzwischengespeichert (die letzten 20, älteste fällt raus). Transkribiert wird erst, wenn die Routine das Tool tatsächlich aufruft -- spart Rechenzeit für Sprachnachrichten, die z.B. gar nicht beantwortet werden. - Modell wird lazy geladen: Nicht beim Containerstart, sondern beim ersten tatsächlichen Transkriptions-Aufruf -- der ist dadurch spürbar langsamer (Download + Laden), jeder weitere Aufruf nutzt das bereits geladene Modell und ist deutlich schneller.
- Ressourcen:
WHISPER_MODEL=base(Standard) ist ein Kompromiss aus Geschwindigkeit/Genauigkeit für eine CPU-VPS ohne GPU. Bei sehr begrenztem RAMtinyprobieren, bei Bedarf an Genauigkeitsmall. Das Modell wird nach dem ersten Download im/data-Volume gecacht. - Bekanntes Whisper-Verhalten: Auf sehr kurzen/leisen/inhaltsleeren Aufnahmen "halluziniert" das Modell manchmal plausibel klingenden, aber falschen Text (ein dokumentiertes Verhalten aller Whisper-Modelle, kein Bug dieses Servers) -- bei zweifelhaften Ergebnissen im Zweifel nachfragen.
Wie der Auto-Antwort-Loop funktioniert
Wenn AUTOREPLY_ENABLED=true (Standard) läuft im Container ein
Hintergrund-Thread:
- Fragt Telegram per Long-Polling nach neuen Nachrichten der konfigurierten
Person (
TELEGRAM_CHAT_ID) -- Nachrichten von anderen werden ignoriert. - Kommen mehrere Nachrichten schnell hintereinander, wartet der Server
AUTOREPLY_DEBOUNCE_SECONDSauf weiteren Nachschub und bündelt alles -- die Routine wird dann einmal getriggert statt einmal pro Nachricht. - Schickt einen
POSTmitAuthorization: Bearer $ROUTINE_API_KEYan den Routinen-Endpunkt (ROUTINE_IDin der URL) -- der gebündelte Text geht alstext-Feld direkt mit (sofortiger Kontext für die Routine), zusätzlich liefertneue_nachrichten_abrufendenselben Text noch einmal ab, falls die Routine ihn lieber über MCP nachlesen will.
Kein Doppelt-Antworten: Telegrams getUpdates-Offset-Mechanismus sorgt von
selbst dafür, dass jede Nachricht genau einmal in den Zwischenspeicher
wandert, auch nach einem Neustart des Containers; neue_nachrichten_abrufen
liefert jede Nachricht ebenfalls nur einmal aus.
Kosten: Jeder Routine-Lauf verbraucht claude.ai-Nutzung auf deinem Account (Cloud-Agent-Sitzung), nicht eine separate API-Rechnung.
Lokal testen ohne Cloudflare
docker compose up -d
curl -H "Authorization: Bearer $MCP_AUTH_TOKEN" http://127.0.0.1:4567/healthz
Troubleshooting
- Container startet nicht:
docker compose logs-- meist fehlt eine Pflicht-Variable in.env(z.B.ROUTINE_ID/ROUTINE_API_KEYfehlen, obwohlAUTOREPLY_ENABLED=trueist). Telegram-API-Fehler: chat not found: Die Zielperson hat dem Bot noch nie geschrieben (siehe Schritt 1.3), oder diechat_idist falsch.Telegram-API-Fehler: Unauthorized:TELEGRAM_BOT_TOKENfalsch/abgelaufen.- Claude bekommt 401: Token in Client-Konfiguration und
.envvergleichen. - Routine wird nicht getriggert:
docker compose logs -fprüfen -- Zeile "Telegram-Autoreply-Loop gestartet" sollte beim Start erscheinen, und bei neuer Nachricht "Neue Nachricht(en) erhalten, triggere Routine...". Bei einem HTTP-Fehler danach:ROUTINE_ID/ROUTINE_API_KEYprüfen (401 = Token falsch/gehört nicht zu dieser Routine, 404 =ROUTINE_IDfalsch). - Routine läuft, antwortet aber nicht: In claude.ai unter Routinen die
letzte Sitzung öffnen und den Verlauf prüfen -- meist fehlt der
Ida-Telegram-Connector bei den Konnektoren der Routine, oder
neue_nachrichten_abrufenliefert eine leere Liste (Race Condition sehr unwahrscheinlich, aber möglich bei extrem kurzemAUTOREPLY_DEBOUNCE_SECONDS). Conflict: terminated by other getUpdates request: Der Bot-Token wird gleichzeitig noch woanders pergetUpdatesabgefragt (oder es ist ein Webhook für den Bot gesetzt) -- ein Telegram-Bot-Token kann immer nur von einem Prozess gleichzeitig per Long-Polling abgefragt werden.sprachnachricht_transkribierendauert beim ersten Aufruf sehr lange: normal -- das Modell wird dann erst heruntergeladen/geladen. Danach deutlich schneller. Bei dauerhaft sehr langsamer Transkription ein kleineresWHISPER_MODEL(z.B.tiny) probieren.- "Keine zwischengespeicherte Sprachnachricht ... gefunden": entweder
eine falsche/erfundene
voice_id, oder es sind seither mehr als 20 neue Sprachnachrichten eingegangen (älteste fallen aus dem Zwischenspeicher). - Container braucht spürbar mehr RAM als vorher: durch
faster-whisper+ geladenes Modell erwartet -- bei sehr begrenztem RAMWHISPER_MODEL=tinysetzen oderWHISPER_ENABLED=false.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.