Connect your users to Telegram
For agent platforms and MCP gateways that connect many users to https://tg.mctl.ai/mcp: how a client is admitted, what it may do, the limits it runs under, and how access ends. The limits and the CIMD status below are read from this deployment's running configuration.
Who this is for
A platform that lets its own users bring a Telegram account into an AI agent: one OAuth client on your side, one Telegram sign-in per user, each user's access confined to their own account. Individual users connecting Claude or ChatGPT need none of this page; the landing page and /docs cover them.
Admitting your client
A platform's host is approved by the operator once. After that, every one of its users connects without further setup on either side. There are two ways in:
Client ID Metadata Document (preferred)
- Your
client_idis an https URL on your own domain, and your redirect URIs come from the JSON document served there. You change callbacks by editing your document, not by asking us. - The document must name its own URL as
client_id, carry noclient_secret, list 1 to 20 httpsredirect_uris, and usetoken_endpoint_auth_methodnoneorprivate_key_jwt(RS256 or ES256, withjwks_urion the same host). - Fetches are bounded: 64 KiB body, 10 second timeout, and
Cache-Controlhonoured between 30 seconds and 24 hours. A document that cannot be fetched gives a retryableserver_error; a CIMD client is never silently downgraded to DCR. - For
private_key_jwt, the assertion'saudis the issuerhttps://tg.mctl.aiorhttps://tg.mctl.ai/oauth/token, lifetime at most 5 minutes,jtisingle-use. - On this deployment:
CIMD is accepted for approved hosts.
Discovery advertises it (
client_id_metadata_document_supported).
Dynamic Client Registration
POST /oauth/register(RFC 7591) with redirect URIs on an approved host. Redirects are https, or http on loopback only; custom URL schemes are refused.- Registration is limited to 10 attempts per minute per source IP. A registration is kept for up to 24 hours, and the oldest ones are dropped sooner once the server reaches its cap on dynamic registrations, so register when a user connects rather than once at install.
To have a host approved, write to [email protected] with the host, whether you use CIMD or DCR, and your expected number of users.
Endpoints and discovery
| Purpose | Endpoint |
|---|---|
| MCP (Streamable HTTP) | https://tg.mctl.ai/mcp |
| Protected resource metadata (RFC 9728) | https://tg.mctl.ai/.well-known/oauth-protected-resource |
| Authorization server metadata (RFC 8414) | https://tg.mctl.ai/.well-known/oauth-authorization-server |
| Authorize, token, revoke (RFC 7009), register | /oauth/authorize, /oauth/token, /oauth/revoke, /oauth/register |
Authorization code with PKCE S256 only. Users sign in with Telegram's own OpenID Connect provider; your platform never sees a phone number or a login code.
Scopes and consent
| Scope | Grants |
|---|---|
telegram:dialogs:read | List chats (list_dialogs) |
telegram:messages:read | Read, search and fetch media (get_messages, get_unread_messages, search_messages, prepare_get_media, get_media) |
telegram:messages:send | Send messages and media, edit, delete, forward and react, and the two-step draft_reply/send_reply on core-reply connections, all subject to the user's own consent below |
telegram:messages:pin | Pin messages, through a confirmation step |
account:manage | The user's own account: send consent, notifications, Local Bridge devices, disconnecting and deleting the account |
- Always name your scopes. A request that names scopes gets only those the user is entitled to; a request that omits
scopegets the user's full entitled set, including send, pin andaccount:manage. Read thescopein the token response rather than assuming you got what you asked for. Administrative scopes cannot be requested. - The tool list follows the grant.
tools/listshows only the tools the token's scopes allow, and a call to any other tool is refused as an unknown tool, so a read-only connection never sees the send tools. - Every account starts read-only. A send succeeds only when the scope is granted, the user has turned sending on themselves (by choosing Read + send when connecting, with
set_send_consentor on/telegram/connect/manage), and the deployment allows sending (it does on this deployment). With the scope but without the user's consent, or on a deployment that does not allow sending, a send returns a preview withsent=false. Your platform cannot turn sending on for a user. - Every tool call lands in the user's hash-chained audit log, which they can read and verify. See /security.
Restricted tool profiles
On request, the operator binds your client to a server-side tool profile, which can only remove tools from what the scopes allow and is enforced on every call, not only in tools/list: core-readonly (list, read, unread and search in one named chat, plus the user's own status tools) or core-reply (the same plus a two-step reply: draft_reply returns the exact recipient and text and sends nothing; send_reply takes only the server-issued confirmation). A binding matches your CIMD client_id or a redirect URI; ask at [email protected]. Details: docs/design/core-profiles.md.
Limits on this deployment
| Limit | Value |
|---|---|
| Tool calls per user | 30 per minute |
| Sends to one recipient | 20 per hour |
| Cleanup in one chat (delete, unreact, unpin) | 60 per hour |
| Access token lifetime | 1 hour |
| Refresh token lifetime | 30 days, rotated on every use; reusing a rotated token revokes the whole family |
| Media download | 12 MB per file |
Media upload (send_media) | 20 MiB per file |
Inline media in one read call (fetch_media=true) | 8 MiB in total |
| Read page size | 50 items by default, up to 200 (search: 20, up to 100) |
Limits are per Telegram user, not per platform, so one busy user does not slow down the rest. Telegram's own flood limits apply on top and surface as errors with a retry delay.
The tool surface is a contract
- Every tool carries
readOnlyHintanddestructiveHintannotations. The full surface, schemas included, is committed as docs/tool-descriptors.json. - A release cannot add, remove or change a tool without an approved entry in the change feed at /docs/product-updates; CI enforces it.
Reliability
| Objective (28-day window) | Target |
|---|---|
| Tool call availability | 99.5% |
| OAuth token endpoint availability | 99.9% |
| Read tool latency | p95 under 2 s, p99 under 5 s |
| Send latency | p95 under 4 s |
These are internal objectives with burn-rate alerting, not a contractual SLA. A canary checks OAuth discovery and a read-only tool call every ten minutes. The service runs as a single replica, so a deploy is a short interruption.
Errors your client will see
docs/troubleshooting.md lists the error families clients actually hit, with the fix for each: expired access tokens, a revoked Telegram session (AUTH_KEY_UNREGISTERED), Telegram flood limits, redirect URI refusals and transient Telegram failures.
Revocation and offboarding
- Your client revokes a user's grant with
POST /oauth/revoke, which ends that refresh-token family. Access tokens already issued expire on their own lifetime (1 hour). - A user can disconnect their Telegram session on
/telegram/connect/manage. Disconnecting through a client (disconnect_telegram_account,POST /api/account/disconnect) and deleting the account (delete_telegram_account,DELETE /api/account) needaccount:manage, so request it if users should be able to leave from inside your platform. - Deleting an account revokes, in the same transaction as the delete, every client's refresh tokens for that user, every worker token issued for the user's Telegram id, and every Local Bridge device with its credential. Worker and device credentials stop authenticating within 15 seconds, the revocation cache's refresh interval. Disconnecting ends the Telegram session but keeps the OAuth grant, so the user can reconnect without adding the connector again.
- When a user removes your connector, the server learns of it only if your client calls
/oauth/revoke; otherwise the grant stays valid until the refresh token expires or is revoked. - If an approved CIMD host is removed, its users' refreshes stop at the next token request.
Bring your own session: Local Bridge
For users or companies that will not leave a Telegram session on a hosted server, Local Bridge keeps it on the user's machine. The user installs mctl-telegram-local, signs in to Telegram locally and activates the device in a browser; your platform keeps the same endpoint and the same OAuth client. The server then relays MCP calls to that machine and sees the payloads in transit, so it is not end-to-end encryption. Local Bridge is in beta: the daemon must be running, and editing, deleting, forwarding, searching, reactions and the draft_reply/send_reply pair are not yet available in this mode.
Who runs this service
MCTL Assistant is an unofficial third-party connector using the Telegram API, and an independent open-source project, not affiliated with Telegram, Anthropic or OpenAI. The source is at github.com/mctlhq/mctl-telegram.
Contact
- Integration and host approval: [email protected]
- Security reports: see /security