Skip to content
Conduit

mcp server

claude-cli-telegram

Turn your Mac/Linux into a remote Claude Code terminal via Telegram. Spawns real CLI — sessions, streaming, approvals, voice, git panel, 30+ commands.

Imolatte/claude-cli-telegramcommunitylistedunreviewednot running here yet

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 14, 2026

Claude CLI ↔ Telegram

License: MIT Node.js Claude Code GitHub stars GitHub last commit GitHub issues

Turn your Mac or Linux machine into a remote Claude Code terminal — controlled from your phone via Telegram.

Send a message from Telegram → bot spawns real Claude Code CLI → streams progress → sends response back. Not an API wrapper — runs the actual CLI with full tool access (Bash, Read, Write, Edit, Grep, Glob, etc).

You (Telegram) → Bot (Node.js) → Claude Code CLI → tools → response → You
Voice input + streamingApproval buttonsFull approval flow
/status/sessions

Why This Exists

Anthropic's official Telegram plugin is a minimal MCP bridge — 3 tools, no history, no session management.

Claude CLI ↔ Telegram is a full remote terminal: sessions, streaming progress, approval system for dangerous ops, git panel, voice input, file/photo handling, Mac remote control, and 30+ commands - all from your phone.

Official PluginClaude CLI ↔ Telegram
ArchitectureMCP bridgeFull bot wrapper around CLI
Session managementNoneAuto-resume, rotate, switch, rename
Streaming progressNoneLive tool activity display
Dangerous op approvalNoneInline buttons (approve/deny)
Voice inputNoneGroq Whisper STT
Git integrationNoneFull panel (status/diff/log/push/pull)
Quick commandsNoneShell, system info, clipboard, files
i18nNoneEN + RU (auto-detected)

Quick Start

git clone https://github.com/Imolatte/claude-cli-telegram.git
cd tg-claude && cp config.example.json config.json
# Edit config.json — add bot token, chat ID, Groq API key
cd worker && npm install && node index.mjs

On first /start, a setup wizard walks you through OS, output mode, and token rotation settings.

Features

Input Types

  • Text → Claude Code with full tool access
  • Voice → Groq Whisper STT → Claude
  • Photos → downloaded, passed to Claude for vision analysis
  • Files → downloaded with original extension, passed to Claude
  • Forwards → analyzed by Claude (text before forward auto-combined)

Live Streaming Progress

While Claude works, see exactly what's happening:

🔧 Read: /src/auth.ts
🔧 Edit: /src/auth.ts
🔧 Bash: npm run build
📝 Writing response...

With /codediff enabled, edits show inline diffs:

🔧 Edit: /src/auth.ts
- import { getRegion } from './geo'
+ import { getRegion, USDT_PRICE } from './geo'

Token usage shown after each response: ↓3.2k ↑17k · 4.5s

Session Management

  • Auto-resume sessions across messages (--resume)
  • /sessions — inline keyboard to switch/delete
  • /new [name] — start fresh; /name <title> — rename
  • /detach — disconnect; /cd <path> — change working directory
  • Token usage tracking per session

Approval System

Dangerous operations require your approval:

  • git push/reset/clean, rm -rf, Docker commands
  • DB migrations (prisma migrate, DROP TABLE)
  • Deploys (vercel --prod, npm publish)
  • Sensitive file edits (.env, docker-compose, CI configs)

Dual-channel approval (terminal sessions):

  1. Terminal shows its normal permission prompt (1/2/3)
  2. If unanswered for 5 minutes, Telegram sends a notification
  3. With tmux (recommended): Approve/Deny buttons - taps inject 1 or 3 directly into the native prompt via tmux send-keys. Session keeps running, no context lost.
  4. Without tmux: only a Take over session button is offered as fallback. It kills the terminal Claude and resumes the same session in Telegram (context preserved on disk, but in-memory state like the current plan/todos is lost).

Tip: install tmux (brew install tmux) and launch Claude with tmux new -A -s claude claude (or add alias cc='tmux new -A -s claude claude' to your shell rc). Then dangerous-op approvals from Telegram answer the real terminal prompt without restarting anything. Without tmux only the takeover fallback works.

Telegram sessions: inline buttons sent immediately as before.

Output Modes

