claude-channels-telegram-mcp
README.md
# 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_messages` available
- 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
```bash
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`:
```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
```bash
# 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
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues