{"_id":"@5dive/telegram-codex-mcp","name":"@5dive/telegram-codex-mcp","dist-tags":{"latest":"0.5.7"},"versions":{"0.5.7":{"name":"@5dive/telegram-codex-mcp","version":"0.5.7","description":"Telegram bridge for OpenAI Codex CLI, delivered as a stdio MCP server: wait for messages, reply, edit, react, and download attachments from a Telegram bot.","license":"Apache-2.0","type":"module","homepage":"https://github.com/5dive-ai/5dive-plugins/tree/main/plugins/telegram-codex","repository":{"type":"git","url":"git+https://github.com/5dive-ai/5dive-plugins.git","directory":"plugins/telegram-codex"},"keywords":["telegram","codex","mcp","openai","bot","bridge"],"bin":{"telegram-codex-mcp":"server.ts"},"scripts":{"start":"bun install --no-summary && bun server.ts"},"publishConfig":{"access":"public"},"dependencies":{"@modelcontextprotocol/sdk":"^1.0.0","grammy":"^1.21.0"},"_id":"@5dive/telegram-codex-mcp@0.5.7","bugs":{"url":"https://github.com/5dive-ai/5dive-plugins/issues"},"_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-Qz/YmKF+AMJTWEsN4J+BNcqAXgpgFR+SrvZ65Tnn2ZdAMtAOPhw5+mwzom5DxfTtzvIlHoxURKsjo53jn4aesg==","shasum":"5506f43baf333a950ee6dfdeff8d5c75a2adfddc","tarball":"https://registry.npmjs.org/@5dive/telegram-codex-mcp/-/telegram-codex-mcp-0.5.7.tgz","fileCount":4,"unpackedSize":131711,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDlX9G/9XAMOr0aWbNoh1r+lWvKmlWWOQXGq+wkYB6/CAIhAJX/SMXXlo/e6efz/rqFdr64phuxPIY8L3Q1U9mGM1gu"}]},"_npmUser":{"name":"lodar","email":"info@5dive.com"},"directories":{},"maintainers":[{"name":"lodar","email":"info@5dive.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/telegram-codex-mcp_0.5.7_1783509540163_0.7710921117393428"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-08T11:18:59.887Z","0.5.7":"2026-07-08T11:19:00.311Z","modified":"2026-07-08T11:19:00.952Z"},"maintainers":[{"name":"lodar","email":"info@5dive.com"}],"description":"Telegram bridge for OpenAI Codex CLI, delivered as a stdio MCP server: wait for messages, reply, edit, react, and download attachments from a Telegram bot.","homepage":"https://github.com/5dive-ai/5dive-plugins/tree/main/plugins/telegram-codex","keywords":["telegram","codex","mcp","openai","bot","bridge"],"repository":{"type":"git","url":"git+https://github.com/5dive-ai/5dive-plugins.git","directory":"plugins/telegram-codex"},"bugs":{"url":"https://github.com/5dive-ai/5dive-plugins/issues"},"license":"Apache-2.0","readme":"# telegram-codex MCP\n\nA Telegram bridge for [OpenAI Codex CLI](https://github.com/openai/codex),\ndelivered as a stdio MCP server.\n\nSibling to the [`telegram/`](../telegram/) plugin (which targets Claude\nCode). Forked rather than shared because the runtime contracts diverge —\nCodex has no channel-notification protocol, so inbound delivery here is\npoll-based via a `wait_for_message` tool instead of pushed via channels.\n\n## What you get\n\nFive MCP tools available to Codex:\n\n- `wait_for_message` — block until the user sends a DM/group message.\n- `reply` — send a new Telegram message (text, MarkdownV2, file attachments).\n- `edit_message` — patch a prior bot message in place (silent, no push).\n- `react` — emoji reaction on an inbound message.\n- `download_attachment` — fetch a file by `file_id` into the local inbox.\n\n## Prerequisites\n\n- [Bun](https://bun.sh) — `curl -fsSL https://bun.sh/install | bash`\n- [Codex CLI](https://github.com/openai/codex) — `npm i -g @openai/codex`\n- A Telegram bot token from [@BotFather](https://t.me/BotFather)\n\n## Setup\n\n**1. Install the server**\n\n```sh\ngit clone https://github.com/5dive-ai/5dive-plugins\ncd 5dive-plugins/plugins/telegram-codex\nbun install\n```\n\n**2. Save the bot token**\n\n```sh\nmkdir -m 700 -p ~/.codex/channels/telegram\ncat > ~/.codex/channels/telegram/.env <<EOF\nTELEGRAM_BOT_TOKEN=123456789:AAH...\nEOF\nchmod 600 ~/.codex/channels/telegram/.env\n```\n\n**3. Seed the allowlist**\n\nTwo options:\n\n**3a. Pair via the bot (recommended)**\n\n```sh\nbun pair.ts\n```\n\nThe CLI prints `DM @<botname> from your Telegram account within 60s to\npair...`. Send any message to your bot from the Telegram account you\nwant allowed. The CLI captures your user_id, writes\n`~/.codex/channels/telegram/access.json`, and replies \"✅ paired\" in\nthe chat.\n\nRe-run anytime to add another user to the allowlist. Conflicts with a\nrunning Codex MCP server (one getUpdates consumer per token) — stop\nCodex first, pair, then restart.\n\n**3b. Hand-write access.json**\n\n```json\n{\n  \"allowFrom\": [\"123456789\"],\n  \"groups\": {\n    \"-1001234567890\": { \"requireMention\": false, \"allowFrom\": [] }\n  }\n}\n```\n\n- `allowFrom` — Telegram user IDs allowed to DM the bot. In a DM the\n  `chat_id` equals the user ID.\n- `groups` — group/supergroup chat IDs (negative) and per-group policy.\n  - `requireMention: true` only routes messages that `@mention` the bot\n    (or quote-reply to it).\n  - `allowFrom: []` falls back to the top-level `allowFrom` list; a\n    non-empty list overrides per group.\n\nMessages from anyone not on the lists are silently dropped before they\nreach `wait_for_message`. Group access can only be configured by\nhand-writing access.json — the `pair.ts` CLI handles DMs only.\n\n**4. Wire into Codex**\n\nAdd to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.telegram]\ncommand = \"bun\"\nargs = [\"/absolute/path/to/5dive-plugins/plugins/telegram-codex/server.ts\"]\n```\n\n**5. Add the comms playbook**\n\nDrop the contents of [`AGENTS.md`](./AGENTS.md) into your\n`~/.codex/AGENTS.md` so the model knows when and how to use the tools.\n\n**6a. (Optional) Wire the \"approve risky commands from Telegram\" bridge**\n\nCodex's `PermissionRequest` hook fires every time it wants to run a command\nthat exceeds its current `approval_policy` / `sandbox_mode`. The\n`request-permission.ts` hook in this plugin routes that prompt to your\nTelegram bot — a message with **✅ allow / ❌ deny** inline buttons. Tap one\nand Codex proceeds (or doesn't).\n\n```toml\n[features]\nhooks = true\n\n[[hooks.PermissionRequest]]\n\n[[hooks.PermissionRequest.hooks]]\ntype = \"command\"\ncommand = \"bun /absolute/path/to/5dive-plugins/plugins/telegram-codex/hooks/request-permission.ts\"\ntimeout = 180\nasync = false\n```\n\nBehavior notes:\n\n- **Fail-closed.** If the MCP server isn't running (no Telegram bridge),\n  or no one taps a button before the 120s default timeout, the hook\n  returns `deny`. Codex's native UI then takes over — you're never\n  silently auto-approved.\n- The MCP server must be live for the bridge to work. In practice this\n  means Codex must have called at least one telegram tool earlier in the\n  session (the MCP server lazy-spawns). For one-shot Codex runs where\n  the very first action is privileged, the bridge will fall through to\n  Codex's native UI.\n- **Hook trust gate.** On the first session after wiring this hook,\n  Codex shows a one-time \"Hook needs review\" TUI prompt. Press `2` (or\n  `t`) to trust. Codex persists the decision in `[hooks.state]` of the\n  config.\n- **Override timeout** with `CODEX_TG_APPROVAL_TIMEOUT_MS` env (range\n  5000–600000).\n- **Bypass entirely** with `CODEX_TG_APPROVAL_DISABLED=1` env — useful\n  for unattended runs where you want Codex's own approval policy to be\n  authoritative without going to Telegram.\n\nThis is useless if your `approval_policy = \"never\"` / `sandbox_mode =\n\"danger-full-access\"`. The bridge only matters when Codex actually\nneeds to ask.\n\n**6b. (Optional) Wire the \"turn complete\" ping**\n\nTo get a Telegram ping every time Codex finishes a turn, add the `Stop`\nhook to `~/.codex/config.toml`:\n\n```toml\n[features]\nhooks = true\n\n[[hooks.Stop]]\n\n[[hooks.Stop.hooks]]\ntype = \"command\"\ncommand = \"bun /absolute/path/to/5dive-plugins/plugins/telegram-codex/hooks/notify-stop.ts\"\nasync = false\n```\n\nCodex 0.134 doesn't support `async = true` — keep it sync. The hook\nfires once per Codex turn and runs in under a second.\n\nOverride the message text per-session with `CODEX_NOTIFY_TEXT=...`;\nsilence pings entirely with `CODEX_NOTIFY_DISABLED=1` (useful when\nyou're already talking to the bot via `wait_for_message`/`reply` and\nthe Stop ping would be duplicate).\n\n**7. Run Codex**\n\n```sh\ncodex\n```\n\nDM your bot. Codex calls `wait_for_message`, your DM resolves it, Codex\nreplies via the `reply` tool. Done.\n\n## Differences from the Claude Code build\n\n| Concern               | `telegram/` (Claude Code)              | `telegram-codex/` (this)         |\n| --------------------- | -------------------------------------- | -------------------------------- |\n| Inbound delivery      | `claude/channel` JSON-RPC notification | `wait_for_message` blocking tool |\n| Permission relay      | `claude/channel/permission` protocol   | `PermissionRequest` hook + buttons |\n| Slash commands        | `/telegram:configure`, `:access`, …    | bot-side menu (`/help` `/status` `/stop` `/restart` `/agents` `/tasks` `/task` `/org` `/model` `/ping` `/start`) |\n| Lifecycle hooks       | PreToolUse, Stop, etc.                 | `Stop` hook ships in `hooks/`    |\n| State dir             | `~/.claude/channels/telegram/`         | `~/.codex/channels/telegram/`    |\n| Pairing flow          | code via DM → `/telegram:access pair`  | `bun pair.ts` standalone CLI     |\n\nA fifth runtime, [`telegram-opencode`](../telegram-opencode), is **not** in this\nfamily: opencode ships a headless HTTP server, so its bridge is a long-running\nrelay (no `wait_for_message`, no watchdog, no hooks) rather than an MCP server.\n\n## Roadmap\n\n- v0.1.0 — outbound + blocking inbound, preconfigured allowlist\n- v0.1.1 — `Stop` hook for \"turn complete\" Telegram ping\n- v0.1.2 — pairing CLI (`bun pair.ts`) for one-shot user-id capture\n- v0.1.3 — approval-mode bridge: `PermissionRequest` → Telegram buttons\n- v0.1.4 — bot slash commands (`/help`, `/status`, `/ping`) + setMyCommands menu; wait_for_message capped at 90s to stay inside Codex's MCP-call timeout\n- v0.1.5 — `reply` chunks text >4000 chars across multiple Telegram messages (paragraph→line→word→hard cut), so long Codex outputs no longer fail with 400 Bad Request\n- v0.1.6 — Stop hook suppresses the \"turn complete\" ping when Codex sent a `reply` within the last 30s (the user already knows). Override via `CODEX_NOTIFY_SUPPRESS_MS` env (0 disables suppression)\n- v0.1.7 — typing indicator (re-sends `sendChatAction` every 4s between `wait_for_message` and `reply`, with a 5min ceiling) so a thinking Codex looks different from a hung one\n- v0.1.8 — silence watchdog `PreToolUse` hook — pings \"🟡 still working — N tool calls in, Xs since last reply\" when Codex has been silent past `CODEX_SILENCE_WATCHDOG_MS` (default 120000). Single ping per silence window — the hook resets its own clock so spam is impossible.\n- v0.1.9 — `Notification` hook relays error-flavored notifications (rate limit, API failure, timeout) to Telegram with a `⚠️ codex: …` prefix. Relay-all override via `CODEX_NOTIFY_RELAY_ALL=1`; disable via `CODEX_NOTIFY_RELAY_DISABLED=1`\n- v0.1.10 — `/stop` bot command sends Ctrl-C via tmux to interrupt the current Codex turn; `/restart` invokes `sudo 5dive agent restart <name>` so the systemd unit respawns the session in ~2s. Both gated on allowFrom\n- v0.1.11 — `/agents` lists sibling 5dive agents on the host (active/inactive, type, channel, marks self). Wraps `sudo 5dive agent list --json`\n- v0.2.0 — configurable knobs in `access.json`: `ackReaction` (emoji on every inbound, off by default), `textChunkLimit` (override the 4000-char chunker cap, range 500–4096), `dmPolicy` (allowlist/static — reserved for forward parity)\n- v0.2.1 — `notify-user` skill (`skills/notify-user/SKILL.md`) — Codex-adapted comms playbook covering cadence, the wait_for_message loop, files/images/reactions, the approval bridge, and security. Description trimmed under Codex's 1024-char SKILL.md limit.\n- v0.2.2 — quieter user-facing pings: silence-watchdog now says `⏳ still working…` instead of dumping `N tool calls in, Xs since last reply` (telemetry read like debug output). `notification-relay` drops transient `RetryState` payloads and, when the upstream hands us a Rust `Debug`-formatted `SessionNotification { ... }` blob, extracts the inner `reason:` field instead of forwarding the whole struct.\n- v0.2.3 — silence-watchdog backoff: first ping in a silence stretch trips at the base threshold (default 2 min); the 2nd ping needs ~20 min of additional silence, the 3rd+ needs ~30 min (cap). A real `reply` resets the counter. Stops the \"⏳ still working…\" message from feeling like a 2-minute heartbeat during long silent runs.\n- v0.2.4 — `PLUGIN_VERSION` now reads from `package.json` at startup instead of a hardcoded const, so `/ping` / `/status` / `setMyCommands` report the actual shipped version. Previous PATCH bumps shipped the code but `/status` kept showing `0.2.1`.\n- v0.2.5 — silence-watchdog base default raised from 2 min → 10 min. With backoff (1× / 10× / 15×), the cadence on a truly silent run is now ~10 min, then +100 min, then +150 min cap — far less surprise during normal back-and-forth where the user just sent a slash command 2 minutes ago. Override via `CODEX_SILENCE_WATCHDOG_MS` as before.\n- v0.2.6 — silence-watchdog is now **off by default**. With `notify-user` auto-seeded, the agent already acks + edits progress updates; the watchdog ping was a redundant heartbeat that just felt like noise. Opt back in with `CODEX_SILENCE_WATCHDOG_ENABLED=1`. (this)\n","readmeFilename":"README.md","_rev":"1-fa28c2f8e328f92dc0ce73efc3d47adf"}