Multi-tenant Telegram gateway for AI agents — HTTP+stdio, 8 tools, MTProto User API
This MCP server is a Multi-tenant Telegram gateway for AI agents. It provides an HTTP+stdio interface and bridges Telegram access via an MTProto User API. It exposes 8 context-efficient tools for use by MCP clients.
🛠️ Key Features
Multi-tenant Telegram gateway for AI agents
HTTP + stdio connectivity
MTProto User API / MTProto bridge
8 context-efficient tools
🚀 Use Cases
Integrate Telegram into AI agent workflows via an MCP client
Use MCP tools to access or automate Telegram-related actions in an agentic setup
⚡ Developer Benefits
Works as an MCP server with HTTP+stdio support
Designed for MCP clients and “mcp-server” interoperability
Supports a Telegram MTProto bridge for agent integration
Search all Telegram chats at once (not scoped to one chat). Comma-separated query terms; optional filters by date, chat kind, and public username. Success: message list and metadata dict. Global search ignores include_total_count. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters8
query
string
required
Search terms, comma-separated for multiple terms (OR-style global search). Required.
limit
integer
optional
Maximum messages to return (recommended 50 or less).
min_date
string
optional
Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.
max_date
string
optional
Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.
If true, prefer chats with a public username; if false, without. Does not apply to private DMs. Omit to skip this filter.
auto_expand_batches
integer
optional
Extra search batches to run when filters narrow results. Higher values may return more matches at the cost of latency.
include_total_count
boolean
optional
If true, response may include total_count where supported (per-chat search; ignored for global search).
Raw schema
{
"type": "object",
"properties": {
"query": {
"description": "Search terms, comma-separated for multiple terms (OR-style global search). Required.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Maximum messages to return (recommended 50 or less).",
"type": "integer"
},
"min_date": {
"default": null,
"description": "Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.",
"type": "string"
},
"max_date": {
"default": null,
"description": "Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.",
"type": "string"
},
"chat_type": {
"default": null,
"description": "Comma-separated chat kinds: private, bot, group, channel. Case-insensitive; extra spaces allowed.",
"type": "string"
},
"public": {
"default": null,
"description": "If true, prefer chats with a public username; if false, without. Does not apply to private DMs. Omit to skip this filter.",
"type": "boolean"
},
"auto_expand_batches": {
"default": 2,
"description": "Extra search batches to run when filters narrow results. Higher values may return more matches at the cost of latency.",
"type": "integer"
},
"include_total_count": {
"default": false,
"description": "If true, response may include total_count where supported (per-chat search; ignored for global search).",
"type": "boolean"
}
},
"required": [
"query"
],
"additionalProperties": false
}
get_messages
Read or search messages in one chat: browse latest, search text, fetch by ids, or load replies to a message (comments, forum topics, threads). Use from_user to filter by sender (server-side, per-chat only). Use context to include neighboring messages and reply chains around each result. Use include_replies to fetch up to 5 direct replies per result. Do not combine message_ids with query or reply_to_id. Success: messages, has_more, optional total_count and discussion fields. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters13
chat_id
string
required
Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.
query
string
optional
Search within this chat only; comma-separated terms. Omit to browse latest or use message_ids / reply_to_id modes.
message_ids
array
optional
Exact message ids to fetch. Mutually exclusive with query and reply_to_id.
reply_to_id
integer
optional
Anchor message id: channel post id, forum topic_id from get_chat_info, or a message id for direct replies. Use with thread_scope.
thread_scope
string
optional
Only with reply_to_id. auto: full forum topic (topic_id) or channel comment thread via getReplies; else direct replies. full: nested branch under a message id (forum in-topic uses search window, not whole topic); supergroup threads use search top_msg_id. direct: immediate replies only.
limit
integer
optional
Maximum messages to return (recommended 50 or less).
min_date
string
optional
Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.
max_date
string
optional
Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.
from_user
string
optional
Only return messages from this sender. Not a display-name or contact-name search — bare strings resolve like chat_id via get_entity (usernames are case-insensitive and may match an unrelated channel). Prefer @username, phone (+…), or numeric user id. Also accepts 'me', 'self', t.me URL, -100 prefixed id. Uses Telegram's native from_id server-side filter (per-chat search only).
context
integer
optional
Number of surrounding messages to include as context for each search result. 0 = disabled (default). 1-10 = include N messages before and N after each result. Also fetches the message being replied to and top replies (if include_replies=true). Requires chat_id. Disabled when result count exceeds cost-based caps.
include_replies
boolean
optional
If true, fetch up to 5 direct replies per search result and attach as replies. Each result costs one API call (not batchable). Default: false.
auto_expand_batches
integer
optional
Extra search batches to run when filters narrow results. Higher values may return more matches at the cost of latency.
include_total_count
boolean
optional
If true, response may include total_count where supported (per-chat search; ignored for global search).
Raw schema
{
"type": "object",
"properties": {
"chat_id": {
"description": "Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.",
"type": "string"
},
"query": {
"default": null,
"description": "Search within this chat only; comma-separated terms. Omit to browse latest or use message_ids / reply_to_id modes.",
"type": "string"
},
"message_ids": {
"default": null,
"description": "Exact message ids to fetch. Mutually exclusive with query and reply_to_id.",
"items": {
"type": "integer"
},
"type": "array"
},
"reply_to_id": {
"default": null,
"description": "Anchor message id: channel post id, forum topic_id from get_chat_info, or a message id for direct replies. Use with thread_scope.",
"type": "integer"
},
"thread_scope": {
"default": "auto",
"description": "Only with reply_to_id. auto: full forum topic (topic_id) or channel comment thread via getReplies; else direct replies. full: nested branch under a message id (forum in-topic uses search window, not whole topic); supergroup threads use search top_msg_id. direct: immediate replies only.",
"enum": [
"auto",
"full",
"direct"
],
"type": "string"
},
"limit": {
"default": 50,
"description": "Maximum messages to return (recommended 50 or less).",
"type": "integer"
},
"min_date": {
"default": null,
"description": "Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.",
"type": "string"
},
"max_date": {
"default": null,
"description": "Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.",
"type": "string"
},
"from_user": {
"default": null,
"description": "Only return messages from this sender. Not a display-name or contact-name search — bare strings resolve like chat_id via get_entity (usernames are case-insensitive and may match an unrelated channel). Prefer @username, phone (+…), or numeric user id. Also accepts 'me', 'self', t.me URL, -100 prefixed id. Uses Telegram's native from_id server-side filter (per-chat search only).",
"type": "string"
},
"context": {
"default": 0,
"description": "Number of surrounding messages to include as context for each search result. 0 = disabled (default). 1-10 = include N messages before and N after each result. Also fetches the message being replied to and top replies (if include_replies=true). Requires chat_id. Disabled when result count exceeds cost-based caps.",
"maximum": 10,
"minimum": 0,
"type": "integer"
},
"include_replies": {
"default": false,
"description": "If true, fetch up to 5 direct replies per search result and attach as replies. Each result costs one API call (not batchable). Default: false.",
"type": "boolean"
},
"auto_expand_batches": {
"default": 2,
"description": "Extra search batches to run when filters narrow results. Higher values may return more matches at the cost of latency.",
"type": "integer"
},
"include_total_count": {
"default": false,
"description": "If true, response may include total_count where supported (per-chat search; ignored for global search).",
"type": "boolean"
}
},
"required": [
"chat_id"
],
"additionalProperties": false
}
send_message
Send text and optional file attachments to a Telegram chat. Supports reply-to (including forum topics and channel discussion groups), parse_mode: classic markdown/html/auto (entities) or rich (Rich Message document; dialect auto-detected). parse_mode=rich cannot be combined with files. File attachments as http(s) URLs, local paths, or data: URIs. When files are provided, the message text becomes a caption. For channel posts with reply_to_id, automatically posts in the linked discussion group. Success: dict with message_id, date, chat, text, status='sent', and sender info (rich messages also set rich=true and rich_format). Error: dict with ok=false and error string. Use send_message to create new messages; use edit_message to modify existing ones. Use send_message_to_phone when targeting a phone number instead of a chat_id. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters5
chat_id
string
required
Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.
message
string
required
Message text. When sending files, used as caption.
reply_to_id
integer
optional
Telegram message id to reply to. For forums, topic root id; for channel posts, post id (may create a comment). Omit for a new top-level message.
parse_mode
string
optional
'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.
files
array
optional
List of attachment URLs, local paths, or data URIs (one or more strings). data: URIs (data:<mime>;base64,<payload>) work in all server modes; local paths work in stdio mode only.
Raw schema
{
"type": "object",
"properties": {
"chat_id": {
"description": "Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.",
"type": "string"
},
"message": {
"description": "Message text. When sending files, used as caption.",
"type": "string"
},
"reply_to_id": {
"default": null,
"description": "Telegram message id to reply to. For forums, topic root id; for channel posts, post id (may create a comment). Omit for a new top-level message.",
"type": "integer"
},
"parse_mode": {
"default": "auto",
"description": "'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.",
"enum": [
"markdown",
"html",
"auto",
"rich"
],
"type": "string"
},
"files": {
"default": null,
"description": "List of attachment URLs, local paths, or data URIs (one or more strings). data: URIs (data:<mime>;base64,<payload>) work in all server modes; local paths work in stdio mode only.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"chat_id",
"message"
],
"additionalProperties": false
}
edit_message
Replace the text of an existing message in a Telegram chat. Only works on messages sent by the authenticated account. Cannot edit media or other message attributes — text only. parse_mode: classic markdown/html/auto or rich (Rich Message; dialect auto-detected). Success: dict with message_id, date, chat, text, status='edited', and edit_date (rich messages also set rich=true and rich_format). Error: dict with ok=false and error string (e.g. message not found or not editable). Use edit_message to update a previously sent message; use send_message to create new ones. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters4
chat_id
string
required
Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.
message_id
integer
required
Message id in this chat to edit (from get_messages or Telegram).
message
string
required
Message text. When sending files, used as caption.
parse_mode
string
optional
'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.
Raw schema
{
"type": "object",
"properties": {
"chat_id": {
"description": "Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.",
"type": "string"
},
"message_id": {
"description": "Message id in this chat to edit (from get_messages or Telegram).",
"type": "integer"
},
"message": {
"description": "Message text. When sending files, used as caption.",
"type": "string"
},
"parse_mode": {
"default": "auto",
"description": "'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.",
"enum": [
"markdown",
"html",
"auto",
"rich"
],
"type": "string"
}
},
"required": [
"chat_id",
"message_id",
"message"
],
"additionalProperties": false
}
find_chats
Find users/groups/channels by name, username, or phone. Comma-separated usernames are searched in parallel and results are merged round-robin. Global search (query required) searches all Telegram; with min_date, max_date, or filter, search uses dialog list or a named filter; include_peers filters use last-activity from GetPeerDialogs; flag-based filters use dialog list dates. Success: dict with key chats (list of chat objects). Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters7
query
string
optional
Name, username (no @), phone (+country…), or comma-separated usernames for batch lookup. Example: 'alice,bob,charlie'. Required for global search unless you use min_date/max_date or folder alone.
If true, prefer chats with a public username; if false, without. Does not apply to private DMs. Omit to skip this filter.
min_date
string
optional
Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.
max_date
string
optional
Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.
folder
string
optional
Telegram folder name (case-insensitive exact match after normalization). In Telegram's UI these are called folders; internally they are "dialog filters" — saved filter presets that group chats by custom criteria (pinned, unread, business, etc.). See Filters-vs-Folders.md for the technical distinction.
Raw schema
{
"type": "object",
"properties": {
"query": {
"default": null,
"description": "Name, username (no @), phone (+country…), or comma-separated usernames for batch lookup. Example: 'alice,bob,charlie'. Required for global search unless you use min_date/max_date or folder alone.",
"type": "string"
},
"limit": {
"default": 20,
"description": "Maximum chats to return (recommended 50 or less).",
"type": "integer"
},
"chat_type": {
"default": null,
"description": "Comma-separated chat kinds: private, bot, group, channel. Case-insensitive; extra spaces allowed.",
"type": "string"
},
"public": {
"default": null,
"description": "If true, prefer chats with a public username; if false, without. Does not apply to private DMs. Omit to skip this filter.",
"type": "boolean"
},
"min_date": {
"default": null,
"description": "Inclusive minimum date filter (ISO 8601 date or datetime). Omit for no lower bound.",
"type": "string"
},
"max_date": {
"default": null,
"description": "Inclusive maximum date filter (ISO 8601 date or datetime). Omit for no upper bound.",
"type": "string"
},
"folder": {
"default": null,
"description": "Telegram folder name (case-insensitive exact match after normalization). In Telegram's UI these are called folders; internally they are \"dialog filters\" — saved filter presets that group chats by custom criteria (pinned, unread, business, etc.). See Filters-vs-Folders.md for the technical distinction.",
"type": "string"
}
},
"additionalProperties": false
}
get_chat_info
Load profile and metadata for one user, bot, group, or channel. Success: info dict; forum chats may include topics up to topics_limit. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters2
chat_id
string
required
Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.
topics_limit
integer
optional
Max forum topics to list when the chat is a forum.
Raw schema
{
"type": "object",
"properties": {
"chat_id": {
"description": "Target chat: numeric id (e.g. -100…), username without @, or 'me' for Saved Messages.",
"type": "string"
},
"topics_limit": {
"default": 20,
"description": "Max forum topics to list when the chat is a forum.",
"type": "integer"
}
},
"required": [
"chat_id"
],
"additionalProperties": false
}
send_message_to_phone
Send to a phone number: may create a temporary contact, then send text or files. Supports parse_mode: classic markdown/html/auto or rich (Rich Message; dialect auto-detected). parse_mode=rich cannot be combined with files. Success: send result plus contact_was_new / contact_removed when applicable. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters8
phone_number
string
required
E.164 phone number with country code, e.g. +1234567890 (must be on Telegram).
message
string
required
Message text. When sending files, used as caption.
first_name
string
optional
First name when creating a temporary contact.
last_name
string
optional
Last name when creating a temporary contact.
remove_if_new
boolean
optional
If true, delete the contact after send when it was created only for this send.
reply_to_msg_id
integer
optional
Reply to this message id in the target chat after resolve.
parse_mode
string
optional
'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.
files
array
optional
List of attachment URLs, local paths, or data URIs (one or more strings). data: URIs (data:<mime>;base64,<payload>) work in all server modes; local paths work in stdio mode only.
Raw schema
{
"type": "object",
"properties": {
"phone_number": {
"description": "E.164 phone number with country code, e.g. +1234567890 (must be on Telegram).",
"type": "string"
},
"message": {
"description": "Message text. When sending files, used as caption.",
"type": "string"
},
"first_name": {
"default": "Contact",
"description": "First name when creating a temporary contact.",
"type": "string"
},
"last_name": {
"default": "Name",
"description": "Last name when creating a temporary contact.",
"type": "string"
},
"remove_if_new": {
"default": false,
"description": "If true, delete the contact after send when it was created only for this send.",
"type": "boolean"
},
"reply_to_msg_id": {
"default": null,
"description": "Reply to this message id in the target chat after resolve.",
"type": "integer"
},
"parse_mode": {
"default": "auto",
"description": "'markdown'/'html'/'auto': classic entity formatting (auto detects). 'rich': Telegram Rich Message document; dialect auto-detected (known HTML tags outside code → rich HTML, else rich markdown). Default is 'auto'. parse_mode='rich' cannot be combined with files.",
"enum": [
"markdown",
"html",
"auto",
"rich"
],
"type": "string"
},
"files": {
"default": null,
"description": "List of attachment URLs, local paths, or data URIs (one or more strings). data: URIs (data:<mime>;base64,<payload>) work in all server modes; local paths work in stdio mode only.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"phone_number",
"message"
],
"additionalProperties": false
}
invoke_mtproto
Low-level Telegram API (MTProto) invoke for methods not wrapped by other tools. Dangerous methods require allow_dangerous=true. Success: API result dict or normalized error. Full documentation: https://github.com/leshchenko1979/fast-mcp-telegram/blob/main/docs/Tools-Reference.md
Parameters4
method_full_name
string
required
Telegram API method, e.g. "messages.GetHistory" or "users.GetFullUser" (normalization applied).
params_json
string
required
JSON object string of TL parameters as in Telegram API docs; nested TL uses "_": "typeName" discriminator.
allow_dangerous
boolean
optional
If false, destructive methods (e.g. deletes) are blocked. Set true only when intended.
resolve
boolean
optional
If true, resolve string/int peer-like fields to TL Input* entities before invoke.
Raw schema
{
"type": "object",
"properties": {
"method_full_name": {
"description": "Telegram API method, e.g. \"messages.GetHistory\" or \"users.GetFullUser\" (normalization applied).",
"type": "string"
},
"params_json": {
"description": "JSON object string of TL parameters as in Telegram API docs; nested TL uses \"_\": \"typeName\" discriminator.",
"type": "string"
},
"allow_dangerous": {
"default": false,
"description": "If false, destructive methods (e.g. deletes) are blocked. Set true only when intended.",
"type": "boolean"
},
"resolve": {
"default": true,
"description": "If true, resolve string/int peer-like fields to TL Input* entities before invoke.",
"type": "boolean"
}
},
"required": [
"method_full_name",
"params_json"
],
"additionalProperties": false
}
This server sits between your AI agent and Telegram's API:
code
Your agent → MCP/HTTP → this server → MTProto → Telegram
What it does: Authenticates you with Telegram (QR or phone/bot token), exposes 8 AI-friendly tools instead of 80+ micro-APIs, and bridges raw MTProto for power users. Multi-tenant — one server, many users, isolated sessions.
Optional PREFIX_MCP_TOOLS_WITH_ACCOUNT — when one agent uses several MCP connections (same server, different tokens), prefixes tool names so they do not collide; not needed for standard multi-user hosting
Bot token alternative (no phone, no OTP):
Set BOT_API_TOKEN instead of --phone-number. See Installation Guide.
2. Configure MCP Client
stdio mode (local): Add to your MCP client config (e.g. claude_desktop_config.json) — stdio (standard input/output) is the default transport for local MCP clients: