Skip to content

Architecture

Overview

The bot is a Node.js application built with Telegraf. It bridges Telegram DMs to isolated Claude Code CLI subprocesses using an MCP (Model Context Protocol) relay.

┌──────────────────────────────────────────────────────────────────────┐
│ Telegram │
│ Long-polling via Telegraf bot │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ index.ts │
│ Command routing, inline keyboard handlers, session orchestration │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ SessionManager │
│ Per-user ClaudeSession lifecycle │
└──────────────────────────────────────────────────────────────────────┘
┌─────────────────┴──────────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────────────────┐
│ ClaudeSession │ │ RelayServer │
│ Spawns Claude CLI │ │ Local HTTP server (localhost) │
│ subprocess │ │ Proxies MCP tool calls back │
│ stream-json output │ │ to the main process │
└───────────────────────┘ └───────────────────────────────────┘
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────────────────┐
│ Claude Code CLI │ │ MCP Server (stdio) │
│ Project root as CWD │ │ Provides send_message, │
│ │ │ send_document, confirmation │
└───────────────────────┘ └───────────────────────────────────┘
┌───────────────────────┐
│ MessageQueue │
│ Serial queue for │
│ all Telegram sends │
└───────────────────────┘
┌───────────────────────┐
│ Telegram Bot API │
└───────────────────────┘

Modules

FileResponsibility
src/index.tsBot commands, routing, inline keyboard handlers
src/bot.tsTelegraf instance, sendMessage, markdown→HTML conversion
src/config.tsZod env validation; exits on invalid config
src/sessionManager.tsLifecycle: create/send/end ClaudeSession per user
src/claudeSession.tsSpawns Claude CLI subprocess, parses stream-json events
src/repoManager.tsGit clone + FS CRUD for shared repos in repos/
src/relayServer.tsLocal HTTP server; proxies MCP tool calls to main process
src/messageQueue.tsSerial queue for all Telegram API calls
src/commandsConfig.tsLoads/validates commands.json custom slash commands
mcp-server/src/index.tsMCP tools: send_message, send_document, send_confirmation_request

Data storage

All state is stored on the filesystem. No database is used.

repos/
<repo-name>/ ← shared git clone (read-only by convention)
sessions/<chat_id>/
.active_repo ← tracks which shared repo is active for this user

Repositories are stored centrally in repos/ (configured via REPOS_DIR). Per-user state (active repo selection) is tracked under sessions/.

MCP relay pattern

Claude’s MCP server runs as a stdio subprocess. It cannot make outbound HTTP requests to the Telegram Bot API directly, so it proxies through RelayServer:

  1. MCP server receives a tool call (e.g., send_message)
  2. It POSTs to RelayServer at http://127.0.0.1:<port>/relay
  3. RelayServer forwards to MessageQueue.enqueue()
  4. MessageQueue sends to Telegram Bot API serially

Session lifecycle

  1. User sends /start or clicks a repo → SessionManager.startSession()
  2. ClaudeSession is created with cwd = project root and repo path injected via --append-system-prompt
  3. Each user message → ClaudeSession.send() spawns a one-shot Claude process
  4. Claude outputs stream-json events → handleJsonEvent() routes to onOutput
  5. Session ends on: /end, idle timeout, or process crash