telegram-claude-mcp 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +286 -0
  2. package/bin/setup.js +336 -0
  3. package/package.json +5 -3
package/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # telegram-claude-mcp
2
+
3
+ Get Telegram notifications from Claude Code with interactive permission buttons.
4
+
5
+ When Claude needs permission to run a command or access a file, you'll get a Telegram message with Allow/Deny buttons. When Claude finishes working, you can reply with more instructions to continue.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx telegram-claude-setup
11
+ ```
12
+
13
+ Then:
14
+ 1. Create a Telegram bot via [@BotFather](https://t.me/BotFather)
15
+ 2. Get your chat ID (see instructions below)
16
+ 3. Edit `~/.claude/settings.json` with your bot token and chat ID
17
+ 4. Restart Claude Code
18
+
19
+ ## Features
20
+
21
+ - **Permission buttons** - Allow/Deny tool usage from Telegram
22
+ - **Interactive stop** - Reply to continue Claude's work after it stops
23
+ - **Notifications** - Get notified about Claude events
24
+ - **Multi-session** - Run multiple Claude instances with message tagging
25
+
26
+ ## Manual Installation
27
+
28
+ If you prefer not to use npm, follow these steps:
29
+
30
+ ### 1. Create Hook Scripts
31
+
32
+ Create directory `~/.claude/hooks/` and add these scripts:
33
+
34
+ **~/.claude/hooks/permission-hook.sh**
35
+ ```bash
36
+ #!/bin/bash
37
+ SESSION_DIR="/tmp/telegram-claude-sessions"
38
+
39
+ find_active_session() {
40
+ local latest_file=""
41
+ local latest_time=0
42
+ [ -d "$SESSION_DIR" ] || return
43
+ for info_file in "$SESSION_DIR"/*.info; do
44
+ [ -e "$info_file" ] || continue
45
+ local pid
46
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
47
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
48
+ local file_time
49
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
50
+ if [ "$file_time" -gt "$latest_time" ]; then
51
+ latest_time=$file_time
52
+ latest_file=$info_file
53
+ fi
54
+ fi
55
+ done
56
+ echo "$latest_file"
57
+ }
58
+
59
+ INFO_FILE=$(find_active_session)
60
+ [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
61
+
62
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
63
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
64
+ HOOK_URL="http://${HOOK_HOST}:${HOOK_PORT}/permission"
65
+
66
+ INPUT=$(cat)
67
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .name // empty')
68
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // .arguments // {}')
69
+
70
+ PAYLOAD=$(jq -n --arg tool_name "$TOOL_NAME" --argjson tool_input "$TOOL_INPUT" \
71
+ '{tool_name: $tool_name, tool_input: $tool_input}')
72
+
73
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" -H "Content-Type: application/json" -d "$PAYLOAD" --max-time 600)
74
+ [ $? -eq 0 ] && echo "$RESPONSE"
75
+ ```
76
+
77
+ **~/.claude/hooks/stop-hook.sh**
78
+ ```bash
79
+ #!/bin/bash
80
+ SESSION_DIR="/tmp/telegram-claude-sessions"
81
+
82
+ find_active_session() {
83
+ local latest_file=""
84
+ local latest_time=0
85
+ [ -d "$SESSION_DIR" ] || return
86
+ for info_file in "$SESSION_DIR"/*.info; do
87
+ [ -e "$info_file" ] || continue
88
+ local pid
89
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
90
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
91
+ local file_time
92
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
93
+ if [ "$file_time" -gt "$latest_time" ]; then
94
+ latest_time=$file_time
95
+ latest_file=$info_file
96
+ fi
97
+ fi
98
+ done
99
+ echo "$latest_file"
100
+ }
101
+
102
+ INPUT=$(cat)
103
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
104
+ [ "$STOP_HOOK_ACTIVE" = "true" ] && exit 0
105
+
106
+ INFO_FILE=$(find_active_session)
107
+ [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
108
+
109
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
110
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
111
+ HOOK_URL="http://${HOOK_HOST}:${HOOK_PORT}/stop"
112
+
113
+ TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
114
+ PAYLOAD=$(jq -n --arg transcript_path "$TRANSCRIPT_PATH" '{transcript_path: $transcript_path}')
115
+
116
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" -H "Content-Type: application/json" -d "$PAYLOAD" --max-time 300)
117
+ if [ $? -eq 0 ]; then
118
+ DECISION=$(echo "$RESPONSE" | jq -r '.decision // empty')
119
+ [ "$DECISION" = "block" ] && echo "$RESPONSE"
120
+ fi
121
+ ```
122
+
123
+ **~/.claude/hooks/notify-hook.sh**
124
+ ```bash
125
+ #!/bin/bash
126
+ SESSION_DIR="/tmp/telegram-claude-sessions"
127
+
128
+ find_active_session() {
129
+ local latest_file=""
130
+ local latest_time=0
131
+ [ -d "$SESSION_DIR" ] || return
132
+ for info_file in "$SESSION_DIR"/*.info; do
133
+ [ -e "$info_file" ] || continue
134
+ local pid
135
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
136
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
137
+ local file_time
138
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
139
+ if [ "$file_time" -gt "$latest_time" ]; then
140
+ latest_time=$file_time
141
+ latest_file=$info_file
142
+ fi
143
+ fi
144
+ done
145
+ echo "$latest_file"
146
+ }
147
+
148
+ INFO_FILE=$(find_active_session)
149
+ [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
150
+
151
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
152
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
153
+ HOOK_URL="http://${HOOK_HOST}:${HOOK_PORT}/notify"
154
+
155
+ INPUT=$(cat)
156
+ MESSAGE=$(echo "$INPUT" | jq -r '.message // "Notification"')
157
+ PAYLOAD=$(jq -n --arg type "notification" --arg message "$MESSAGE" '{type: $type, message: $message}')
158
+
159
+ curl -s -X POST "$HOOK_URL" -H "Content-Type: application/json" -d "$PAYLOAD" --max-time 10 >/dev/null 2>&1
160
+ ```
161
+
162
+ Make them executable:
163
+ ```bash
164
+ chmod +x ~/.claude/hooks/*.sh
165
+ ```
166
+
167
+ ### 2. Configure Claude Settings
168
+
169
+ Add to `~/.claude/settings.json`:
170
+
171
+ ```json
172
+ {
173
+ "hooks": {
174
+ "PermissionRequest": [
175
+ {
176
+ "matcher": "*",
177
+ "hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-hook.sh" }]
178
+ }
179
+ ],
180
+ "Stop": [
181
+ {
182
+ "matcher": "*",
183
+ "hooks": [{ "type": "command", "command": "~/.claude/hooks/stop-hook.sh" }]
184
+ }
185
+ ],
186
+ "Notification": [
187
+ {
188
+ "matcher": "*",
189
+ "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify-hook.sh" }]
190
+ }
191
+ ]
192
+ },
193
+ "mcpServers": {
194
+ "telegram": {
195
+ "command": "npx",
196
+ "args": ["-y", "telegram-claude-mcp"],
197
+ "env": {
198
+ "TELEGRAM_BOT_TOKEN": "YOUR_BOT_TOKEN",
199
+ "TELEGRAM_CHAT_ID": "YOUR_CHAT_ID",
200
+ "SESSION_NAME": "default"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ ```
206
+
207
+ ### 3. Create Telegram Bot
208
+
209
+ 1. Open Telegram and message [@BotFather](https://t.me/BotFather)
210
+ 2. Send `/newbot` and follow prompts
211
+ 3. Copy the bot token
212
+
213
+ ### 4. Get Your Chat ID
214
+
215
+ 1. Start a chat with your new bot
216
+ 2. Send any message to it
217
+ 3. Visit: `https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates`
218
+ 4. Find `"chat":{"id": YOUR_CHAT_ID}` in the response
219
+
220
+ ### 5. Update Settings
221
+
222
+ Replace `YOUR_BOT_TOKEN` and `YOUR_CHAT_ID` in `~/.claude/settings.json`
223
+
224
+ ### 6. Restart Claude Code
225
+
226
+ ## Environment Variables
227
+
228
+ | Variable | Description | Required |
229
+ |----------|-------------|----------|
230
+ | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes |
231
+ | `TELEGRAM_CHAT_ID` | Your Telegram chat ID | Yes |
232
+ | `SESSION_NAME` | Session identifier (for multi-instance) | No (default: "default") |
233
+ | `SESSION_PORT` | Preferred HTTP port | No (default: 3333, auto-finds available) |
234
+
235
+ ## How It Works
236
+
237
+ ```
238
+ Claude Code Hook Scripts telegram-claude-mcp
239
+ | | |
240
+ |-- Permission needed -------->| |
241
+ | |-- POST /permission --------->|
242
+ | | |-- Send to Telegram
243
+ | | |<- User clicks Allow
244
+ | |<-------- {allow} ------------|
245
+ |<-------- Allow --------------| |
246
+ | | |
247
+ |-- Work complete, stops ----->| |
248
+ | |-- POST /stop --------------->|
249
+ | | |-- Send to Telegram
250
+ | | |<- User replies
251
+ | |<-- {block, "do X next"} -----|
252
+ |<-- Continue with "do X" -----| |
253
+ ```
254
+
255
+ ## Multiple Sessions
256
+
257
+ Run multiple Claude instances with different session names:
258
+
259
+ ```json
260
+ {
261
+ "env": {
262
+ "SESSION_NAME": "project-a"
263
+ }
264
+ }
265
+ ```
266
+
267
+ Messages are tagged with `[project-a]` so you know which instance sent them.
268
+
269
+ ## Troubleshooting
270
+
271
+ **Buttons appear but don't work:**
272
+ - Check for stale MCP processes: `ps aux | grep telegram-claude`
273
+ - Kill old processes and restart Claude
274
+
275
+ **No messages in Telegram:**
276
+ - Verify bot token and chat ID are correct
277
+ - Ensure you've started a chat with your bot
278
+ - Check Claude's MCP server logs
279
+
280
+ **Permission hook not firing:**
281
+ - Verify hook scripts are executable: `ls -la ~/.claude/hooks/`
282
+ - Check Claude settings have hooks configured
283
+
284
+ ## License
285
+
286
+ MIT
package/bin/setup.js ADDED
@@ -0,0 +1,336 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Setup script for telegram-claude-mcp hooks integration
5
+ *
6
+ * Usage: npx telegram-claude-mcp setup
7
+ *
8
+ * This will:
9
+ * 1. Copy hook scripts to ~/.claude/hooks/
10
+ * 2. Update Claude settings with hooks configuration
11
+ * 3. Display instructions for completing setup
12
+ */
13
+
14
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, chmodSync } from 'fs';
15
+ import { join, dirname } from 'path';
16
+ import { homedir } from 'os';
17
+ import { fileURLToPath } from 'url';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+
21
+ const CLAUDE_DIR = join(homedir(), '.claude');
22
+ const HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
23
+ const SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
24
+
25
+ // Hook scripts content
26
+ const PERMISSION_HOOK = `#!/bin/bash
27
+ #
28
+ # Claude Code Permission Hook - forwards permission requests to Telegram
29
+ #
30
+
31
+ SESSION_DIR="/tmp/telegram-claude-sessions"
32
+
33
+ find_active_session() {
34
+ local latest_file=""
35
+ local latest_time=0
36
+
37
+ [ -d "$SESSION_DIR" ] || return
38
+
39
+ for info_file in "$SESSION_DIR"/*.info; do
40
+ [ -e "$info_file" ] || continue
41
+
42
+ local pid
43
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
44
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
45
+ local file_time
46
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
47
+ if [ "$file_time" -gt "$latest_time" ]; then
48
+ latest_time=$file_time
49
+ latest_file=$info_file
50
+ fi
51
+ fi
52
+ done
53
+
54
+ echo "$latest_file"
55
+ }
56
+
57
+ INFO_FILE=$(find_active_session)
58
+
59
+ if [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ]; then
60
+ exit 0
61
+ fi
62
+
63
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
64
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
65
+ HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/permission"
66
+
67
+ INPUT=$(cat)
68
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
69
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
70
+
71
+ if [ -z "$TOOL_NAME" ]; then
72
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // .name // empty')
73
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.toolInput // .input // .arguments // {}')
74
+ fi
75
+
76
+ PAYLOAD=$(jq -n \\
77
+ --arg tool_name "$TOOL_NAME" \\
78
+ --argjson tool_input "$TOOL_INPUT" \\
79
+ '{tool_name: $tool_name, tool_input: $tool_input}')
80
+
81
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
82
+ -H "Content-Type: application/json" \\
83
+ -d "$PAYLOAD" \\
84
+ --max-time 600)
85
+
86
+ [ $? -eq 0 ] && echo "$RESPONSE"
87
+ `;
88
+
89
+ const STOP_HOOK = `#!/bin/bash
90
+ #
91
+ # Claude Code Interactive Stop Hook - sends stop notification and waits for reply
92
+ #
93
+
94
+ SESSION_DIR="/tmp/telegram-claude-sessions"
95
+
96
+ find_active_session() {
97
+ local latest_file=""
98
+ local latest_time=0
99
+
100
+ [ -d "$SESSION_DIR" ] || return
101
+
102
+ for info_file in "$SESSION_DIR"/*.info; do
103
+ [ -e "$info_file" ] || continue
104
+
105
+ local pid
106
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
107
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
108
+ local file_time
109
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
110
+ if [ "$file_time" -gt "$latest_time" ]; then
111
+ latest_time=$file_time
112
+ latest_file=$info_file
113
+ fi
114
+ fi
115
+ done
116
+
117
+ echo "$latest_file"
118
+ }
119
+
120
+ INPUT=$(cat)
121
+
122
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
123
+ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
124
+ exit 0
125
+ fi
126
+
127
+ INFO_FILE=$(find_active_session)
128
+
129
+ if [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ]; then
130
+ exit 0
131
+ fi
132
+
133
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
134
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
135
+ HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/stop"
136
+
137
+ TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
138
+
139
+ PAYLOAD=$(jq -n \\
140
+ --arg transcript_path "$TRANSCRIPT_PATH" \\
141
+ '{transcript_path: $transcript_path}')
142
+
143
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
144
+ -H "Content-Type: application/json" \\
145
+ -d "$PAYLOAD" \\
146
+ --max-time 300)
147
+
148
+ if [ $? -eq 0 ]; then
149
+ DECISION=$(echo "$RESPONSE" | jq -r '.decision // empty')
150
+ REASON=$(echo "$RESPONSE" | jq -r '.reason // empty')
151
+
152
+ if [ "$DECISION" = "block" ] && [ -n "$REASON" ]; then
153
+ echo "$RESPONSE"
154
+ fi
155
+ fi
156
+ `;
157
+
158
+ const NOTIFY_HOOK = `#!/bin/bash
159
+ #
160
+ # Claude Code Notification Hook - sends notifications to Telegram
161
+ #
162
+
163
+ SESSION_DIR="/tmp/telegram-claude-sessions"
164
+
165
+ find_active_session() {
166
+ local latest_file=""
167
+ local latest_time=0
168
+
169
+ [ -d "$SESSION_DIR" ] || return
170
+
171
+ for info_file in "$SESSION_DIR"/*.info; do
172
+ [ -e "$info_file" ] || continue
173
+
174
+ local pid
175
+ pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
176
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
177
+ local file_time
178
+ file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
179
+ if [ "$file_time" -gt "$latest_time" ]; then
180
+ latest_time=$file_time
181
+ latest_file=$info_file
182
+ fi
183
+ fi
184
+ done
185
+
186
+ echo "$latest_file"
187
+ }
188
+
189
+ INFO_FILE=$(find_active_session)
190
+
191
+ if [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ]; then
192
+ exit 0
193
+ fi
194
+
195
+ HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
196
+ HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
197
+ HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/notify"
198
+
199
+ INPUT=$(cat)
200
+ MESSAGE=$(echo "$INPUT" | jq -r '.message // "Notification from Claude"')
201
+
202
+ PAYLOAD=$(jq -n \\
203
+ --arg type "notification" \\
204
+ --arg message "$MESSAGE" \\
205
+ '{type: $type, message: $message}')
206
+
207
+ curl -s -X POST "$HOOK_URL" \\
208
+ -H "Content-Type: application/json" \\
209
+ -d "$PAYLOAD" \\
210
+ --max-time 10 > /dev/null 2>&1
211
+ `;
212
+
213
+ // Hooks configuration for Claude settings
214
+ const HOOKS_CONFIG = {
215
+ PermissionRequest: [
216
+ {
217
+ matcher: '*',
218
+ hooks: [
219
+ {
220
+ type: 'command',
221
+ command: '~/.claude/hooks/permission-hook.sh'
222
+ }
223
+ ]
224
+ }
225
+ ],
226
+ Stop: [
227
+ {
228
+ matcher: '*',
229
+ hooks: [
230
+ {
231
+ type: 'command',
232
+ command: '~/.claude/hooks/stop-hook.sh'
233
+ }
234
+ ]
235
+ }
236
+ ],
237
+ Notification: [
238
+ {
239
+ matcher: '*',
240
+ hooks: [
241
+ {
242
+ type: 'command',
243
+ command: '~/.claude/hooks/notify-hook.sh'
244
+ }
245
+ ]
246
+ }
247
+ ]
248
+ };
249
+
250
+ function setup() {
251
+ console.log('\nšŸ“± telegram-claude-mcp Setup\n');
252
+ console.log('This will configure Telegram notifications for Claude Code.\n');
253
+
254
+ // Create hooks directory
255
+ if (!existsSync(HOOKS_DIR)) {
256
+ mkdirSync(HOOKS_DIR, { recursive: true });
257
+ console.log('āœ“ Created ~/.claude/hooks/');
258
+ }
259
+
260
+ // Write hook scripts
261
+ const permissionHookPath = join(HOOKS_DIR, 'permission-hook.sh');
262
+ writeFileSync(permissionHookPath, PERMISSION_HOOK);
263
+ chmodSync(permissionHookPath, 0o755);
264
+ console.log('āœ“ Installed permission-hook.sh');
265
+
266
+ const stopHookPath = join(HOOKS_DIR, 'stop-hook.sh');
267
+ writeFileSync(stopHookPath, STOP_HOOK);
268
+ chmodSync(stopHookPath, 0o755);
269
+ console.log('āœ“ Installed stop-hook.sh');
270
+
271
+ const notifyHookPath = join(HOOKS_DIR, 'notify-hook.sh');
272
+ writeFileSync(notifyHookPath, NOTIFY_HOOK);
273
+ chmodSync(notifyHookPath, 0o755);
274
+ console.log('āœ“ Installed notify-hook.sh');
275
+
276
+ // Update Claude settings
277
+ let settings = {};
278
+ if (existsSync(SETTINGS_FILE)) {
279
+ try {
280
+ settings = JSON.parse(readFileSync(SETTINGS_FILE, 'utf-8'));
281
+ } catch {
282
+ console.log('⚠ Could not parse existing settings.json, creating new one');
283
+ }
284
+ }
285
+
286
+ // Merge hooks config
287
+ settings.hooks = { ...settings.hooks, ...HOOKS_CONFIG };
288
+
289
+ // Add MCP server config if not present
290
+ if (!settings.mcpServers) {
291
+ settings.mcpServers = {};
292
+ }
293
+
294
+ if (!settings.mcpServers.telegram) {
295
+ settings.mcpServers.telegram = {
296
+ command: 'npx',
297
+ args: ['-y', 'telegram-claude-mcp'],
298
+ env: {
299
+ TELEGRAM_BOT_TOKEN: 'YOUR_BOT_TOKEN',
300
+ TELEGRAM_CHAT_ID: 'YOUR_CHAT_ID',
301
+ SESSION_NAME: 'default'
302
+ }
303
+ };
304
+ console.log('āœ“ Added telegram MCP server config (needs bot token)');
305
+ }
306
+
307
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
308
+ console.log('āœ“ Updated ~/.claude/settings.json with hooks');
309
+
310
+ // Print next steps
311
+ console.log('\n' + '─'.repeat(50));
312
+ console.log('\nšŸ“‹ Next Steps:\n');
313
+
314
+ console.log('1. Create a Telegram bot:');
315
+ console.log(' - Open Telegram and message @BotFather');
316
+ console.log(' - Send /newbot and follow the prompts');
317
+ console.log(' - Copy the bot token\n');
318
+
319
+ console.log('2. Get your Chat ID:');
320
+ console.log(' - Start a chat with your new bot');
321
+ console.log(' - Send any message');
322
+ console.log(' - Visit: https://api.telegram.org/bot<TOKEN>/getUpdates');
323
+ console.log(' - Find "chat":{"id": YOUR_CHAT_ID}\n');
324
+
325
+ console.log('3. Update ~/.claude/settings.json:');
326
+ console.log(' - Set TELEGRAM_BOT_TOKEN to your bot token');
327
+ console.log(' - Set TELEGRAM_CHAT_ID to your chat ID\n');
328
+
329
+ console.log('4. Restart Claude Code\n');
330
+
331
+ console.log('─'.repeat(50));
332
+ console.log('\n✨ Setup complete! Configure your bot token and chat ID to finish.\n');
333
+ }
334
+
335
+ // Run setup
336
+ setup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telegram-claude-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "MCP server that lets Claude message you on Telegram with hooks support",
5
5
  "author": "Geravant",
6
6
  "license": "MIT",
@@ -21,12 +21,14 @@
21
21
  "type": "module",
22
22
  "main": "src/index.ts",
23
23
  "bin": {
24
- "telegram-claude-mcp": "./bin/run.js"
24
+ "telegram-claude-mcp": "./bin/run.js",
25
+ "telegram-claude-setup": "./bin/setup.js"
25
26
  },
26
27
  "files": [
27
28
  "src",
28
29
  "bin",
29
- "hooks"
30
+ "hooks",
31
+ "README.md"
30
32
  ],
31
33
  "scripts": {
32
34
  "start": "node --import tsx src/index.ts",