MCTL
Developer reference

Developer reference

Connection details, tool catalogue, scopes, and account controls for the MCTL MCP server.

On this page: connect · tools · scopes · storage · controls · smoke test · Local Bridge

Connect a client

  1. Claude (claude.ai, desktop, mobile): Customize → Connectors → Add custom connector, name MCTL Assistant, MCP server URL https://tg.mctl.ai/mcp, default sign-in settings. Team and Enterprise Owners add it under Organization settings → Connectors. Step by step: Connect in Claude.
  2. Claude Code: claude mcp add --transport http --scope user mctl https://tg.mctl.ai/mcp, then /mcp to sign in.
  3. ChatGPT and other clients: add https://tg.mctl.ai/mcp as a remote MCP server with OAuth; in ChatGPT that is Plugins → Personal → Add → Add custom MCP server, name MCTL Assistant, Create as a plugin. Per-client notes: Other MCP clients. MCTL is not listed in the public plugin directory; adding it as your own plugin is the supported path. Cursor, VS Code and Windsurf are not yet tested against tg.mctl.ai.

OAuth

  • Discovery: MCTL is its own authorization server. Clients find it through https://tg.mctl.ai/.well-known/oauth-protected-resource and https://tg.mctl.ai/.well-known/oauth-authorization-server.
  • Client registration: tg.mctl.ai supports both Client ID Metadata Documents (CIMD — what Claude uses by default) and Dynamic Client Registration, so no client ID or secret has to be entered by hand. A self-hosted deployment advertises CIMD only when it is enabled; /platforms shows this deployment's live setting. Authorization code with PKCE S256 only. Platforms that connect many users: see /platforms.

