@deadragdoll/tellymcp 0.0.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 (124) hide show
  1. package/.env.example.client +93 -0
  2. package/.env.example.gateway +120 -0
  3. package/CHANGELOG.md +155 -0
  4. package/LICENSE +21 -0
  5. package/README-ru.md +338 -0
  6. package/README.md +1262 -0
  7. package/STANDALONE-ru.md +266 -0
  8. package/STANDALONE.md +266 -0
  9. package/TOOLS.md +1296 -0
  10. package/config/templates/env.both.template +83 -0
  11. package/config/templates/env.client.template +60 -0
  12. package/config/templates/env.gateway.template +82 -0
  13. package/dist/cli.js +636 -0
  14. package/dist/index.js +17 -0
  15. package/dist/lib/logfeed/store.js +52 -0
  16. package/dist/lib/middlewares/tracer.js +172 -0
  17. package/dist/lib/mixins/db.js +267 -0
  18. package/dist/lib/mixins/logfeed.js +34 -0
  19. package/dist/lib/mixins/session.errors.js +142 -0
  20. package/dist/lib/moleculer.js +2 -0
  21. package/dist/lib/trace.js +147 -0
  22. package/dist/lib/traceContext.js +116 -0
  23. package/dist/moleculer.config.js +274 -0
  24. package/dist/services/features/telegram-mcp/approval.service.js +33 -0
  25. package/dist/services/features/telegram-mcp/browser.service.js +42 -0
  26. package/dist/services/features/telegram-mcp/collaboration.service.js +53 -0
  27. package/dist/services/features/telegram-mcp/ensuredb.service.js +337 -0
  28. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +378 -0
  29. package/dist/services/features/telegram-mcp/gateway-loopback.js +10 -0
  30. package/dist/services/features/telegram-mcp/gateway-rmq.service.js +294 -0
  31. package/dist/services/features/telegram-mcp/gateway-socket.service.js +1463 -0
  32. package/dist/services/features/telegram-mcp/gateway.service.js +1141 -0
  33. package/dist/services/features/telegram-mcp/inbox.service.js +33 -0
  34. package/dist/services/features/telegram-mcp/mcp-http.service.js +76 -0
  35. package/dist/services/features/telegram-mcp/mcp-server.service.js +127 -0
  36. package/dist/services/features/telegram-mcp/notify.service.js +33 -0
  37. package/dist/services/features/telegram-mcp/pair.service.js +33 -0
  38. package/dist/services/features/telegram-mcp/runtime.service.js +36 -0
  39. package/dist/services/features/telegram-mcp/session-context.service.js +33 -0
  40. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +103 -0
  41. package/dist/services/features/telegram-mcp/src/app/config/env.js +317 -0
  42. package/dist/services/features/telegram-mcp/src/app/http.js +774 -0
  43. package/dist/services/features/telegram-mcp/src/app/index.js +2 -0
  44. package/dist/services/features/telegram-mcp/src/app/providers/mcp/server.js +13 -0
  45. package/dist/services/features/telegram-mcp/src/app/providers/redis/client.js +18 -0
  46. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +740 -0
  47. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +267 -0
  48. package/dist/services/features/telegram-mcp/src/app/webapp/relay.js +69 -0
  49. package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +9 -0
  50. package/dist/services/features/telegram-mcp/src/entities/auth/model/types.js +2 -0
  51. package/dist/services/features/telegram-mcp/src/entities/browser/model/types.js +2 -0
  52. package/dist/services/features/telegram-mcp/src/entities/collaboration/model/types.js +2 -0
  53. package/dist/services/features/telegram-mcp/src/entities/inbox/model/types.js +2 -0
  54. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +545 -0
  55. package/dist/services/features/telegram-mcp/src/entities/request/model/types.js +2 -0
  56. package/dist/services/features/telegram-mcp/src/entities/session/model/types.js +2 -0
  57. package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +33 -0
  58. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClearLogsTool.js +28 -0
  59. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +28 -0
  60. package/dist/services/features/telegram-mcp/src/features/browser/model/browserCloseTool.js +28 -0
  61. package/dist/services/features/telegram-mcp/src/features/browser/model/browserComputedStyleTool.js +28 -0
  62. package/dist/services/features/telegram-mcp/src/features/browser/model/browserConsoleTool.js +28 -0
  63. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +28 -0
  64. package/dist/services/features/telegram-mcp/src/features/browser/model/browserErrorsTool.js +28 -0
  65. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +28 -0
  66. package/dist/services/features/telegram-mcp/src/features/browser/model/browserNetworkFailuresTool.js +28 -0
  67. package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +33 -0
  68. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +28 -0
  69. package/dist/services/features/telegram-mcp/src/features/browser/model/browserReloadTool.js +28 -0
  70. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +28 -0
  71. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +689 -0
  72. package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForTool.js +28 -0
  73. package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForUrlTool.js +28 -0
  74. package/dist/services/features/telegram-mcp/src/features/collaboration/model/backend.js +2 -0
  75. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +26 -0
  76. package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +390 -0
  77. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +102 -0
  78. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +33 -0
  79. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +33 -0
  80. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +69 -0
  81. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +657 -0
  82. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayReplyResolution.js +17 -0
  83. package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +33 -0
  84. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +33 -0
  85. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +33 -0
  86. package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +77 -0
  87. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +93 -0
  88. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +33 -0
  89. package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +33 -0
  90. package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +33 -0
  91. package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +202 -0
  92. package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +33 -0
  93. package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +33 -0
  94. package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +33 -0
  95. package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +33 -0
  96. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +409 -0
  97. package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +33 -0
  98. package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +33 -0
  99. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +123 -0
  100. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +33 -0
  101. package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +243 -0
  102. package/dist/services/features/telegram-mcp/src/shared/api/storage/contract.js +2 -0
  103. package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/registry.js +8 -0
  104. package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/types.js +2 -0
  105. package/dist/services/features/telegram-mcp/src/shared/api/transport/contract.js +2 -0
  106. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +86 -0
  107. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +436 -0
  108. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabSemantics.js +21 -0
  109. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabUi.js +87 -0
  110. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/messageFormat.js +60 -0
  111. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +46 -0
  112. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +6534 -0
  113. package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +280 -0
  114. package/dist/services/features/telegram-mcp/src/shared/lib/ids/ids.js +34 -0
  115. package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +68 -0
  116. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +223 -0
  117. package/dist/services/features/telegram-mcp/src/shared/lib/redact-secrets/redactSecrets.js +22 -0
  118. package/dist/services/features/telegram-mcp/src/shared/lib/truncate/truncate.js +12 -0
  119. package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +124 -0
  120. package/dist/services/features/telegram-mcp/src/shared/types/common.js +2 -0
  121. package/dist/services/features/telegram-mcp/standalone-http.service.js +113 -0
  122. package/dist/services/features/telegram-mcp/tools-sync.service.js +33 -0
  123. package/package.json +110 -0
  124. package/scripts/postinstall.js +60 -0
