M mctl-telegram
Developer reference

Developer reference

Tool catalogue, scopes, storage schema, and release checklist for the mctl-telegram MCP connector.

Add as a custom connector (ChatGPT Apps or Claude)

  1. ChatGPT Apps: open Settings → Apps, choose your draft app, then set the MCP server URL to https://tg-preview.mctl.ai/mcp. See landing → Install in ChatGPT for the full flow.
  2. Claude: open claude.ai → Customize → Connectors and click Add custom connector.
  3. Authentication: OAuth. Clients discover this server as its own authorization server via https://tg-preview.mctl.ai/.well-known/oauth-protected-resource and https://tg-preview.mctl.ai/.well-known/oauth-authorization-server.
  4. Complete the Telegram sign-in flow in your browser when prompted (phone → SMS code → 2FA if enabled).
  5. Message access (read/send) is granted by the encrypted MTProto session stored on this server. If the account is not yet provisioned, the connect screen explains the next step.

ChatGPT Apps setup

  1. Verify identity in the OpenAI Platform Dashboard (individual or business verification required for directory submission).
  2. Create or open a draft app under Settings → Apps in ChatGPT Developer Mode.
  3. Set MCP URL to https://tg-preview.mctl.ai/mcp and auth to OAuth.
  4. Run a read-only smoke test (list_dialogs) before submitting for review.
  5. Prepare submission URLs: homepage https://tg-preview.mctl.ai/, privacy https://tg-preview.mctl.ai/privacy, support [email protected].

Apps SDK readiness checklist

Available tools

ToolModeScope requiredNotes
list_dialogs read telegram:dialogs:read Inputs: limit (≤200, default 50), optional query.
get_unread_messages read telegram:messages:read Inputs: optional peer, limit (≤200).
get_messages read telegram:messages:read Full history of a specific peer, not just unread.
send_message write telegram:messages:send No mode argument — the gate decides. On tg.mctl.ai, ALLOW_SEND=false blocks real sends and returns a dry-run preview (sent=false, with dry_reason). A real send additionally requires identity scope and per-account send_enabled=true.
pin_message write telegram:messages:pin Inputs: peer, message_id, unpin (bool).
disconnect_telegram_account write any authenticated identity Soft-revokes your session — see Your controls.
delete_telegram_account write any authenticated identity Hard-deletes the encrypted session blob.
list_telegram_identities read admin:users Admin only. Roster of signed-in users with access tier and session state.
set_telegram_access write admin:users Admin only. Grant/revoke the client tier for a Telegram id.

Scopes and permissions

Scopes are derived from your Telegram identity. Operators on the admin allowlist (TG_LOGIN_ADMINS) get the full platform-admins set. Everyone else gets the client tier — the telegram:* tool scopes for their own account, without platform-admin capability. When AUTO_APPROVE_CLIENTS is enabled (open registration) every signed-in user is a client automatically; otherwise the client tier comes from the TG_LOGIN_CLIENTS allowlist or an operator grant. An operator can revoke any user with set_telegram_access.

Telegram identityScopes granted
Telegram id ∈ TG_LOGIN_ADMINSall telegram:* scopes plus admin:users
Telegram id ∈ TG_LOGIN_CLIENTStelegram:* scopes (dialogs/messages read, send, pin) — no admin:users
Any other authenticated Telegram idnone — every tool call returns 403 until the operator opts you in

For backward compatibility the server can still be run under AUTH_MODE=shared-hmac-legacy, in which case the original GitHub-group→scope mapping applies. The default mode is local-jwt.

What we store

RowContentsRetention
users telegram_login_id, telegram_username, telegram_display_name (synthetic github_login=tg:<id> kept for legacy joins). Legacy rows may still carry a real github_login. Until you call delete_telegram_account (cascades).
telegram_accounts.session_encrypted AES-256-GCM-sealed MTProto session blob, per-user HKDF-derived key. Until you call disconnect (marked revoked) or delete (row removed).
audit_logs Tool name, redacted peer reference, status, error. Never the message body, phone number, or session bytes. Retained for 90 days, then removed by the audit-log sweeper (window configurable via AUDIT_RETENTION_DAYS; set to 0 to keep indefinitely). Ask the operator if you need an earlier purge.

Your controls

Smoke-test from your terminal

Confirm the well-known and a JSON-RPC handshake before adding the connector:

curl -s https://tg-preview.mctl.ai/.well-known/oauth-protected-resource

curl -s -X POST https://tg-preview.mctl.ai/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-03-26","capabilities":{},
                 "clientInfo":{"name":"smoke","version":"0"}}}'

First-time Telegram login

Sign-in via a ChatGPT App or Claude connector authenticates you with Telegram OIDC. If the server has no MTProto session for your account yet, the connect flow continues straight into an in-browser "Enable message access" step: enter your phone number, type the login code Telegram sends you, and — if your account uses it — your two-step verification password. The server then persists an AES-256-GCM-encrypted MTProto session keyed to your Telegram identity, and the connector is ready — no operator CLI step required. The bundled mctl-telegram-login CLI remains available for operators as a fallback. Full details in the repo README.