ModeResponsesApprovalsUse case
terminalTerminalTerminal (+ TG after 5 min)At desk
hybridTerminalTelegramAway, approvals on phone
telegramTelegramTelegramFully remote

Switch with /mode in Telegram or node mode.mjs <mode> in terminal.

Commands

Main

CommandDescription
/helpFull command reference
/statusMode, model, session, cwd, token progress bar
/setupRe-run first-time setup wizard
/stopKill running Claude process
/planToggle Plan / Build mode
/whereRun Claude on the Mac or on the server (see Mac and server)

Sessions

CommandDescription
/sessionsSession list with connect/delete buttons
/new [name]Start a new session
/name <title>Rename current session
/detachDisconnect from session
/cd <path>Set working directory (- to reset)

Display & Output

CommandDescription
/codediffToggle inline code diff in tool updates
/modeOutput mode: terminal / hybrid / telegram
/modelSwitch model: sonnet / opus / haiku
/botlangBot UI language: en / ru
/langVoice recognition language: ru / en / auto

Git

CommandDescription
/gitGit panel — Status, Diff, Log, Stage, Commit (AI message), Push, Pull
/git <args>Direct git command, e.g. /git log --oneline -5
/undoRollback last commit (soft/hard, with confirmation)
/diff [ref]Show git diff with pagination

Quick Commands (instant, no Claude)

CommandDescription
/sh <cmd>Run shell command
/sysCPU, RAM, disk, battery, Wi-Fi, IP, uptime
/clipGet/set clipboard
/dl <path>Download file to Telegram
/recentRecently edited files with one-tap download
/screenshot <url>Screenshot via Puppeteer
/cron <Xh/Xm> <text>Set a reminder

Mac Remote Control

CommandDescription
/sleepPut Mac to sleep
/lockLock screen
/shutdownShut down (with confirmation)
/rebootRestart (with confirmation)
/batteryBattery status with low-battery alerts

MCP: Claude → Telegram

Claude can proactively message you:

  • send_telegram(text) — send text message
  • send_file_telegram(file_path, caption?) — send file

Group Chat Support

Add the bot to any Telegram group:

  • Only the owner + users on the allowed list can interact
  • Bot responds when @mentioned or when replying to its messages
  • Dangerous operations from non-owners are automatically denied (owner gets a DM notification)

Setup

Prerequisites

  • Node.js 20+
  • Claude Code CLI installed and in PATH
  • Telegram Bot — create via @BotFather
  • Groq API key (free): groq.com → API Keys
  • Optional: FFmpeg (brew install ffmpeg) for voice fallback

Install

git clone https://github.com/Imolatte/claude-cli-telegram.git
cd tg-claude/worker && npm install
cp ../config.example.json ../config.json

Configure

Edit config.json:

{
  "botToken": "YOUR_BOT_TOKEN",
  "chatId": "YOUR_TELEGRAM_CHAT_ID",
  "groqApiKey": "YOUR_GROQ_API_KEY",
  "timeoutMs": 300000,
  "claudeTimeoutMs": 1800000,
}
FieldDescriptionDefault
botTokenTelegram bot token from @BotFatherrequired
chatIdYour Telegram user ID (owner)required
groqApiKeyGroq API key for voice STTrequired
timeoutMsApproval request timeout (ms)300000 (5 min)
claudeTimeoutMsMax time for a single Claude task (ms)1800000 (30 min)

Get your chatId: send any message to your bot, then open https://api.telegram.org/bot<TOKEN>/getUpdates — find chat.id.

Claude Code Hooks

Add to ~/.claude/settings.json for approval forwarding and completion notifications:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "",
      "hooks": [{ "type": "command", "command": "node /full/path/to/tg-claude/approval-hook.mjs", "timeout": 310 }]
    }],
    "Stop": [{
      "hooks": [{ "type": "command", "command": "node /full/path/to/tg-claude/stop-hook.mjs", "timeout": 10 }]
    }]
  }
}

Start

cd worker && node index.mjs

Autostart (macOS)

Create ~/Library/LaunchAgents/com.tg-claude.worker.plist pointing to node index.mjs in the worker directory. See launcher.sh for reference.

Logs: /tmp/tg-claude.log

Mac and server

The bot can run Claude in two places: on your Mac and on a Linux server. There are two ways to set that up.

