claude-channels-telegram-mcp
The missing outbound layer for Claude Channels. Adds photos, files (PDFs, source code), keyboards, Telegram Stars, and TON tools to Claude Code — safely coexisting with Channels via CHANNELS_MODE (no polling conflicts).
What This Is
Claude Channels handles inbound Telegram messages and provides reply, react, and edit_message for outbound. That's it. Any richer response — a photo, a file, a button, a payment invoice — requires this toolkit.
Example: Claude Code coding session, results delivered to Telegram
| Claude does | Tool | |---|---| | Generates a chart or graph | send_photo → image in Telegram | | Edits a source file | send_document → file delivered to Telegram | | Produces a test result report (PDF) | send_document → PDF in Telegram | | Takes a screenshot during web testing | send_photo → screenshot in Telegram |
| Claude Channels built-in | This toolkit adds | |---|---| | reply — plain text response | send_keyboard — inline buttons (URL or callback) | | react — emoji reaction | send_photo, send_document — media | | edit_message — edit sent message | send_invoice — Telegram Stars payment | | (inbound handled automatically) | register_commands, set_bot_description — BotFather setup | | — | 16 TON blockchain tools incl. TON Connect |
Why not just use any Bot API MCP?
Other Bot API MCP servers (TONresistor/telegram-mcp, tsgram-mcp, ton-agent-kit) also provide outbound tools — but they all poll getUpdates continuously. Running them alongside Claude Channels causes offset conflicts: messages get dropped or delivered twice.
CHANNELS_MODE=true disables all polling in this toolkit. Channels owns the inbound; this toolkit handles the outbound. No conflicts.
Primary mode: CHANNELS_MODE
CHANNELS_MODE (CHANNELS_MODE=true) — the intended use case, alongside Claude Channels:
- Inbound: handled by Channels automatically
- Outbound: all 30 send/TON tools available
- Polling disabled — no offset conflicts with Channels
Standalone (no Channels) — also supported, without the Channels plugin:
- Inbound:
get_updates/get_pending_messagesavailable - All 33 tools available
MCP Tools — 30 in CHANNELS_MODE / 33 standalone
Telegram (17 tools)
| Tool | Description | |------|-------------| | send_message | Send plain text (HTML) messages; supports reply-to for threaded replies | | send_keyboard | Send messages with inline keyboard buttons (URL links or callbacks) | | register_commands | Register /commands in BotFather bot menu | | set_bot_description | Set bot profile description | | send_photo | Send photos (URL or local file) with caption | | send_document | Send documents/files with caption | | delete_message | Delete bot's own messages | | pin_message | Pin a message in chat | | get_chat_info | Get chat/user information | | send_invoice | Send Telegram Stars payment invoice | | send_chat_action | Show "typing…" / "sending photo…" status in chat | | get_file | Get download URL for a file from its file_id | | answer_callback_query | Respond to inline button presses with toast/alert; closes loading spinner | | answer_pre_checkout_query | Approve or reject a Telegram Stars payment (10 s deadline) | | get_updates | Fetch incoming messages/callbacks via polling (standalone mode only) | | get_pending_messages | Simplified view of unread messages (standalone mode only) | | mark_messages_read | Advance internal offset to mark updates as processed (standalone mode only) |
TON Blockchain (16 tools)
| Tool | Description | |------|-------------| | get_ton_balance | Get TON balance for any wallet address | | get_jetton_balances | Get all token (Jetton) holdings | | get_token_price | Real-time token price + 24h/7d change | | estimate_swap | DEX swap output estimation | | send_ton | Execute TON transfer from agent wallet (requires wallet config) | | generate_ton_payment_link | Generate ton:// + TonKeeper deep link — lets users pay from their own wallet | | get_transactions | Fetch recent TON/Jetton transaction history for any address | | transfer_jetton | Send Jetton tokens (e.g. USDT) from agent wallet (requires wallet config) | | get_nft_items | List NFT items owned by a wallet (name, image, collection) | | get_nft_collection | Get NFT collection metadata (name, description, item count, owner) | | resolve_ton_dns | Resolve a .ton or .t.me domain to a wallet address | | get_staking_info | List top liquid staking pools (APY, TVL, min stake) sorted by APY | | generate_tonconnect_link | Generate TonKeeper connection link (returns session_id + URL button) | | check_tonconnect_session | Poll session for wallet connection status and address | | request_ton_payment | Request TON payment from connected user wallet (user approves in TonKeeper) | | disconnect_wallet | Disconnect wallet and delete session |
Quick Start
git clone https://github.com/Masashi-Ono0611/claude-channels-telegram-mcp.git
cd claude-channels-telegram-mcp && bun install
Configure MCP
Add to your project's .mcp.json:
{
"mcpServers": {
"telegram-toolkit": {
"command": "bun",
"args": ["run", "/path/to/claude-channels-telegram-mcp/src/index.ts"],
"env": {
"TELEGRAM_BOT_TOKEN": "<your-bot-token>",
"TON_API_BASE_URL": "https://testnet.tonapi.io",
"CHANNELS_MODE": "true"
}
}
}
}
Operating Modes
| Mode | When to use | Tools | |------|-------------|-------| | CHANNELS_MODE (recommended) | With Claude Channels plugin | 30 tools (polling excluded) | | Standalone | Without Claude Channels | 33 tools (polling included) |
Set CHANNELS_MODE=true in your .mcp.json env (shown above — already included in the example).
Environment Variables
# Required
TELEGRAM_BOT_TOKEN=<bot-token-from-botfather>
CHANNELS_MODE=true # set to true when using alongside Claude Channels plugin
# TON network (auto-detects testnet/mainnet from URL)
TON_API_BASE_URL=https://testnet.tonapi.io # testnet (default)
TON_API_BASE_URL=https://tonapi.io # mainnet
TON_API_KEY=<optional-api-key> # increases rate limits
# Agent wallet (required for send_ton / transfer_jetton)
AGENT_MNEMONIC_PATH=/path/to/mnemonic.txt
TONCENTER_ENDPOINT=<override-if-needed> # auto-derived from TON_API_BASE_URL
TONCENTER_API_KEY=<optional-toncenter-key>
If TON env vars are not set, TON tools return "not configured" errors. Telegram tools work regardless.
Architecture
Claude Code
│
├── Claude Channels (built-in plugin)
│ └── reply, react, edit_message (inbound + outbound messaging)
│
└── claude-channels-telegram-mcp (this MCP server)
├── Telegram: send, keyboards, media, commands, invoice, callbacks, polling
└── TON: balance, price, swap, transfer, payment links, tx history, jetton, wallet connect
Key design: In CHANNELS_MODE, only send-only APIs are active — no polling conflicts with Channels. In standalone mode, get_updates + send_message + answer_callback_query provide a full receive-respond loop. TON read tools use tonapi.io; write tools use TonCenter RPC. Network (testnet/mainnet) is auto-detected from TON_API_BASE_URL.
Complete Response Pipeline (standalone)
User presses inline button → get_updates → callback_query received
→ answer_callback_query("Processing...") → send_invoice(Stars)
→ User pays → pre_checkout_query received
→ answer_pre_checkout_query(ok=true) → send_message("Payment complete!")
Payment Link Flow (CHANNELS_MODE or standalone)
generate_ton_payment_link + send_keyboard enables a user-pays flow without exposing the agent wallet:
Claude → generate_ton_payment_link → send_keyboard with URL button
User taps button → TonKeeper / any TON wallet opens → user signs & sends
Claude → get_transactions → verify payment received
TON Connect Flow (user wallet)
generate_tonconnect_link + send_keyboard connects a user's own wallet (TonKeeper) without exposing any agent private key:
Claude → generate_tonconnect_link → { session_id, tonkeeper_link }
Claude → send_keyboard with URL button (tonkeeper_link)
User taps → TonKeeper opens → user approves connection
Claude → check_tonconnect_session(session_id) → { status: "connected", address }
Claude → request_ton_payment(session_id, to, "0.5") → user approves in TonKeeper → { boc }
Claude → disconnect_wallet(session_id)
Session state is persisted to ~/.ctt/sessions/<session_id>.json, so connection survives across MCP calls.
CHANNELS_MODE: What Works and What Doesn't
In CHANNELS_MODE (CHANNELS_MODE=true), the three polling tools are disabled at both ListTools and CallTool level to prevent offset conflicts with the Channels plugin.
⚠️ reply vs send_message — not interchangeable
| | Channels reply | toolkit send_message | |---|---|---| | Responds in conversation thread | ✅ | ❌ (sends a new standalone message) | | Requires chat_id | ❌ (implicit) | ✅ (must provide) | | Available in CHANNELS_MODE | ✅ | ✅ |
Use Channels reply for all conversational responses. Use send_message only for proactive or scheduled sends (e.g. notifications).
⚠️ Callback buttons don't work in CHANNELS_MODE
Callback buttons (callback_data) require get_updates to receive the callback event back from Telegram. Since polling is disabled in CHANNELS_MODE, the callback data is never delivered to Claude.
| Button type | CHANNELS_MODE | Standalone | |---|---|---| | URL button (url field) — opens a link | ✅ works | ✅ works | | Callback button (callback_data) — returns data to bot | ❌ data never arrives | ✅ works |
In CHANNELS_MODE, use URL buttons for all interactive elements (payment links, TonKeeper connect, external pages).
Tested
| Tool | Result | |------|--------| | register_commands | ✅ 5 commands registered | | set_bot_description | ✅ Description set | | send_keyboard | ✅ URL buttons sent | | send_photo | ✅ URL image sent | | send_document | ✅ Local file sent | | delete_message | ✅ Message deleted | | pin_message | ✅ Message pinned | | get_chat_info | ✅ User info retrieved | | send_invoice | ✅ 1 Star invoice sent | | get_ton_balance | ✅ 4.76 TON | | get_token_price | ✅ $1.26 (+2.65%) | | get_jetton_balances | ✅ Jetton holdings listed | | estimate_swap | ✅ Swap estimate returned | | send_ton | ✅ TON transfer confirmed | | generate_ton_payment_link | ✅ ton:// + TonKeeper links generated | | get_transactions | ✅ Tx history fetched | | transfer_jetton | ✅ Jetton transfer confirmed | | send_chat_action | ✅ "typing…" status shown | | get_file | ✅ Download URL returned | | get_updates | ✅ Incoming messages fetched | | get_pending_messages | ✅ Unread messages listed | | mark_messages_read | ✅ Offset advanced | | send_message | ✅ Plain text + reply_to tested | | answer_callback_query | ✅ Toast (show_alert=false) tested | | answer_pre_checkout_query | ✅ Validation guard + 1 Star payment approved | | get_nft_items | ✅ count=0 (testnet wallet, API reachable) | | get_nft_collection | ✅ "Hipo Payout" collection fetched | | resolve_ton_dns | ✅ foundation.ton resolved | | get_staking_info | ✅ 5 pools returned | | generate_tonconnect_link | ✅ TonKeeper link generated | | check_tonconnect_session | ✅ connected + address (testnet, real device) | | request_ton_payment | ✅ 0.01 TON signed + BOC returned (testnet) | | disconnect_wallet | ✅ session disconnected + file deleted |
License
MIT