package/README.md ADDED
@@ -0,0 +1,1262 @@
1
+ # TellyMCP
2
+
3
+ [English](README.md) | [Русский](README-ru.md)
4
+
5
+ [![npm version](https://img.shields.io/npm/v/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
6
+ [![npm downloads](https://img.shields.io/npm/dm/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
7
+ [![node >= 24](https://img.shields.io/badge/node-%3E%3D24-339933)](https://nodejs.org/)
8
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ TellyMCP is a Telegram Human-in-the-Loop MCP server for coding agents.
11
+
12
+ It lets an agent:
13
+
14
+ - ask a human for clarification through Telegram
15
+ - receive unsolicited Telegram messages later through an inbox
16
+ - pair multiple agent sessions
17
+ - collaborate across local and remote sessions
18
+ - open a live tmux view inside Telegram Mini App
19
+ - exchange notes, screenshots, and files through `.mcp-xchange`
20
+
21
+ Current tools:
22
+
23
+ - `create_session_pair_code`
24
+ - `clear_session_pairing`
25
+ - `set_session_context`
26
+ - `set_tmux_target`
27
+ - `get_tmux_target`
28
+ - `get_session_context`
29
+ - `clear_session_context`
30
+ - `rename_session`
31
+ - `notify_telegram`
32
+ - `get_telegram_inbox_count`
33
+ - `get_telegram_inbox`
34
+ - `delete_telegram_inbox_message`
35
+ - `ask_user_telegram`
36
+ - `browser_open`
37
+ - `browser_reload`
38
+ - `browser_click`
39
+ - `browser_fill`
40
+ - `browser_press`
41
+ - `browser_wait_for`
42
+ - `browser_wait_for_url`
43
+ - `browser_console`
44
+ - `browser_errors`
45
+ - `browser_network_failures`
46
+ - `browser_clear_logs`
47
+ - `browser_dom`
48
+ - `browser_computed_style`
49
+ - `browser_screenshot`
50
+ - `browser_close`
51
+ - `refresh_tools_markdown`
52
+ - `send_partner_note`
53
+ - `send_partner_file`
54
+
55
+ ## Prerequisites
56
+
57
+ - Node.js 24+
58
+ - `tmux`
59
+ - Redis
60
+ - a Telegram bot token from BotFather
61
+ - for `gateway` / `both`: Postgres
62
+ - optional for durable fanout on gateway: RabbitMQ
63
+
64
+ ## tmux is strongly recommended
65
+
66
+ TellyMCP works best when the agent itself runs inside `tmux`.
67
+
68
+ Without `tmux`, the service can still run, but you lose the full interactive path:
69
+
70
+ - no Live View
71
+ - no tmux nudges
72
+ - no direct tmux control from Telegram Mini App
73
+
74
+ Typical start:
75
+
76
+ ```bash
77
+ tmux new -s backend
78
+ ```
79
+
80
+ or attach later:
81
+
82
+ ```bash
83
+ tmux attach -t backend
84
+ ```
85
+
86
+ Why the tmux session name matters:
87
+
88
+ - it helps you distinguish running agents
89
+ - it appears in tmux-related UI and diagnostics
90
+ - it makes Telegram session switching and Live targeting easier to understand
91
+
92
+ Use short, meaningful names such as:
93
+
94
+ - `backend`
95
+ - `frontend`
96
+ - `review`
97
+ - `ops`
98
+
99
+ If you run multiple agents, put each one in its own tmux session or pane and pair them separately.
100
+
101
+ ## Quick start
102
+
103
+ ### Standalone client node
104
+
105
+ This is the simplest setup. No shared gateway, no Postgres, no RabbitMQ.
106
+
107
+ 1. Install:
108
+
109
+ ```bash
110
+ npm install -g @deadragdoll/tellymcp
111
+ ```
112
+
113
+ 2. Create a client config:
114
+
115
+ ```bash
116
+ tellymcp init client
117
+ ```
118
+
119
+ 3. Edit the generated `.env` and set at minimum:
120
+
121
+ - `TELEGRAM_BOT_TOKEN`
122
+ - `TELEGRAM_BOT_USERNAME`
123
+ - `REDIS_HOST`
124
+ - `MCP_HTTP_BEARER_TOKEN`
125
+
126
+ 4. Validate the setup:
127
+
128
+ ```bash
129
+ tellymcp doctor --env .env
130
+ ```
131
+
132
+ 5. Run the node:
133
+
134
+ ```bash
135
+ tellymcp run --env .env
136
+ ```
137
+
138
+ 6. Add MCP to your agent:
139
+
140
+ ```bash
141
+ tellymcp mcp --help
142
+ ```
143
+
144
+ Typical local MCP endpoint in `client` mode:
145
+
146
+ - `http://127.0.0.1:8787/mcp`
147
+
148
+ Detailed step-by-step guide:
149
+
150
+ - [STANDALONE.md](STANDALONE.md)
151
+ - [STANDALONE-ru.md](STANDALONE-ru.md)
152
+
153
+ ### Shared gateway or combined `both` node
154
+
155
+ Use this when you want:
156
+
157
+ - cross-machine collaboration
158
+ - cross-bot projects
159
+ - gateway-relayed Live View
160
+ - persistent gateway-side project and delivery state
161
+
162
+ 1. Create a gateway or combined config:
163
+
164
+ ```bash
165
+ tellymcp init gateway
166
+ ```
167
+
168
+ or
169
+
170
+ ```bash
171
+ tellymcp init both
172
+ ```
173
+
174
+ 2. Edit `.env` and configure:
175
+
176
+ - `DISTRIBUTED_MODE=gateway|both`
177
+ - `PORT`
178
+ - `ROOT_PREFIX=/api`
179
+ - `TELEGRAM_BOT_TOKEN`
180
+ - `REDIS_*`
181
+ - `DB_*`
182
+ - `WEBAPP_PUBLIC_URL`
183
+ - `GATEWAY_PUBLIC_URL`
184
+ - `GATEWAY_WS_URL`
185
+ - optional `RMQ_*`
186
+
187
+ 3. Put the node behind nginx or another reverse proxy on the same prefix:
188
+
189
+ - `/api/mcp`
190
+ - `/api/webapp`
191
+ - `/api/gateway`
192
+ - `/api/healthz`
193
+
194
+ 4. Validate the setup:
195
+
196
+ ```bash
197
+ tellymcp doctor --env .env
198
+ ```
199
+
200
+ 5. Run it:
201
+
202
+ ```bash
203
+ tellymcp run --env .env
204
+ ```
205
+
206
+ Typical public MCP endpoint in `gateway` / `both` mode:
207
+
208
+ - `https://your-host.example/api/mcp`
209
+
210
+ ## Start with the bot from inside an agent
211
+
212
+ Once MCP is connected, you can start Telegram pairing by asking the agent in plain language.
213
+
214
+ Typical phrases the agent should understand:
215
+
216
+ - `pair with Telegram`
217
+ - `link to Telegram`
218
+ - `connect this session to Telegram`
219
+ - `register this session in Telegram`
220
+ - `create a Telegram pairing code`
221
+ - `bind this agent to Telegram`
222
+
223
+ Expected pairing flow:
224
+
225
+ 1. The agent calls `create_session_pair_code`.
226
+ 2. It returns a short code and, when possible, a deep link.
227
+ 3. You open Telegram and send `/start <code>` or `/link <code>` to the bot.
228
+ 4. After successful pairing, `/menu` opens the session menu.
229
+
230
+ Recommended prompt if you want to be explicit:
231
+
232
+ ```text
233
+ Pair this session with Telegram and give me the link code.
234
+ ```
235
+
236
+ If the agent works inside `tmux`, it should also pass tmux attributes and `cwd` during pairing so Live View and nudges work immediately.
237
+
238
+ ### Telegram setup
239
+
240
+ 1. Open BotFather in Telegram.
241
+ 2. Create a bot with `/newbot`.
242
+ 3. Save the bot token.
243
+ 4. Set `TELEGRAM_BOT_USERNAME` if you want deep-link pairing hints.
244
+
245
+ ## MCP configuration helper
246
+
247
+ TellyMCP does not modify your agent config automatically.
248
+
249
+ Use:
250
+
251
+ ```bash
252
+ tellymcp mcp --help
253
+ ```
254
+
255
+ This prints ready-to-paste MCP JSON snippets for:
256
+
257
+ - local standalone client
258
+ - shared gateway endpoint
259
+ - optional bearer token usage
260
+
261
+ ## Doctor
262
+
263
+ `doctor` is mode-aware.
264
+
265
+ `client` checks:
266
+
267
+ - `tmux`
268
+ - `.env`
269
+ - Redis
270
+ - local MCP bind
271
+ - external gateway `healthz` when `GATEWAY_PUBLIC_URL` is configured
272
+ - `GATEWAY_WS_URL`
273
+ - `WEBAPP_PUBLIC_URL`
274
+
275
+ `gateway` / `both` checks:
276
+
277
+ - `tmux`
278
+ - `.env`
279
+ - Redis
280
+ - local `healthz`
281
+ - public `healthz`
282
+ - public `ws`
283
+ - public `webapp`
284
+ - Postgres
285
+ - RabbitMQ when `RMQ_*` is configured
286
+
287
+ ## Important configuration
288
+
289
+ Common:
290
+
291
+ - `TELEGRAM_BOT_TOKEN`
292
+ - `TELEGRAM_BOT_USERNAME`
293
+ - `REDIS_HOST`
294
+ - `REDIS_PORT`
295
+ - `REDIS_DB`
296
+ - `MODE=queue|reject`
297
+ - `PAIR_CODE_TTL_SECONDS`
298
+ - `MCP_HTTP_HOST`
299
+ - `MCP_HTTP_PORT`
300
+ - `MCP_HTTP_PATH`
301
+ - `MCP_HTTP_BEARER_TOKEN`
302
+ - `TMUX_SOCKET_PATH`
303
+ - `TMUX_NUDGE_ENABLED`
304
+ - `TMUX_NUDGE_DEBOUNCE_SECONDS`
305
+ - `TMUX_NUDGE_COOLDOWN_SECONDS`
306
+ - `WEBAPP_ENABLED`
307
+ - `WEBAPP_BASE_PATH`
308
+ - `WEBAPP_LAUNCH_MODE=default|expand|fullscreen`
309
+ - `MCP_XCHANGE_DIR`
310
+ - `PROXY_USE=http|socks5`
311
+ - `HTTP_PROXY`
312
+ - `SOCKS5_PROXY`
313
+
314
+ Client-only:
315
+
316
+ - `DISTRIBUTED_MODE=client`
317
+ - `GATEWAY_PUBLIC_URL` optional
318
+ - `GATEWAY_WS_URL` optional
319
+ - `GATEWAY_WS_PATH`
320
+ - `GATEWAY_AUTH_TOKEN` optional
321
+
322
+ Gateway / both:
323
+
324
+ - `DISTRIBUTED_MODE=gateway|both`
325
+ - `PORT`
326
+ - `ROOT_PREFIX=/api`
327
+ - `DB_HOST`
328
+ - `DB_PORT`
329
+ - `DB_USER`
330
+ - `DB_PASSWORD`
331
+ - `DB_NAME`
332
+ - optional `RMQ_HOST`
333
+ - optional `RMQ_PORT`
334
+ - optional `RMQ_USER`
335
+ - optional `RMQ_PASSWORD`
336
+ - optional `RMQ_VHOST`
337
+ - optional `RMQ_EXCHANGE`
338
+
339
+ For ready-to-edit templates, use:
340
+
341
+ - `.env.example.client`
342
+ - `.env.example.gateway`
343
+ - `tellymcp init client|gateway|both`
344
+
345
+ ## What it does
346
+
347
+ Flow:
348
+
349
+ 1. The MCP client creates or updates a session context.
350
+ 2. The MCP client creates a short one-time 3-digit session pairing code.
351
+ It should also pass the agent `cwd` when available.
352
+ 3. The human user links that session in Telegram with `/start <code>` or `/link <code>`.
353
+ 4. After pairing, Telegram shows an inline menu for session switching, inbox, content export, live tmux view, and maintenance actions. `/menu` opens the root switcher.
354
+ 5. The MCP client calls `ask_user_telegram` with the linked `session_id`.
355
+ 6. The server sends a redacted Telegram message and waits for the answer.
356
+ 7. The answer is returned as structured MCP tool output.
357
+ 8. Unsolicited Telegram messages are stored in a per-session inbox for later agent processing.
358
+ 9. If the Telegram message contains a photo or document, the file is written into the session `.mcp-xchange/` and delivered according to the currently open session or collaboration target.
359
+
360
+ ## Architecture
361
+
362
+ - TypeScript, strict mode
363
+ - official MCP SDK over Streamable HTTP
364
+ - `grammy` for Telegram transport
365
+ - pluggable `HumanTransport` interface
366
+ - `ioredis` for Redis access
367
+ - `@grammyjs/storage-redis` for Redis-backed session storage
368
+ - FSD-inspired backend structure
369
+
370
+ Telegram is implemented as the first transport backend. Tool orchestration does not depend on Telegram-specific APIs directly.
371
+
372
+ For maintainers and future extension work, see [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
373
+
374
+ Canonical instructions:
375
+
376
+ - gateway `TOOLS.md` is the canonical instruction source
377
+ - `TOOLS.md` now carries a human-readable version marker near the top of the file
378
+ - gateway/client sync still relies on content hash, not on the version string
379
+ - gateway/client runtime compatibility is checked separately in `ws hello/hello_ack`
380
+ - protocol major mismatch blocks gateway transport until the older side is upgraded
381
+ - when behavior changes materially, bump both:
382
+ - the `TOOLS.md` version marker
383
+ - the file content itself
384
+
385
+ Logs are written in two places at the same time:
386
+
387
+ - pretty console output to `stderr`
388
+ - JSONL file at `.telegram-human-mcp/log.jsonl`
389
+
390
+ If Telegram access requires a proxy, the bot transport can use:
391
+
392
+ - HTTP proxy through `HTTP_PROXY`
393
+ - SOCKS5 proxy through `SOCKS5_PROXY`
394
+
395
+ The chosen proxy mode is controlled by `PROXY_USE`.
396
+
397
+ Debug/admin HTTP routes are disabled by default:
398
+
399
+ - `/sessions` requires `MCP_HTTP_ENABLE_DEBUG_ROUTES=true`
400
+ - `/prune` requires `MCP_HTTP_ENABLE_PRUNE_ROUTE=true`
401
+
402
+ If exposed outside localhost, also set `MCP_HTTP_BEARER_TOKEN`.
403
+
404
+ ## Distributed modes
405
+
406
+ The service now has a role-oriented distributed scaffold:
407
+
408
+ - `DISTRIBUTED_MODE=client`
409
+ - current default
410
+ - full local Telegram/MCP/tmux/browser flow
411
+ - local `🏠 Local` flow works directly
412
+ - remote `👥 Collab` flow goes through gateway when `GATEWAY_PUBLIC_URL` is configured
413
+ - `DISTRIBUTED_MODE=gateway`
414
+ - enables `/gateway/*` HTTP surface
415
+ - serves as the shared relay/control plane
416
+ - `DISTRIBUTED_MODE=both`
417
+ - exposes both local service behavior and gateway HTTP surface in one process
418
+
419
+ Current implementation status:
420
+
421
+ - `GET /gateway/healthz` works
422
+ - `POST /gateway/client/register` works
423
+ - `POST /gateway/projects/create` works
424
+ - `POST /gateway/projects/join` works
425
+ - `POST /gateway/sessions/register` works
426
+ - `POST /gateway/partner-note` works
427
+ - `ws` control-plane is active
428
+ - optional `RabbitMQ` exchange can be enabled for durable gateway-side event fanout
429
+ - if `GATEWAY_PUBLIC_URL` is configured, partner-note delivery goes through the gateway HTTP surface
430
+ - in `DISTRIBUTED_MODE=both`, this also covers same-bot local delivery transparently
431
+ - remote project messaging and delivery status go through the gateway DB and `ws`
432
+ - gateway-relayed `Live View` goes through `ws` for client nodes without their own public domain
433
+ - `Collab -> Tools -> History` sends a markdown export of the last 5 Collab events
434
+ for the current active session
435
+ - `TOOLS.md` sync is state-based:
436
+ - client sends per-session `tools_hash` in `ws hello`
437
+ - gateway compares against canonical gateway `TOOLS.md`
438
+ - mismatch triggers `tools_event`
439
+ - client also self-checks on `hello_ack`
440
+ - gateway periodically rechecks online sockets for changed gateway `TOOLS.md`
441
+
442
+ Mode-specific runtime requirements:
443
+
444
+ - `client`
445
+ - local Redis
446
+ - `GATEWAY_PUBLIC_URL`
447
+ - no gateway Postgres bootstrap is performed
448
+ - `gateway`
449
+ - Postgres is required for gateway persistence
450
+ - optional `RMQ_*` enables durable gateway-side event dispatch
451
+ - `both`
452
+ - Postgres is required because the gateway role is active
453
+ - optional `RMQ_*` enables durable gateway-side event dispatch
454
+
455
+ Current file model:
456
+
457
+ - exchange files and screenshots live directly in local `.mcp-xchange`
458
+ - remote delivery sends payloads through gateway delivery events
459
+ - `vfs/minio` are no longer part of the active Telegram file exchange path
460
+ - if an agent must send a real local file to a partner, prefer `send_partner_file`
461
+ over plain `send_partner_note`
462
+
463
+ Current presence model:
464
+
465
+ - gateway knows whether a client node is online through active `ws`
466
+ - gateway also updates `gateway_clients.last_seen_at`
467
+ - there is no separate heartbeat of the coding agent process inside each session yet
468
+ - because of that, a status screen can honestly show client `online/offline`, but not guaranteed agent `online/offline`
469
+
470
+ ## Mini App
471
+
472
+ If `WEBAPP_ENABLED=true`, the session menu exposes `🖥 Live`.
473
+
474
+ The Mini App:
475
+
476
+ - is served by this same Node service under `WEBAPP_BASE_PATH`
477
+ - in `client` mode can also be opened through the shared gateway domain
478
+ - uses vanilla JS and reads the visible tmux pane area through gateway/client relay
479
+ - validates Telegram `initData` server-side using the official hash check
480
+ - can auto-apply launch mode from env:
481
+ - `default`
482
+ - `expand`
483
+ - `fullscreen` with fallback to `expand` when the Telegram client does not support fullscreen
484
+ - requires the Telegram user from `initData` to match the bound session user
485
+ - resolves the active session from the bound Telegram user, so a session id in the URL is not required for normal use
486
+ - deletes the temporary `Open Live View` launcher message after successful Mini App bootstrap
487
+ - allows only a fixed control set:
488
+ - `Esc`
489
+ - `Tab`
490
+ - `/`
491
+ - `Backspace`
492
+ - `Up`
493
+ - `Down`
494
+ - `Enter`
495
+
496
+ `WEBAPP_VISIBLE_SCREENS` controls how much content the live viewport captures relative to the visible tmux height. The default `2` means about two visible screens of content.
497
+
498
+ `WEBAPP_PUBLIC_URL` is only required when the node exposes its own public Mini App URL directly. In `DISTRIBUTED_MODE=client` with `GATEWAY_PUBLIC_URL` configured, `🖥 Live` can be opened through the gateway domain instead.
499
+
500
+ ## Browser feedback
501
+
502
+ The service can also launch an internal Playwright runtime and keep one isolated browser context per `session_id`.
503
+
504
+ Current browser model:
505
+
506
+ - one shared browser process
507
+ - one isolated `BrowserContext + Page` per MCP session
508
+ - events are captured per session:
509
+ - console messages
510
+ - page runtime errors
511
+ - failed or HTTP-error network requests
512
+ - screenshots are written into the same `.mcp-xchange` flow as Telegram file exchange
513
+ - exchange files use the same local `.mcp-xchange` handoff model as Telegram uploads
514
+
515
+ Recommended local dev settings:
516
+
517
+ - `BROWSER_ENABLED=true`
518
+ - `BROWSER_HEADLESS=false`
519
+ - `BROWSER_ADDRESS=http://localhost:5173`
520
+ - start your SPA dev server on `0.0.0.0:5173`
521
+ - open it through `browser_open`
522
+
523
+ Recommended Docker settings:
524
+
525
+ - `BROWSER_HEADLESS=true`
526
+ - target the host dev server through `http://host.docker.internal:3000`
527
+
528
+ Current browser tools:
529
+
530
+ - `browser_open`
531
+ - `browser_reload`
532
+ - `browser_click`
533
+ - `browser_fill`
534
+ - `browser_press`
535
+ - `browser_wait_for`
536
+ - `browser_wait_for_url`
537
+ - `browser_console`
538
+ - `browser_errors`
539
+ - `browser_network_failures`
540
+ - `browser_clear_logs`
541
+ - `browser_dom`
542
+ - `browser_computed_style`
543
+ - `browser_screenshot`
544
+ - `browser_close`
545
+
546
+ Browser target convention:
547
+
548
+ - browser interaction tools support `ai_tag` in addition to `selector` and `text`
549
+ - frontend code may annotate elements with:
550
+ - `data-drive-tag="save-button"`
551
+ - or `ai-tag="save-button"`
552
+ - recommended convention is `data-drive-tag="..."` with an explicit value
553
+
554
+ If `BROWSER_ADDRESS` is configured, `browser_open` may use either:
555
+
556
+ - a full URL like `http://localhost:5173/settings`
557
+ - or a relative path like `/settings`
558
+
559
+ `browser_screenshot` returns:
560
+
561
+ - `file_path` full path to the written screenshot file
562
+ - `workspace_dir` the resolved workspace root for that session
563
+ - `exchange_dir` the resolved `.mcp-xchange` directory used for the write
564
+ - `telegram_message_id` when `send_to_telegram=true`
565
+
566
+ ## Telegram UI
567
+
568
+ The Telegram bot exposes one root entrypoint:
569
+
570
+ - `/menu`
571
+
572
+ Current root menu behavior:
573
+
574
+ - shows the current active session
575
+ - shows the last worked session and update time
576
+ - shows tmux status
577
+ - lists paired sessions as one button per row
578
+ - keeps `Refresh` and `Tools` on the final row
579
+
580
+ Current session menu behavior:
581
+
582
+ - title is `Session: <name>`
583
+ - first row: `Live | Content | Browser`
584
+ - second row: `Local | Collab`
585
+ - third row: `Inbox | Storage | Settings`
586
+ - final row: `Back`
587
+
588
+ `Settings` contains:
589
+
590
+ - `Info`
591
+ - `Rename`
592
+ - `Unpair`
593
+ - `Back`
594
+
595
+ Current browser menu behavior:
596
+
597
+ - `Screenshots`
598
+ - browser screenshots are separated from ordinary uploaded files
599
+
600
+ Current storage behavior:
601
+
602
+ - `Storage` shows `.mcp-xchange` contents for the active session
603
+ - storage entries can be opened and sent back to Telegram as files
604
+
605
+ Current file behavior:
606
+
607
+ - top-level `Files` menu is removed
608
+ - `Browser -> Screenshots` still shows screenshots created by `browser_screenshot`
609
+ - if the user is inside:
610
+ - the current session
611
+ - `🏠 Local -> Напарник`
612
+ - `👥 Collab -> Project -> Member`
613
+ then the next uploaded file is delivered directly into that target
614
+
615
+ Local link behavior:
616
+
617
+ - `Link` opens a list of other sessions visible to the same Telegram identity
618
+ - choosing one creates a mutual partner link between the two sessions
619
+ - once linked, the button becomes `Unlink`
620
+ - this link is intended for backend/frontend or similar agent collaboration
621
+ - linked agents should use `send_partner_note` for structured collaboration
622
+
623
+ Local partner menu behavior:
624
+
625
+ - `Local` opens a linked-session collaboration menu
626
+ - available actions are:
627
+ - `Ask`
628
+ - `Share`
629
+ - `Unlink`
630
+ - the Telegram prompt format is:
631
+ - first line = short summary
632
+ - optional blank line
633
+ - remaining text = full message body
634
+ - partner wake-up semantics:
635
+ - `TMUX_PARTNER_NUDGE_MESSAGE` is for collaboration notes, not for human Telegram inbox
636
+ - the receiving agent should read `.mcp-xchange/SHARED_INDEX.md` and the newest note first
637
+
638
+ Linked-session collaboration contract:
639
+
640
+ - `send_partner_note` writes one note per event into the partner workspace
641
+ - collaborative notes live under `.mcp-xchange/shares/`
642
+ - copied artifacts live under `.mcp-xchange/shares/files/<share_id>/`
643
+ - `.mcp-xchange/SHARED_INDEX.md` acts as the append-only index of partner-facing notes
644
+ - `.mcp-xchange/LOCAL_INDEX.md` acts as the append-only index of local agent-facing handoffs
645
+ - supported note kinds are:
646
+ - `share`
647
+ - `question`
648
+ - `reply`
649
+ - `request`
650
+ - `handoff`
651
+ - useful partner-facing content usually includes:
652
+ - API summaries
653
+ - what changed
654
+ - current errors
655
+ - sample payloads
656
+ - relevant git changes from the agent workspace
657
+ - do not send raw implementation source files as partner artifacts; prefer summaries, specs, payload examples, logs, screenshots, and Markdown notes
658
+ - recommended mapping:
659
+ - `question` for "what APIs do you expose?", "what's new?", "send the error details"
660
+ - `reply` for direct answers, usually with `in_reply_to`
661
+ - `share` for one-way status updates
662
+ - `request` for explicit teammate actions
663
+ - `handoff` for transferring results or artifacts
664
+ - before sending a local partner note, the agent should call `get_session_context` and verify that `linked_session_id` exists
665
+
666
+ Collab project behavior:
667
+
668
+ - `👥 Collab` is the project-based multi-machine and multi-bot collaboration flow
669
+ - target session is chosen from `Projects -> <project> -> <member>`
670
+ - member screen layout is:
671
+ - first row: `Ask | Share`
672
+ - second row: `Live`
673
+ - semantics inside `Project -> Member` depend on the action:
674
+ - `Ask` sends a task to the selected member session
675
+ - expected reply route is `member -> current session`
676
+ - `Share` creates a task for the current session
677
+ - expected send route is `current session -> member`
678
+ - `Live` now uses an approval flow before opening the selected member session
679
+ - after approval, the requester receives a fresh `Open Live View` button through the existing webapp relay path
680
+ - direct file uploads still go to that exact target session when a member screen is open
681
+ - if an old member-menu message becomes stale, clicking it deletes that outdated Telegram message instead of leaving a dead keyboard
682
+
683
+ Recommended share-note structure:
684
+
685
+ ```md
686
+ ---
687
+ share_id: 2026-05-12T22-10-00Z-frontend-question-api
688
+ kind: question
689
+ from_session_id: frontend-session
690
+ to_session_id: backend-session
691
+ created_at: 2026-05-12T22:10:00Z
692
+ requires_reply: true
693
+ in_reply_to: null
694
+ artifacts: []
695
+ ---
696
+
697
+ # Summary
698
+ Need an up-to-date backend API summary.
699
+
700
+ # Message
701
+ - what endpoints exist
702
+ - what auth is required
703
+ - what changed recently
704
+
705
+ # Expected Reply
706
+ Short Markdown summary plus spec or sample payload files when available.
707
+ ```
708
+
709
+ Current content menu behavior:
710
+
711
+ - `Visible`
712
+ - `Full`
713
+ - `Last 300`
714
+ - `Last 1000`
715
+
716
+ `Tools` currently contains:
717
+
718
+ - `Broadcast`
719
+ - `Prune all`
720
+
721
+ `Broadcast` uses a one-shot prompt. After a successful broadcast, only that prompt is deleted. Cancel returns to `Tools` without destroying the existing menu message.
722
+
723
+ ## Telegram file exchange
724
+
725
+ Ordinary Telegram messages may include:
726
+
727
+ - text only
728
+ - photo with optional caption
729
+ - document with optional caption
730
+
731
+ When a photo or document arrives:
732
+
733
+ - the file is downloaded into `MCP_XCHANGE_DIR`, default `.mcp-xchange`, under the paired agent workspace
734
+ - if the user is already inside a concrete target context, the upload itself is the handoff action
735
+ - otherwise the file is delivered into the currently open session as a local session handoff
736
+ - there is no separate `Files` confirmation screen anymore
737
+
738
+ Runtime note:
739
+
740
+ - the main service writes these files directly in the local workspace
741
+
742
+ ## Default session identity
743
+
744
+ If a tool call omits `session_id`, the server derives a stable default session automatically.
745
+
746
+ Resolution order for the human-readable project/session title:
747
+
748
+ 1. `PROJECT_NAME` from `.env`
749
+ 2. `package.json` `name`
750
+ 3. git root directory name
751
+ 4. current working directory name
752
+
753
+ The derived `session_id` is built from that title plus a short stable hash of the project path, so it remains consistent across restarts.
754
+
755
+ This means you can call session-oriented tools without explicitly passing `session_id` when working in a single project context.
756
+
757
+ ## Repository development
758
+
759
+ ```bash
760
+ yarn install
761
+ ```
762
+
763
+ ### Build
764
+
765
+ ```bash
766
+ yarn build
767
+ ```
768
+
769
+ ### Run
770
+
771
+ Development:
772
+
773
+ ```bash
774
+ yarn dev:gw
775
+ ```
776
+
777
+ Production build:
778
+
779
+ ```bash
780
+ yarn build
781
+ yarn start:gw
782
+ ```
783
+
784
+ After startup you should see readiness logs in the console.
785
+
786
+ In repository dev mode, the HTTP service exposes:
787
+
788
+ - MCP endpoint at `http://127.0.0.1:8787/mcp` by default
789
+ - health check at `http://127.0.0.1:8787/healthz`
790
+
791
+ If `MCP_HTTP_BEARER_TOKEN` is configured:
792
+
793
+ - `/mcp` requires `Authorization: Bearer ...`
794
+ - `/sessions` and `/prune` also require the same bearer when enabled
795
+ - Telegram Mini App does not use this bearer directly; it has its own `initData` bootstrap and a short-lived WebApp session token
796
+
797
+ `yarn dev:gw:telegram` is still available, but it only starts the `telegram_mcp` feature node.
798
+ It does not expose HTTP by itself anymore. `/mcp`, `/webapp`, and `/healthz` are now served only through the Moleculer API gateway aliases in the full `dev:gw` / `start:gw` runtime, or through a separate gateway node in the same namespace.
799
+
800
+ ## Optional Docker deployment
801
+
802
+ Docker is no longer required for the default product install flow.
803
+
804
+ This repository still includes a single-container deployment path without an internal nginx layer for ops/self-hosted scenarios.
805
+
806
+ Inside the container:
807
+
808
+ - `node` runs the MCP HTTP service on `0.0.0.0:8787`
809
+ - `redis-server` runs on `127.0.0.1:6379`
810
+ - the application itself serves:
811
+ - `/mcp`
812
+ - `/webapp`
813
+ - `/healthz`
814
+ - `/sessions`
815
+ - `/prune`
816
+
817
+ This means an external reverse proxy can forward directly to container port `8787`, while all app routing stays inside the Node service.
818
+
819
+ Build the image fully inside Docker:
820
+
821
+ ```bash
822
+ docker compose build
823
+ ```
824
+
825
+ Run it:
826
+
827
+ ```bash
828
+ docker compose up -d
829
+ ```
830
+
831
+ Stop it:
832
+
833
+ ```bash
834
+ docker compose down
835
+ ```
836
+
837
+ The compose file:
838
+
839
+ - builds the image from this repository
840
+ - injects `.env`
841
+ - overrides runtime networking so the app talks to local in-container Redis and listens on `0.0.0.0:8787`
842
+ - publishes only `8787:8787`
843
+ - keeps `host.docker.internal` available for optional host-side development integrations
844
+ - persists Redis state in `./data/redis`
845
+
846
+ After startup:
847
+
848
+ - MCP is reachable at `http://<host>:8787/mcp`
849
+ - Mini App static/API routes are reachable under `http://<host>:8787/webapp/`
850
+ - health check is at `http://<host>:8787/healthz`
851
+
852
+ Recommended external reverse proxy pattern:
853
+
854
+ - external proxy forwards `/mcp` to `http://<container-host>:8787/mcp`
855
+ - external proxy forwards `/webapp/` to `http://<container-host>:8787/webapp/`
856
+ - or, if you prefer, the external proxy can forward a wider prefix directly to `http://<container-host>:8787`
857
+ - no direct external access is needed to in-container Redis
858
+ - `tmux` access is expected to be direct from the running `tellymcp` process
859
+
860
+ Important:
861
+
862
+ - pairing state
863
+ - active session bindings
864
+ - inbox messages
865
+ - menu payload buffers
866
+ - WebApp launch/session state
867
+
868
+ are all stored in Redis. In the Docker deployment they survive restarts because `./data/redis` is mounted into the container and Redis AOF is enabled.
869
+
870
+ Optional if the local tmux server uses a non-default socket:
871
+
872
+ ```bash
873
+ TMUX_SOCKET_PATH=/tmp/tmux-1000/default tellymcp run
874
+ ```
875
+
876
+ ## MCP usage
877
+
878
+ ### 1. Save session context
879
+
880
+ Call `set_session_context`:
881
+
882
+ ```json
883
+ {
884
+ "session_id": "backend-refactor",
885
+ "session_label": "Backend refactor",
886
+ "task": "Admin API cleanup",
887
+ "summary": "We are simplifying admin API response shapes and need product confirmations on compatibility-sensitive changes.",
888
+ "files": [
889
+ "backend/src/routes/admin.ts",
890
+ "backend/src/services/adminService.ts"
891
+ ],
892
+ "decisions": ["Keep Telegram as the human clarification channel"],
893
+ "risks": ["Breaking existing clients"]
894
+ }
895
+ ```
896
+
897
+ ### 2. Inspect session state
898
+
899
+ Call `get_session_context`:
900
+
901
+ ```json
902
+ {
903
+ "session_id": "backend-refactor"
904
+ }
905
+ ```
906
+
907
+ This returns:
908
+
909
+ - saved context if it exists
910
+ - whether the session is currently paired
911
+ - stored tmux targeting data if configured
912
+ - Telegram binding metadata if pairing exists
913
+ - a `status_message` describing whether pairing and tmux delivery are active
914
+
915
+ ### 2. Bind tmux context for Telegram delivery
916
+
917
+ If Codex is running inside tmux, capture the current tmux context before you leave the workstation. A reliable way is:
918
+
919
+ ```bash
920
+ tmux display-message -p '#{session_name} #{window_name} #{window_index} #{pane_id} #{pane_index}'
921
+ ```
922
+
923
+ The preferred path is to pass these attributes, together with the agent workspace `cwd`, directly into `create_session_pair_code`, so pairing immediately creates a distinct session identity for this agent and gives the server the correct `.mcp-xchange` root:
924
+
925
+ ```json
926
+ {
927
+ "tmux_session_name": "dev",
928
+ "tmux_window_name": "test",
929
+ "tmux_window_index": 1,
930
+ "tmux_pane_id": "%7",
931
+ "tmux_pane_index": 0
932
+ }
933
+ ```
934
+
935
+ Important:
936
+
937
+ - if you pair without tmux attributes, Telegram linking still succeeds
938
+ - but `tmux_target` stays empty
939
+ - in that state tmux nudges and Mini App controls will not work until `set_tmux_target` is called later
940
+
941
+ You can still call `set_tmux_target` later if you need to update or override the stored target:
942
+
943
+ ```json
944
+ {
945
+ "session_id": "backend-refactor",
946
+ "tmux_session_name": "work",
947
+ "tmux_window_name": "test",
948
+ "tmux_window_index": 1,
949
+ "tmux_pane_id": "%7",
950
+ "tmux_pane_index": 0,
951
+ "tmux_target": "%7"
952
+ }
953
+ ```
954
+
955
+ After that, when the paired session receives an unsolicited inbox message, the service can run:
956
+
957
+ ```bash
958
+ tmux send-keys -t %7 "проверь inbox" C-m
959
+ ```
960
+
961
+ The service does not forward the Telegram message text into tmux. It only nudges the agent. The agent still reads actual message contents through `get_telegram_inbox` and `delete_telegram_inbox_message`, or through `get_telegram_inbox_count` first in passive no-tmux mode.
962
+ If several Telegram messages arrive close together, the nudge is debounced by `TMUX_NUDGE_DEBOUNCE_SECONDS` so the agent gets one wake-up for the batch instead of one wake-up per message.
963
+ Ordinary Telegram messages are always stored in the inbox of the currently active session for that Telegram identity.
964
+
965
+ ### 3. Pair a session
966
+
967
+ If the user asks to register or link the current agent in Telegram, the agent should first collect:
968
+
969
+ - current `cwd`
970
+ - tmux attributes when running inside tmux
971
+
972
+ Only after that should it call `create_session_pair_code`.
973
+
974
+ Call `create_session_pair_code` with a stable session id:
975
+
976
+ ```json
977
+ {
978
+ "session_id": "backend-refactor",
979
+ "session_label": "Backend refactor"
980
+ }
981
+ ```
982
+
983
+ The tool returns a short-lived code, a status message for the agent, and optionally a Telegram deep link.
984
+
985
+ If you omit `session_id`, the server derives one automatically.
986
+
987
+ If multiple agents work from different tmux windows or panes, pass tmux attributes during pairing so the server derives distinct session identities automatically:
988
+
989
+ ```json
990
+ {
991
+ "tmux_session_name": "dev",
992
+ "tmux_window_name": "test",
993
+ "tmux_window_index": 1,
994
+ "tmux_pane_id": "%7",
995
+ "tmux_pane_index": 0
996
+ }
997
+ ```
998
+
999
+ With tmux attributes present, they participate in the derived default `session_id` and `session_label`, which prevents multiple agents from collapsing into one Telegram session even if they share similar project structure. In practice this means `/menu` can later show both agents separately and let Telegram switch the active async context between them.
1000
+
1001
+ ### 4. Link in Telegram
1002
+
1003
+ In Telegram, send one of:
1004
+
1005
+ ```text
1006
+ /start ABCD-EFGH
1007
+ ```
1008
+
1009
+ or
1010
+
1011
+ ```text
1012
+ /link ABCD-EFGH
1013
+ ```
1014
+
1015
+ After successful pairing, the bot sends a main inline menu. You can also reopen it later with:
1016
+
1017
+ ```text
1018
+ /menu
1019
+ ```
1020
+
1021
+ ### 5. Ask the user
1022
+
1023
+ Call `ask_user_telegram`:
1024
+
1025
+ ```json
1026
+ {
1027
+ "session_id": "backend-refactor",
1028
+ "question": "Can I change the response shape for the admin API?",
1029
+ "task": "Admin API cleanup",
1030
+ "context": "The old shape is inconsistent and adds special cases in the client.",
1031
+ "options": [
1032
+ "Keep current response shape",
1033
+ "Change response shape and update all callers"
1034
+ ],
1035
+ "recommended_option": "Keep current response shape",
1036
+ "fallback_if_timeout": "Keep current response shape"
1037
+ }
1038
+ ```
1039
+
1040
+ If you want the saved session context to be appended automatically, set:
1041
+
1042
+ ```json
1043
+ {
1044
+ "use_saved_context": true
1045
+ }
1046
+ ```
1047
+
1048
+ ### 6. Clear session context
1049
+
1050
+ Call `clear_session_context`:
1051
+
1052
+ ```json
1053
+ {
1054
+ "session_id": "backend-refactor"
1055
+ }
1056
+ ```
1057
+
1058
+ This removes saved context and also removes Telegram pairing for the same session.
1059
+
1060
+ ### 7. Clear session pairing
1061
+
1062
+ Call `clear_session_pairing`:
1063
+
1064
+ ```json
1065
+ {
1066
+ "session_id": "backend-refactor"
1067
+ }
1068
+ ```
1069
+
1070
+ This removes the Telegram binding so the session can be paired again.
1071
+
1072
+ ### 8. Send one-way notification
1073
+
1074
+ Call `notify_telegram`:
1075
+
1076
+ ```json
1077
+ {
1078
+ "session_id": "backend-refactor",
1079
+ "message": "Build finished successfully. Ready for review.",
1080
+ "task": "Admin API cleanup",
1081
+ "risk_level": "low",
1082
+ "use_saved_context": true
1083
+ }
1084
+ ```
1085
+
1086
+ This sends a Telegram message without waiting for a reply.
1087
+
1088
+ ### 9. Poll unsolicited Telegram inbox messages
1089
+
1090
+ If the user writes to the bot without replying to an active question, the message is stored in the session inbox.
1091
+
1092
+ If the paired session has a tmux target, the preferred path is event-driven:
1093
+
1094
+ - Telegram message arrives
1095
+ - service stores it in inbox
1096
+ - service nudges tmux
1097
+ - agent wakes up and calls `get_telegram_inbox`
1098
+
1099
+ If there is no tmux nudge path, use passive mode. First call `get_telegram_inbox_count`:
1100
+
1101
+ ```json
1102
+ {
1103
+ "session_id": "backend-refactor"
1104
+ }
1105
+ ```
1106
+
1107
+ Only if `total > 0`, call `get_telegram_inbox`:
1108
+
1109
+ ```json
1110
+ {
1111
+ "session_id": "backend-refactor"
1112
+ }
1113
+ ```
1114
+
1115
+ After the agent processes an inbox item, delete it explicitly with `delete_telegram_inbox_message`:
1116
+
1117
+ ```json
1118
+ {
1119
+ "session_id": "backend-refactor",
1120
+ "message_id": "inbox_20260504120000_ab12cd"
1121
+ }
1122
+ ```
1123
+
1124
+ ## Telegram menu
1125
+
1126
+ The bot now exposes a small inline menu for Telegram-side control:
1127
+
1128
+ - `Inbox` shows the latest unsolicited inbox messages for the active session
1129
+ - tapping an inbox item opens its full contents
1130
+ - the detail card has a `Delete` action
1131
+ - `Session: ...` shows the currently active linked session
1132
+ - `Refresh` re-renders the current menu state
1133
+
1134
+ Menu callback payloads stay short. Buttons only carry a short Redis key, while the actual menu state is stored server-side with TTL in Redis.
1135
+
1136
+ ## Queue mode
1137
+
1138
+ `MODE=reject`
1139
+
1140
+ - if one request is already active, the next tool call fails immediately
1141
+
1142
+ `MODE=queue`
1143
+
1144
+ - requests are queued FIFO
1145
+ - queued requests are not sent to Telegram until they become active
1146
+
1147
+ ## Connect to Codex
1148
+
1149
+ Recommended long-running service flow:
1150
+
1151
+ 1. Start the service.
1152
+
1153
+ For local client mode:
1154
+
1155
+ ```bash
1156
+ yarn dev:client
1157
+ ```
1158
+
1159
+ Then register:
1160
+
1161
+ ```bash
1162
+ codex mcp add telegramHuman --url http://127.0.0.1:8787/mcp
1163
+ ```
1164
+
1165
+ For gateway/both mode behind nginx:
1166
+
1167
+ ```bash
1168
+ yarn dev:builder
1169
+ ```
1170
+
1171
+ 2. Register the already-running MCP endpoint in Codex:
1172
+
1173
+ ```bash
1174
+ codex mcp add telegramHuman --url http://127.0.0.1:8080/api/mcp
1175
+ ```
1176
+
1177
+ If you enable bearer auth with `MCP_HTTP_BEARER_TOKEN`, register it like this:
1178
+
1179
+ ```bash
1180
+ export TELEGRAM_MCP_BEARER_TOKEN="your-token"
1181
+ codex mcp add telegramHuman \
1182
+ --url http://127.0.0.1:8080/api/mcp \
1183
+ --bearer-token-env-var TELEGRAM_MCP_BEARER_TOKEN
1184
+ ```
1185
+
1186
+ Why these URLs differ:
1187
+
1188
+ - `dev:client` serves MCP directly from the local standalone listener, by default at `http://127.0.0.1:8787/mcp`
1189
+ - `dev:builder` / `both` mode serves MCP through the shared backend ingress, by default at `http://127.0.0.1:8080/api/mcp`
1190
+
1191
+ For externally exposed deployments:
1192
+
1193
+ - prefer enabling `MCP_HTTP_BEARER_TOKEN`
1194
+ - keep `/sessions` and `/prune` disabled unless you actively need them
1195
+ - leave WebApp access to Telegram `initData` validation plus its short-lived session token flow
1196
+
1197
+ This project no longer uses stdio mode. MCP access is exposed only through the HTTP endpoint.
1198
+
1199
+ Current Moleculer feature services:
1200
+
1201
+ - `telegramMcp.runtime`
1202
+ - `telegramMcp.pair`
1203
+ - `telegramMcp.sessionContext`
1204
+ - `telegramMcp.notify`
1205
+ - `telegramMcp.inbox`
1206
+ - `telegramMcp.approval`
1207
+ - `telegramMcp.browser`
1208
+ - `telegramMcp.collaboration`
1209
+ - `telegramMcp.mcpServer`
1210
+ - `telegramMcp.http`
1211
+
1212
+ ## Example AGENTS.md snippet
1213
+
1214
+ ```md
1215
+ ## Telegram clarification
1216
+
1217
+ If you need clarification from the user and the answer is required to continue safely,
1218
+ use the MCP tools `create_session_pair_code` and `ask_user_telegram`.
1219
+
1220
+ Rules:
1221
+
1222
+ - prefer explicit `session_id` when multiple projects or sessions share one Telegram bot; otherwise the derived default session is acceptable
1223
+ - save or refresh session context before risky question flows when it helps reuse context
1224
+ - if the agent runs inside tmux, capture `#{session_name} #{window_name} #{window_index} #{pane_id} #{pane_index}` and pass them to `create_session_pair_code`; use `set_tmux_target` later only if you need to refresh or override the target
1225
+ - if the session is not linked yet, create a pair code first
1226
+ - if a paired session has a configured tmux target, treat a tmux nudge as the signal to check the inbox
1227
+ - if a paired session has no tmux target, periodically call `get_telegram_inbox_count`
1228
+ - call `get_telegram_inbox` only if the count is greater than zero
1229
+ - before the final answer in passive no-tmux mode, check `get_telegram_inbox_count`
1230
+ - after handling an inbox item, call `delete_telegram_inbox_message`
1231
+ - include concise task context
1232
+ - include affected files when relevant
1233
+ - include a conservative fallback if the request times out
1234
+ - never send secrets, tokens, private keys, database URLs, or raw customer data
1235
+ ```
1236
+
1237
+ ## Verification
1238
+
1239
+ Commands run locally:
1240
+
1241
+ - `npm run format:check`
1242
+ - `npm run build`
1243
+ - `npm run lint`
1244
+
1245
+ Tests are not implemented in this iteration.
1246
+
1247
+ ## Known limitations
1248
+
1249
+ - Telegram is the only transport backend implemented right now
1250
+ - no webhook support, long polling only
1251
+ - no automated tests in the current iteration
1252
+ - queued requests are coordinated in-process, with Redis used as the shared state backend
1253
+ - session context tools are implemented, but there is no version history or merge strategy beyond last write wins
1254
+ - inbox polling is explicit; unsolicited Telegram messages are not pushed into the agent automatically
1255
+ - MCP HTTP sessions are kept in-process; restarting the service drops active MCP client sessions and they reconnect cleanly
1256
+
1257
+ ## Security notes
1258
+
1259
+ - all outbound question content is redacted before sending to Telegram
1260
+ - replies are accepted only from the Telegram user/chat bound to the session
1261
+ - pairing codes are short-lived and one-time use
1262
+ - do not use this server to send secrets, raw `.env` content, tokens, private keys, or customer data