First sign-in

  1. Sign in with Telegram in the browser window your client opens (Telegram's own OpenID Connect login). On Telegram’s sign-in screen, turn on Allow messages so the MCTL login bot can send you setup help and the notifications you choose. Telegram leaves it off unless you switch it on, and you can sign in either way.
  2. Permissions: if the server has no Telegram session for you yet, choose Read only (the default) or Read + send. You can change it later with set_send_consent or on the manage page.
  3. Phone number: enter it, then the login code Telegram sends to your Telegram app, and your two-step verification password if you use one. The server stores an AES-256-GCM-encrypted MTProto session keyed to your Telegram identity, and the connector is ready.

Available tools

A connection sees only the tools its scopes allow: tools/list is filtered by the token, and a call to any other tool is refused as an unknown tool. The full surface, with input schemas and readOnlyHint/destructiveHint annotations, is committed as docs/tool-descriptors.json, and every change to it is announced in product updates.

ToolModeScope requiredNotes
list_dialogsreadtelegram:dialogs:readYour chats with type, title, username and unread count. limit (default 50, up to 200), optional query.
get_messagesreadtelegram:messages:readRecent history of one chat, not just unread.
get_unread_messagesreadtelegram:messages:readUnread messages, optionally for one chat. limit up to 200.
search_messagesreadtelegram:messages:readquery, optional peer, limit (default 20, up to 100), min_date/max_date (inclusive UTC, RFC 3339 or YYYY-MM-DD). Newest first.
prepare_get_media, get_mediareadtelegram:messages:readInspect a message's media, then download it with the returned confirmation_id.
send_message, prepare_send_messagewritetelegram:messages:sendSend a text message; prepare_send_message snapshots it for confirmation first. No mode argument — the send gate decides: a real send needs ALLOW_SEND=true on the server (on for tg.mctl.ai), the scope, and your own send_enabled=true (a new read-only account has send_enabled=false). Otherwise it returns a preview (sent=false with dry_reason).
send_mediawritetelegram:messages:sendPhoto, video, document, animation or voice note with an optional caption. Same send gate.
edit_message, delete_messages, forward_messages, set_reactionwritetelegram:messages:sendEdit your own message, delete, forward between chats, add or remove a reaction. Same send gate.
prepare_pin_message, pin_messagewritetelegram:messages:pinPin or unpin after a confirmation step. Needs pin rights in the chat and the same send gate.
draft_reply, send_replywritetelegram:messages:sendOnly on connections bound to the restricted core-reply profile. draft_reply sends nothing and returns the exact recipient and text; send_reply takes only the server-issued confirmation and sends once.
get_my_identity, get_my_send_status, get_my_audit_logreadany signed-in connectionWho you are signed in as, whether a send would really be delivered, and your own audit log.
set_send_consentwriteaccount:manageTurn real sending on or off for your own account.
get_my_notification_preferences, set_my_notification_preferencesread writeaccount:manageProduct update, maintenance and security notices from the login bot.
disconnect_telegram_account, delete_telegram_accountwriteaccount:manageSoft revoke or hard delete — see Your controls.
revoke_local_bridge_devicewriteaccount:manageRevoke one of your Local Bridge devices and evict a live daemon. See owner controls.

Operator tools (admin:users, admin:users:read) and broadcast tools (admin:broadcast) are listed in the descriptor file and are never shown to a regular user's connection.

Scopes and permissions

Scopes come from your Telegram identity. On tg.mctl.ai registration is open, so every signed-in user is in the client tier (a self-hosted deployment may instead require an operator grant, see below) and gets the scopes below for their own account. A client asks for a subset by naming scopes in the authorization request; omitting scope grants the full entitled set. Administrative scopes cannot be requested.

ScopeGrants
telegram:dialogs:readList chats
telegram:messages:readRead, search and download media
telegram:messages:sendSend, edit, delete, forward and react — subject to your own send consent
telegram:messages:pinPin and unpin, through a confirmation step
account:manageYour own account: send consent, notifications, Local Bridge devices, disconnect and delete

An operator can also bind a client to a restricted tool profile, which can only remove tools: core-readonly (reads only) or core-reply (reads plus the two-step draft_reply/send_reply). The design is in docs/design/core-profiles.md.

Self-hosting: operators on TG_LOGIN_ADMINS also get admin:users (and admin:broadcast when listed in BROADCAST_OPERATORS); TG_LOGIN_LOOKUP_ADMINS get only the read-only admin:users:read. Without AUTO_APPROVE_CLIENTS, the client tier comes from TG_LOGIN_CLIENTS or an operator grant, and an operator can revoke any user with set_telegram_access.

What we store

  • Telegram session: an AES-256-GCM-sealed MTProto session blob with a per-user HKDF-derived key, kept until you disconnect (marked revoked) or delete (removed).
  • Audit log: tool name, redacted peer reference, status and error — never the message body, phone number, or session bytes. Kept for 90 days by default.
  • Account row: your Telegram id and access tier. Deleting your account clears your username, names and last-seen time; the id stays so a later sign-in is matched and an authorization code issued before the delete cannot be used after it.

The complete inventory, with every table and its retention, is in the privacy policy.

Your controls

  • Disconnect (soft): call MCP tool disconnect_telegram_account, or POST /api/account/disconnect, from a connection granted account:manage; or, on deployments with the Telegram manage page enabled, sign in at /telegram/connect/manage in a browser and press Disconnect, which works whatever a connector was narrowed to. Marks the session revoked and immediately tears down the in-memory MTProto client.
  • Delete (hard): call MCP tool delete_telegram_account, or DELETE /api/account, from a connection granted account:manage. Removes the encrypted session blob and all per-account metadata, and revokes every credential issued for the account: every OAuth client's refresh tokens, every worker token, and every Local Bridge device with its credential.
  • Status: GET /api/account returns connection state, display name, and the per-account send_enabled flag.
  • Audit log: get_my_audit_log MCP tool or GET /api/account/audit — newest-first, RFC3339 before cursor for keyset pagination.
  • Verify your audit chain: GET /api/account/audit/verify recomputes the SHA-256 hash chain over your rows and reports {ok, verified} or {ok:false, first_bad_id, reason}.
  • Revoke from Telegram's side: Telegram app → Settings → Devices → close the session named after this app. This will not delete data here but will sever the MTProto link Telegram-side.
  • Read-only and device credentials: worker tokens and Local Bridge device credentials never carry account:manage, so they cannot disconnect or delete the account they were issued for. The refusal is a 403 on the HTTP paths, the tool is not listed over MCP, and every refusal is recorded in your audit log.

Smoke-test from your terminal

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

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

curl -s -X POST https://tg.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"}}}'

Local Bridge (beta)

Local Bridge is an optional mode in which the Telegram session lives on a machine you control and this server forwards each tool call to a daemon running there, instead of opening Telegram itself. It costs you a machine that stays on, so it is not part of the standard install path — but a new account can be provisioned so this server never holds a session at all, and turning it on is self-service: init, login, activate, daemon. Migrating an existing hosted account into this mode still uses set_account_mode (support, not onboarding).