{"id":51254498,"url":"https://github.com/gotd/tgmcp","last_synced_at":"2026-06-29T09:01:56.833Z","repository":{"id":363782241,"uuid":"1263142449","full_name":"gotd/tgmcp","owner":"gotd","description":"MCP Server for Telegram","archived":false,"fork":false,"pushed_at":"2026-06-10T09:36:44.000Z","size":100,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T11:14:07.476Z","etag":null,"topics":["gotd","mcp","mcp-server","mtproto","telegram"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gotd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"polar":"gotd"}},"created_at":"2026-06-08T17:00:28.000Z","updated_at":"2026-06-10T09:36:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gotd/tgmcp","commit_stats":null,"previous_names":["gotd/tgmcp"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/gotd/tgmcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gotd%2Ftgmcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gotd%2Ftgmcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gotd%2Ftgmcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gotd%2Ftgmcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gotd","download_url":"https://codeload.github.com/gotd/tgmcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gotd%2Ftgmcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34919884,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gotd","mcp","mcp-server","mtproto","telegram"],"created_at":"2026-06-29T09:01:55.011Z","updated_at":"2026-06-29T09:01:56.825Z","avatar_url":"https://github.com/gotd.png","language":"Go","funding_links":["https://polar.sh/gotd"],"categories":[],"sub_categories":[],"readme":"# tgmcp\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"logo.svg\" alt=\"tgmcp logo\" width=\"200\"/\u003e\u003c/p\u003e\n\nA [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for\nTelegram, built on the [gotd](https://github.com/gotd/td) client. It lets an MCP\nclient (Claude Desktop, Claude Code, etc.) discover which channels have unread\nmessages, read those messages, and mark them as read.\n\nIt authenticates as a **user account** (not a bot), so it sees the same channels\nand unread state as the logged-in user.\n\n## Tools\n\n| Tool | Description |\n| --- | --- |\n| `list_unread_channels` | List channels and supergroups that currently have unread messages, with unread counts. |\n| `read_channel_unread` | Read the unread messages of a channel (by `@username` or numeric ID), newest first. Reading does **not** mark them as read. |\n| `mark_channel_read` | Mark all messages in a specific channel or supergroup as read. |\n| `mark_all_channels_read` | Mark every unread channel and supergroup as read in one call. |\n\n## How it works\n\nTo avoid `FLOOD_WAIT`, the server does **not** re-fetch the dialog list on every\ntool call. Instead, mirroring [tdlib](https://github.com/tdlib/td)'s strategy:\n\n- The dialog list is loaded **once** (batched at 100 per request) and served\n  from an in-memory cache.\n- Per-dialog unread counts are kept live from the Telegram **update stream** via\n  gotd's [`updates.Manager`](https://pkg.go.dev/github.com/gotd/td/telegram/updates),\n  which recovers gaps with `getDifference`.\n- The dialog cache, the update state (`pts/qts/date/seq`), and channel access\n  hashes are **persisted to bbolt** (`\u003csession\u003e/updates.bolt`), so a restart\n  reconciles incrementally instead of re-listing every dialog.\n\n## Setup\n\n1. Get `APP_ID` and `APP_HASH` from \u003chttps://my.telegram.org/apps\u003e.\n2. Configure credentials, either via environment variables or a `.env` file:\n\n   ```sh\n   cp .env.example .env\n   # edit .env\n   ```\n\n3. Build:\n\n   ```sh\n   go build -o tgmcp .\n   ```\n\n4. Log in once. This shows a **QR code** to scan from your Telegram app\n   (Settings → Devices → Link Desktop Device) and prompts for the 2FA password\n   if you have one set. It stores a reusable session under `./session/`:\n\n   ```sh\n   ./tgmcp auth\n   ```\n\n## Configuration\n\n| Variable | Required | Default | Description |\n| --- | --- | --- | --- |\n| `APP_ID` | yes | — | App ID from my.telegram.org. |\n| `APP_HASH` | yes | — | App hash from my.telegram.org. |\n| `TG_PHONE` | no | — | Phone number; only used to name the session subfolder. |\n| `TG_SESSION_DIR` | no | `session` | Directory for the session and state database. |\n| `MCP_ADDR` | no | `127.0.0.1:8080` | Address for the MCP HTTP server. |\n| `LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, or `error`. |\n\n## Running\n\nThe server speaks MCP over **HTTP** (streamable transport):\n\n```sh\n./tgmcp serve\n```\n\nIt loads the session created by `tgmcp auth` and never prompts; if the session\nis missing or expired it exits and asks you to run `tgmcp auth` again.\n\n### Claude Code / Claude Desktop config\n\nPoint your MCP client at the HTTP endpoint (adjust the address to `MCP_ADDR`):\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram\": {\n      \"type\": \"http\",\n      \"url\": \"http://127.0.0.1:8080\"\n    }\n  }\n}\n```\n\n## Notes\n\n- Logs are written as JSON to **stderr**, so journald (or any supervisor)\n  captures them. Set `LOG_LEVEL=debug` to see every MTProto call and tool\n  invocation.\n- Unread detection compares each message ID against the dialog's\n  `read_inbox_max_id`; messages newer than that boundary are returned.\n- After a long disconnect, a too-long difference is resynced automatically: a\n  single channel via `messages.getPeerDialogs`, or the whole list via a full\n  re-bootstrap. Deleting `\u003csession\u003e/updates.bolt` forces a clean re-bootstrap on\n  the next start.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgotd%2Ftgmcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgotd%2Ftgmcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgotd%2Ftgmcp/lists"}