ModeWhere the bot runsWhen to pick it
Mac only (role: "solo", the default)on the MacYou only work from one machine and don't mind the bot going quiet while the Mac sleeps.
Server first, Mac as standby (recommended)on the server, with the Mac taking over when neededYou want the bot to answer at 3 a.m. with the laptop closed, and still be able to run things on the Mac when you choose to.

How server-first works

  • The server worker (role: "primary") holds the bot by default and runs Claude right there.
  • The Mac worker (role: "standby") checks the server over ssh every 20 seconds. It takes the bot in two cases: the server has not answered three checks in a row, or you switched to the Mac with /where mac.
  • /where mac hands the bot to the Mac within about 20 seconds. /where server hands it back.
  • If you pick the Mac and then close the lid, the server notices the missing heartbeat after 90 seconds and takes the bot back, so it never goes silent.
  • Telegram allows one poller per bot token. Every hand-over is explicit, and the two workers never fight over updates.
  • Context lives where the CLI runs: the Mac and the server keep separate sessions.
  • Hand-overs are written to the logs only and never show up in the chat.

Server setup

On the server, as a regular user rather than root (Claude refuses --dangerously-skip-permissions under root):

git clone https://github.com/Imolatte/claude-cli-telegram.git ~/tg-claude
cd ~/tg-claude/worker && npm install
cp ../config.example.json ../config.json
echo server > target

Its config.json, in addition to the bot token and chat id:

{
  "role": "primary",
  "serverPrompt": "/home/you/.bot-system-prompt.md",
  "serverMcpConfig": "/home/you/.mcp.json"
}

Both paths are optional. They give the server Claude its own system prompt and MCP servers. Run it as a service. A minimal systemd unit:

[Service]
User=you
WorkingDirectory=/home/you/tg-claude/worker
EnvironmentFile=/home/you/.claude-token.env   # CLAUDE_CODE_OAUTH_TOKEN=... (no "export")
ExecStart=/usr/bin/node /home/you/tg-claude/worker/index.mjs
Restart=always

Get the token with claude setup-token on any machine where you're logged in.

Mac setup

On the Mac, in config.json:

{
  "role": "standby",
  "primaryHost": "you@your-server",
  "primaryUnit": "tg-claude",
  "primaryTargetFile": "/home/you/tg-claude/worker/target"
}

The Mac needs passwordless ssh to primaryHost, since it runs ssh -o BatchMode=yes. primaryUnit is the systemd unit to check. Restart the Mac worker, and /where then shows where Claude runs right now.

Mac only, with an occasional server run

Keep role: "solo" and add serverHost (and optionally serverClaude, serverPrompt, serverMcpConfig). Then /where server runs Claude on the server over ssh for as long as the Mac is awake.

Architecture

worker/
  index.mjs           Bot core: polling, commands, streaming, task queue
  executor.mjs         Spawns Claude Code CLI, parses stream-json events
  sessions.mjs         Session state management
  locale.mjs           i18n: EN + RU strings
  voice.mjs            Groq STT with local Whisper fallback
  mcp-telegram.mjs     MCP server: send_telegram + send_file_telegram

approval-hook.mjs      PreToolUse hook → dual-channel approval (terminal + TG)
stop-hook.mjs          Stop hook → Telegram completion notification
CLAUDE.md              Project context for Claude Code sessions
mode.mjs               CLI: switch output mode (terminal/hybrid/telegram)
bot-system-prompt.md   System prompt appended to Claude Code
config.json            Credentials (gitignored)

Design principles:

  • Minimal runtime dependencies (Puppeteer optional for screenshots)
  • No frameworks — vanilla Node.js, ESM modules
  • Single-process architecture — one node index.mjs runs everything
  • File-based IPC between hooks and bot (via /tmp/ files)
  • Works on macOS and Linux

i18n

Bot UI supports English and Russian. Language is auto-detected from your Telegram profile on first /start. Switch anytime with /botlang.

Adding a new language: copy the en object in worker/locale.mjs, translate all values, submit a PR.

Security

  • Owner-only — single chatId, all other users silently ignored
  • Group access control — explicit allowlist per user ID
  • No credentials in repo — config.json is gitignored
  • Duplicate protection — kills previous instances on startup
  • Stale session recovery — auto-retries without --resume on failure

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT

↑↓ navigate openesc close