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).
  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).