tg-mcp
Provides read-only tools to fetch daily digests, period summaries, search messages, and get message context from specified Telegram chats and channels using the Telegram API via GramJS.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tg-mcpget today's digest from the work channel"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tg-mcp
Telegram digest MCP server that logs into a Telegram user account and works only with selected Telegram chats and channels. Read access is the default; source management is an explicit authenticated capability.
Current shape
Node.js 22 ESM service.
Express + MCP Streamable HTTP endpoint at
/mcp.Optional OAuth 2.1 protected MCP endpoint at
/tg-mcp/oauth-mcp.REST/OpenAPI fallback under
/tg-mcp/apiand/tg-mcp/openapi.json.MongoDB storage.
GramJS-based Telegram user account sync CLI.
Optional OpenAI Audio Transcriptions pipeline for Telegram voice/audio messages.
Optional read-only Telegram slash bot.
MCP prompts:
daily_telegram_digestsearch_telegram
Read-only MCP tools:
list_sourcesget_sync_statusget_audio_transcription_statusget_daily_digestget_period_summarysearch_telegram_messagesget_message_contextget_action_itemsget_source_summary
Optional authenticated owner media tools:
transcribe_source_audiolist_source_imagesget_telegram_images
Optional authenticated owner MCP tools:
send_telegram_messageenable_sourcedisable_sourceset_source_tagssync_sourceget_source_settingsupdate_source_settings
Related MCP server: Telegram MCP Server
Local development
npm ci
npm run cli -- setup-env
npm test
npm startHealth:
curl http://127.0.0.1:3010/healthREST/OpenAPI fallback:
curl http://127.0.0.1:3010/tg-mcp/openapi.json
curl "http://127.0.0.1:3010/tg-mcp/api/sync/status"
curl "http://127.0.0.1:3010/tg-mcp/api/transcriptions/status"
curl "http://127.0.0.1:3010/tg-mcp/api/digest/daily?timelineLimit=80"
curl "http://127.0.0.1:3010/tg-mcp/api/sources/<sourceId>/summary?date=2026-07-09"
curl "http://127.0.0.1:3010/tg-mcp/api/digest/daily?sourceQuery=project"
curl "http://127.0.0.1:3010/tg-mcp/api/digest/daily?refresh=true"
curl "http://127.0.0.1:3010/tg-mcp/api/search?query=release"If APP_AUTH_TOKEN is set, pass Authorization: Bearer <token> for REST and MCP calls.
When NODE_ENV=production, the HTTP service refuses to start without APP_AUTH_TOKEN unless ALLOW_UNAUTHENTICATED=true is set explicitly. Keep that override only for private tests.
ChatGPT Web developer-mode test
ChatGPT Web developer-mode apps do not accept a custom static bearer token for MCP. Authenticated MCP apps should implement OAuth 2.1. For a private read-only test, keep the main /mcp endpoint protected and expose a second no-auth endpoint with a long random path:
CHATGPT_MCP_PATH=/tg-mcp/chatgpt-mcp-<long-random-slug>Then paste this URL into ChatGPT Web as the developer-mode MCP server URL:
https://celticspear.com/tg-mcp/chatgpt-mcp-<long-random-slug>Do not publish that URL. Anyone who knows it can call the read-only MCP tools for the selected Telegram sources. The no-auth path never exposes disabled source metadata or source-management tools, even when owner management is enabled elsewhere. For a shared or published app, use the OAuth endpoint below.
OAuth 2.1 MCP endpoint
tg-mcp can act as an OAuth protected resource server for ChatGPT. It deliberately does not implement login, consent, token issuance, client registration, or refresh tokens: use an established external identity provider that supports authorization code + PKCE and publishes OAuth/OIDC discovery metadata.
Configure the resource server:
OAUTH_ENABLED=true
OAUTH_MCP_PATH=/tg-mcp/oauth-mcp
OAUTH_RESOURCE=https://celticspear.com/tg-mcp/oauth-mcp
OAUTH_ISSUER=https://<your-idp-issuer>
OAUTH_JWKS_URL=https://<your-idp-issuer>/<jwks-path>
OAUTH_JWT_ALGORITHMS=RS256,ES256
OAUTH_ALLOWED_SUBJECTS=<your-exact-idp-sub>
OAUTH_RESOURCE_DOCUMENTATION=https://<your-docs-url>The IdP must echo the OAuth resource parameter and issue an expiring JWT access token with an audience exactly equal to OAUTH_RESOURCE. Tokens must contain sub, scope or scp, and preferably client_id or azp. Configure ChatGPT's callback URL shown in the app/connector management UI and let the IdP expose its authorization/token endpoints through discovery.
Available scopes:
telegram:read: enabled-source lists, digests, search, summaries, status, and owner image List/Get whenMCP_IMAGE_TOOLS_ENABLED=true.telegram:sources:read: disabled-source catalog and source settings.telegram:sources:manage: enable/disable, tags, and settings mutations.telegram:sync:run: exact bounded manual sync and manual audio transcription.telegram:messages:send: send a plain-text or Rich Text message to Saved Messages.
The OAuth transport always requires telegram:read. Each privileged tool checks its additional scopes against the current request token, including after a session has been initialized. Missing scopes return an MCP mcp/www_authenticate challenge so ChatGPT can request authorization again.
Discovery metadata is published at both:
https://celticspear.com/.well-known/oauth-protected-resource
https://celticspear.com/.well-known/oauth-protected-resource/tg-mcp/oauth-mcpMCP_SOURCE_MANAGEMENT_ENABLED=true is required before privileged source
tools are registered. Manual transcription and image delivery are controlled
independently by MCP_MANUAL_TRANSCRIPTION_ENABLED and
MCP_IMAGE_TOOLS_ENABLED. Keep APP_AUTH_TOKEN for admin, REST, CLI setup,
and the legacy /mcp endpoint; OAuth protects only OAUTH_MCP_PATH. See the
implementation and IdP rollout checklist in
docs/oauth-scopes-plan.md.
Telegram setup
Fill these values in .env or /srv/tg-mcp/shared/.env:
TELEGRAM_API_ID=
TELEGRAM_API_HASH=
TELEGRAM_SESSION_FILE=/srv/tg-mcp/shared/sessions/telegram.session
ALLOWED_SOURCE_IDS=Create or update the env file:
npm run cli -- setup-env --set TELEGRAM_API_ID=<api_id> --set TELEGRAM_API_HASH=<api_hash>For the VPS layout:
export TELEGRAM_API_ID=<api_id>
export TELEGRAM_API_HASH=<api_hash>
npm run cli -- setup-env --production --env-path /srv/tg-mcp/shared/.env --from-env TELEGRAM_API_ID --from-env TELEGRAM_API_HASHExisting secrets and safety allowlists (ALLOWED_SOURCE_IDS, OAUTH_ALLOWED_SUBJECTS) are preserved unless you pass a new value with --set or --from-env. The command writes mode 0600, creates a backup before overwriting an existing file, and generates APP_AUTH_TOKEN when it is missing.
List available sources:
npm run cli -- login
npm run cli -- doctor --telegram
npm run cli -- list-sourcesSave the source list into MongoDB, then enable selected chats/channels:
npm run cli -- refresh-sources
npm run cli -- find-sources project
npm run cli -- select-source "Project Alpha" --tag work
npm run cli -- syncSend one plain-text message to the authorized account's Saved Messages:
npm run cli -- send-message "Text for Saved Messages"Send an interactive Rich Text checklist from the authorized user account:
npm run cli -- send-message $'- [ ] Open task\n- [x] Completed task' --rich-textsend-message uses the existing non-interactive Telegram session, does not
connect to MongoDB, and currently has no option for selecting another chat or
group. Plain text is sent literally without Markdown parsing and must contain
between 1 and 4096 characters. --rich-text sends Telegram Rich Markdown using
the account's user session and supports up to 32768 characters. Rich Text is a
Telegram Premium feature; checklist rows use - [ ] and - [x].
Useful variants:
npm run cli -- disable-source <id>
npm run cli -- set-source-tags <id> --tag work --tag project-x
npm run cli -- enable-source <id> --tag work
npm run cli -- get-source-settings <id>
npm run cli -- update-source-settings <id> --sync-interval-seconds 900 --history-depth-days 30 --priority 80
npm run cli -- update-source-settings <id> --include-media false --include-replies true --include-forwarded-posts false
npm run cli -- update-source-settings <id> --priority inherit
npm run cli -- db-sources --include-disabled
npm run cli -- sync --source-id <id> --limit 100
npm run cli -- backfill --days 7 --limit 1000
npm run cli -- transcription-status --source-id <id>
npm run cli -- transcribe-audio --source-id <id> --limit 1
npm run cli -- retry-failed-transcriptions --source-id <id> --limit 10tg_sources.enabled is the operational source of truth. If ALLOWED_SOURCE_IDS is set, it is an additional hard server ceiling: a DB-enabled source outside that list cannot be synchronized, including through CLI, admin, or MCP manual sync. Run refresh-sources before enabling a newly discovered source.
Tags are normalized to lowercase. set-source-tags replaces tags by default; use --mode add or --mode remove for incremental changes.
Disabling a source stops future sync and excludes it from normal search/digests, but it does not delete already stored messages. To delete them, first disable the source and then run the CLI-only destructive operation:
npm run cli -- purge-source-data <id> --forcelogin is interactive and writes the Telegram session file. Later commands reuse that session and should not prompt unless Telegram requires reauthorization.
Check readiness:
npm run cli -- doctor
npm run cli -- doctor --telegram
npm run cli -- doctor --env-path /srv/tg-mcp/shared/.envdoctor returns machine-readable checks plus nextSteps with the next safe commands for the current setup state. doctor --telegram also performs a non-interactive authorization check with the existing session file.
Every CLI command that reads runtime config accepts --env-path PATH, and the service also honors TG_MCP_ENV_FILE. This is useful on the VPS because production secrets live in /srv/tg-mcp/shared/.env, outside the release checkout.
Admin operations
Authenticated admin endpoints are available for operations that should not be exposed as MCP tools:
curl -X POST http://127.0.0.1:3010/admin/sources/refresh \
-H "Authorization: Bearer <APP_AUTH_TOKEN>"
curl -X POST http://127.0.0.1:3010/admin/sources/select \
-H "Authorization: Bearer <APP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"query":"Project Alpha","tags":["work"]}'
curl -X POST http://127.0.0.1:3010/admin/sync \
-H "Authorization: Bearer <APP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"sourceIds":["<sourceId>"],"limit":100}'Use {"backfillDays":7} with /admin/sync to bypass the incremental cursor for a historical import. Source management endpoints also support /admin/sources/<sourceId>/enable, /admin/sources/<sourceId>/disable, /admin/sources/<sourceId>/tags, and GET/PATCH /admin/sources/<sourceId>/settings. Endpoints that talk to Telegram use the existing session file and never prompt.
Example settings patch with optimistic concurrency:
curl -X PATCH http://127.0.0.1:3010/admin/sources/<sourceId>/settings \
-H "Authorization: Bearer <APP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"settings":{"syncIntervalSeconds":900,"priority":80},"expectedVersion":2}'All successful source mutations are appended to tg_source_audit with actor, action, before/after state, and timestamp.
Background sync
The HTTP service can run a safe background sync loop after the Telegram session file exists:
TELEGRAM_SYNC_ENABLED=true
TELEGRAM_SYNC_INTERVAL_SECONDS=300
TELEGRAM_SYNC_ON_START=true
SOURCE_DEFAULT_SYNC_INTERVAL_SECONDS=300
SOURCE_DEFAULT_HISTORY_DEPTH_DAYS=30
SOURCE_SCHEDULER_POLL_INTERVAL_SECONDS=30If credentials, session, or selected sources are missing, the worker logs a warning and waits for the next interval. It never prompts from the systemd service.
The scheduler polls for due sources, orders them by nextSyncAt and per-source priority, and uses a lease so background, admin, CLI, and MCP sync cannot overlap for the same source. TELEGRAM_SYNC_INTERVAL_SECONDS remains a compatibility fallback for SOURCE_DEFAULT_SYNC_INTERVAL_SECONDS.
Normal sync is incremental: each source tracks lastSyncedMessageId and later runs request only newer Telegram messages. backfill --days N intentionally bypasses that cursor for historical imports, but is clamped to the source's historyDepthDays setting.
Per-source settings:
syncIntervalSeconds:60..604800, ornull/CLIinheritfor the server default.historyDepthDays:1..3650, or inherit. This limits import depth and is not retention.includeMedia: keeps supported audio and image metadata and permits supported audio transcription jobs; text captions remain searchable when media metadata is disabled.includeReplies: imports or skips reply messages.includeForwardedPosts: imports or skips forwarded messages.priority:0..100; higher values run first when multiple sources are due.
To expose owner write tools on the bearer-protected MCP endpoint, explicitly enable:
MCP_SOURCE_MANAGEMENT_ENABLED=true
MCP_MESSAGE_SENDING_ENABLED=true
MCP_MANUAL_TRANSCRIPTION_ENABLED=true
MCP_IMAGE_TOOLS_ENABLED=trueThese flags have no effect on the no-auth CHATGPT_MCP_PATH; that endpoint
remains read-only. On the OAuth endpoint, source tools additionally require
telegram:sources:read plus telegram:sources:manage, manual sync and
transcription require telegram:sync:run, and send_telegram_message requires
telegram:messages:send.
When enabled, send_telegram_message accepts text and an optional format.
The default plain_text sends text literally. rich_text sends Telegram Rich
Markdown from the authenticated user account, so task-list rows such as
- [ ] Open and - [x] Done are owned by that user and remain interactive.
The tool is non-idempotent: clients must not automatically retry an ambiguous
failure. Selecting another chat or group is not supported yet.
Check data freshness:
curl "http://127.0.0.1:3010/tg-mcp/api/sync/status?staleAfterHours=24"The MCP tool get_sync_status exposes the same source freshness state to ChatGPT so it can say when data is missing or stale before summarizing.
Audio transcription
Telegram voice notes and audio files are synced as messages even when they do not have a text caption. The sync stores audio metadata in MongoDB and queues them with transcription.status=pending. Once transcribed, the transcript is stored as transcriptText on the same tg_messages document, included in Mongo text search, message context, daily digests, and action detection.
Enable the OpenAI Audio Transcriptions worker:
OPENAI_API_KEY=<openai_api_key>
OPENAI_TRANSCRIPTION_ENABLED=true
OPENAI_TRANSCRIPTION_MODEL=gpt-4o-mini-transcribe
AUDIO_TRANSCRIPTION_SOURCE_IDS=<saved_messages_source_id>
AUDIO_TRANSCRIPTION_INTERVAL_SECONDS=3600
AUDIO_TRANSCRIPTION_BATCH_SIZE=1
AUDIO_TRANSCRIPTION_WORK_DIR=/srv/tg-mcp/shared/audio-workThe background worker only claims jobs from explicitly configured transcription sources: AUDIO_TRANSCRIPTION_SOURCE_IDS or AUDIO_TRANSCRIPTION_SOURCE_TAGS. If neither is set, it will not process pending audio from arbitrary enabled Telegram chats. When background sync stores at least one audio/voice message, the service immediately runs one bounded transcription pass; AUDIO_TRANSCRIPTION_INTERVAL_SECONDS remains a safety polling interval. Keep AUDIO_TRANSCRIPTION_BATCH_SIZE=1 to cap each pass to one OpenAI request.
gpt-4o-mini-transcribe is the default for clean personal recordings. Use gpt-4o-transcribe when recordings are noisy, terminology-heavy, or require the highest available transcription quality.
The worker downloads Telegram media into the work directory, submits it to the OpenAI Audio Transcriptions API, stores only the transcript/metadata by default, and removes the temporary audio file. Completed items have transcriptText plus transcription.status=done, so later runs do not claim the same file again. Files larger than AUDIO_TRANSCRIPTION_MAX_FILE_BYTES are split with ffmpeg when AUDIO_TRANSCRIPTION_SPLIT_LARGE_FILES=true.
Manual operations:
npm run cli -- transcription-status
npm run cli -- transcribe-audio --limit 1
npm run cli -- transcribe-audio --source-id <saved_messages_source_id> --limit 1
npm run cli -- retry-failed-transcriptions --limit 10Authenticated admin endpoints:
curl http://127.0.0.1:3010/admin/transcriptions/status \
-H "Authorization: Bearer <APP_AUTH_TOKEN>"
curl -X POST http://127.0.0.1:3010/admin/transcriptions/run \
-H "Authorization: Bearer <APP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"sourceIds":["<saved_messages_source_id>"],"limit":1}'
curl -X POST http://127.0.0.1:3010/admin/transcriptions/retry-failed \
-H "Authorization: Bearer <APP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"limit":10}'The read-only REST endpoint /tg-mcp/api/transcriptions/status and MCP tool get_audio_transcription_status expose counts for selected sources. Search and digest tools automatically use completed transcripts.
Manual MCP transcription
When MCP_MANUAL_TRANSCRIPTION_ENABLED=true, authenticated owner/OAuth MCP
clients can call:
transcribe_source_audio(sourceId="<exact-enabled-source-id>", limit=5)The command processes only pending voice/audio messages from that exact
source. It does not call Telegram sync, change source settings, or add the
source to AUDIO_TRANSCRIPTION_SOURCE_IDS /
AUDIO_TRANSCRIPTION_SOURCE_TAGS. Call sync_source first when fresh or
historical audio is required.
OAuth requires telegram:read telegram:sync:run. The command is never
registered on the no-auth CHATGPT_MCP_PATH.
Telegram images
Telegram photos plus JPEG, PNG, and WebP documents are stored as message
metadata when the source has includeMedia=true. The server does not run OCR,
vision models, embeddings, or automatic image tagging.
Enable owner image tools and the fixed-retention file cache:
MCP_IMAGE_TOOLS_ENABLED=true
MCP_IMAGE_LIST_MAX_LIMIT=100
MCP_IMAGE_GET_MAX_ITEMS=5
MCP_IMAGE_MAX_FILE_BYTES=10485760
MCP_IMAGE_MAX_TOTAL_BYTES=26214400
MCP_IMAGE_CACHE_MAX_ITEMS_PER_SYNC=100
IMAGE_CACHE_DIR=/srv/tg-mcp/shared/image-cache
IMAGE_CACHE_RETENTION_DAYS=30
IMAGE_CACHE_CLEANUP_INTERVAL_SECONDS=3600IMAGE_CACHE_RETENTION_DAYS is clamped to 1..30. A cache entry expires at a
fixed time calculated when the file is written; List/Get calls do not extend
it. The startup/periodic janitor removes expired files and Mongo cache metadata.
Image bytes live only in IMAGE_CACHE_DIR, outside the web root. MongoDB stores
the Telegram reference, relative path, MIME type, size, SHA-256, cachedAt,
and expiresAt.
Manual workflow:
sync_source(
sourceIds=["<exact-enabled-source-id>"],
backfillDays=30,
cacheImages=true,
imageLimit=100
)
list_source_images(
sourceId="<exact-enabled-source-id>",
limit=20
)
get_telegram_images(
sourceId="<exact-enabled-source-id>",
messageIds=[123, 124]
)sync_source does not download image bytes unless cacheImages=true.
get_telegram_images reads unexpired local cache entries first and downloads
only cache misses from Telegram. It returns a text context block followed by
real MCP image content for each successful item. The model using the MCP app
can then inspect the pixels itself.
Images without captions cannot be found semantically by this server. Use
list_source_images with a bounded date range and pass selected message ids to
get_telegram_images. Albums retain Telegram groupedId.
If an image is removed from Telegram after it was cached, the local copy
remains available only until its fixed expiry. There is no permanent archive.
Both image tools require telegram:read, exact enabled sources, and the
ALLOWED_SOURCE_IDS ceiling when configured. They are never registered on the
no-auth CHATGPT_MCP_PATH.
Digest cache
Daily, period, and source summaries are cached in tg_digests. The cache key includes the period, timezone, source filters, timeline options, and selected source sync state, so a later Telegram sync naturally invalidates stale summaries.
Use refresh=true in REST calls, or refresh: true in MCP tool arguments, to force recomputation from stored messages.
Optional Telegram slash bot
The HTTP service can also run a small read-only Telegram bot for quick checks against the same selected data:
TELEGRAM_BOT_ENABLED=true
TELEGRAM_BOT_TOKEN=<bot token from BotFather>
TELEGRAM_BOT_ALLOWED_CHAT_IDS=<your chat id or comma-separated ids>
TELEGRAM_BOT_TIMEZONE=Europe/ChisinauSupported commands:
/digest_today [source]
/digest_week [source]
/search <query>
/actions [source]
/sources [query]TELEGRAM_BOT_ALLOWED_CHAT_IDS is optional, but recommended. The bot never sends Telegram messages on behalf of the synced user account; it only replies with digests/search results from MongoDB.
VPS quick deploy
The target server already has Apache, MongoDB, and bundled Node.js.
Expected layout:
/srv/tg-mcp/
repo.git/
releases/
shared/
.env
image-cache/
logs/
sessions/
node/
current -> releases/<release>Deploy code:
ops/deploy.shRun a VPS preflight from a checkout or release directory:
ENV_FILE=/srv/tg-mcp/shared/.env ops/preflight.sh
CHECK_TELEGRAM=true ENV_FILE=/srv/tg-mcp/shared/.env ops/preflight.shThe preflight checks Node.js, git state, tests, and doctor against the selected env file. CHECK_TELEGRAM=true additionally verifies the existing Telegram session without prompting.
Run a smoke test after the HTTP service is running:
AUTH_TOKEN=<APP_AUTH_TOKEN> BASE_URL=http://127.0.0.1:3010 ops/smoke.sh
AUTH_TOKEN=<APP_AUTH_TOKEN> BASE_URL=https://celticspear.com ops/smoke.shThe smoke test checks /health, OpenAPI, REST sync status, and an MCP initialize request.
Install systemd service:
sudo ops/install-systemd-service.shInstall Apache /mcp proxy:
sudo ops/install-apache-proxy.shInstall log rotation for /srv/tg-mcp/shared/logs/*.log:
sudo ops/install-logrotate.shThat installer exposes:
https://celticspear.com/mcp
https://celticspear.com/tg-mcp/oauth-mcp
https://celticspear.com/.well-known/oauth-protected-resource
https://celticspear.com/tg-mcp/openapi.json
https://celticspear.com/tg-mcp/api/...This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceA read-only Telegram MCP server that retrieves messages from your DMs, groups, and channels, enabling Claude to generate executive briefings from Telegram conversations.Last updatedMIT
- Flicense-qualityBmaintenanceModel Context Protocol server for Telegram. Let AI read, search, send, and forward your Telegram messages.Last updated52
- Alicense-qualityCmaintenanceA read-only MCP server that lets AI agents read personal Telegram chats from an allowlist of folders, with no send/edit/delete capability.Last updated28MIT
- Flicense-qualityCmaintenanceAn MCP server that connects to a Telegram group chat, persists messages to a local SQLite database, and exposes tools to search, retrieve, and send messages via SSE.Last updated
Related MCP Connectors
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Read-only MCP server for Robinhood Chain token discovery, research, and due diligence via GMGN.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/s4relok/tg-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server