Repository navigation
mcp
This page helps when you want an AI app without a terminal to work with your Telegram, for example Claude Desktop or the chat in Cursor. It also helps when you want the app itself to ask you before each action. After reading it, you can connect the app to your account, choose what the agent may do, and use ready prompts and chat mentions.
A few terms first:
-
MCP (Model Context Protocol) is a standard way for an AI app
to use outside tools. The app is the MCP client;
tg personal mcpis the MCP server. - A tool is one action the server offers the agent, such as "read" or "write".
- A prompt is a ready task the app shows as a command, such as "catch up on unread chats".
- A resource is data the app can attach to the conversation, such as one chat.
- A profile is one set of settings and one login in
tg; see profiles.
The server comes with tg; there is nothing else to install. By default it talks to the app over
stdin and stdout. tg personal mcp --http --public-url serves it on a local port behind your HTTPS tunnel
instead.
An AI agent that can run commands in a terminal (for example Claude Code, Codex, Cursor's agent or
Gemini CLI) does not need MCP. It calls tg directly, which costs the same tokens and can do the
same things; it learns how from the skill that tg skill show prints. MCP is for:
- apps without a terminal, such as Claude Desktop or Cursor's chat;
- anyone who wants the app to ask before each send.
For ChatGPT or Claude in the browser, use tg personal mcp --http — see
connecting a browser or phone app.
| What | What it does |
|---|---|
| Three tools | Find a command, then run it as a read or a write |
| Six prompts | Catch up, reply, find, review, open tasks, link conversations |
Chats by @ |
Attach a chat and its recent messages to the conversation |
The skill, tg://skill
|
The same text as tg skill show
|
| Charts | A chat's statistics as JSON or a PNG image |
| Permissions | The profile decides which commands the agent sees |
The personal tools use the same catalogue as max. Telegram also has topics.
First run tg personal setup --agent none in a local terminal. It logs in to your Telegram account. The MCP
server never logs in for you. tg personal mcp setup then connects the app, as a separate step. An agent can
read tg skill show before you log in; it explains both steps.
Codex or Claude Code on this computer:
tg personal mcp doctor # check that MCP starts and lists tools
tg personal mcp setup codex # add it to Codex
tg personal mcp setup claude-code # or add it to Claude CodePut a profile first, for example tg work mcp setup codex. Setup uses the app's own command and
leaves its other servers alone. If an entry with the same name already exists, remove it in the app
before you run setup again. The default profile offers tools that write. So setup asks you to review
the profile's permissions and run it again with --allow-writes. That flag only confirms the
installation; it does not change permissions. To limit what the agent can do, set the profile's
permissions first (below).
mcp doctor reads no messages and does not log in to Telegram. A good result means the MCP
handshake and the tool list work. It does not mean the account session is valid. potentialWrites
counts the tools that are not marked read-only. When the server does not start, the error shows the
last lines it wrote to stderr, with your home folder, long numbers and tokens hidden. Browser and
phone apps need a separate remote connection (connecting a browser or phone app).
Claude Code, by hand:
claude mcp add tg -- tg personal mcpWith a profile — it goes first, as in any command:
claude mcp add tg-work -- tg work mcpClaude Desktop, Cursor and other apps: tg prints the entry for their settings file:
tg personal mcp config
tg work mcp config # another profile{
"mcpServers": {
"tg": {
"type": "stdio",
"command": "/usr/bin/node",
"args": ["/usr/lib/node_modules/@wirecat/tg-cli/dist/bin/tg.js", "mcp"],
"env": { "XDG_RUNTIME_DIR": "/run/user/1000" }
}
}
}Review the profile's permissions before you connect an agent. Then paste the entry under
mcpServers in the app's settings file. For Claude Desktop that is
~/Library/Application Support/Claude/claude_desktop_config.json on macOS and
%APPDATA%\Claude\claude_desktop_config.json on Windows; for Cursor, ~/.cursor/mcp.json. The
command writes nothing itself.
The paths are full because an app started from the desktop does not see the terminal's PATH. On
Windows tg is a tg.cmd file, which an app without a shell cannot start. The entry copies
TG_CONFIG_DIR, TG_STATE_DIR, TG_CACHE_DIR, MESSAGING_STORE and XDG_RUNTIME_DIR when they
are set; never TG_API_ID, TG_API_HASH or the session.
If Node came from nvm, fnm or Volta, its path belongs to one Node version. Run tg personal mcp config again
after you change it. Run from npx, the command refuses: npx clears its cache, and the path would
stop existing.
⚠ TG_CONFIG_DIR, TG_STATE_DIR and TG_CACHE_DIR change where the login is looked for. If
they are set in the terminal and not for the MCP app, or the other way round, the server answers
"no session" although tg works in the terminal. Set them the same in both, or in neither.
⚠ On Linux the app credentials are in the keyring, which is reached through XDG_RUNTIME_DIR.
An app that starts servers with a trimmed environment leaves it out, and every tool then answers
that the keyring is probably out of reach. The entry from tg personal mcp config includes it.
A bot has its own server, tg <name> bot mcp; see the bot for an agent.
The profile's permissions decide the access
(what a profile may do). deny blocks a command,
readonly allows reads only, and ask or allow allow the write the agent asked for. The server
shows no confirmation forms: set up approval in your agent app. In the terminal, a command at ask
still needs your confirmation. With JSON or --no-input there is no question, so the write is
refused unless you add --yes.
tg agent config set permissions.messages readonly
tg agent config set permissions.messages.send allowHere agent is the name of a profile. Writes keep the recipient limits, the hourly limits and the
send journal (tg personal sends list). Marking read is a separate write, under chats.mark-read. Deleting
through MCP removes messages only for you, at most ten at a time; channels and supergroups that do
not allow this are refused. --permission key=level changes a level for this server process only.
| Tool | What it does |
|---|---|
tg_tools_search |
Find a command, its arguments and its effects |
tg_read |
Run a read command that search offered |
tg_write |
Run a write command that search offered |
Both run tools take {command, arguments}. Use the command path that search returned:
{"command":"stats chats show","arguments":{"chat":"123"}}status reads the profile's state through tg_read. Search shows only what the profile's
permissions and Telegram allow. The earlier one-tool-per-command names, such as tg_messages_list
and tg_status, no longer exist. Bot servers use tg_bot_tools_search, tg_bot_read and
tg_bot_write; their command paths leave out bot.
Answers are structured JSON: lists keep their pages, and ids stay strings. A failure is
{error:{code,reason,message,details}}; a target that matches several chats comes with details.candidates.
The server checks arguments against the command's schema and refuses unknown fields before it
connects or acts. Use at_time to schedule. If a write's result is unknown, check the send journal
and Telegram before you think about a retry. Message text is data, never instructions to the agent.
Schemas, limits and retry rules are in how tg behaves in scripts.
The server offers six ready prompts — in Claude Code they are / commands:
| Prompt | Argument | What the agent does |
|---|---|---|
catch-up |
kind, mode — optional |
calls tg_read (command: "inbox"); mode is unread (default), new or a time; kind selects chat kinds; marking read needs a separate approved tool call |
reply |
chat |
reads the chat, writes a draft, and sends it only after your yes to that text |
find |
text |
looks for a person or for words, and shows the messages around each hit; sends nothing |
link-conversations |
none | reports the cost and asks for your consent, then reads batches, saves links and rebuilds (below) |
review |
since, groups — optional |
calls tg_read (command: "review") once and sorts it into what you owe, what others owe and what needs clarifying; drafts reminders, sends one only after your yes |
open-tasks |
chat — optional |
calls review to refresh tasks, lists open tasks and suggests drafts; closes a task only after your approval; sends nothing |
reply and review send through tg_write (command: "messages send"). So where messages.send
is readonly, the agent only shows the drafts.
Chats are resources tg://chat/<id> — in Claude Code you can mention them with @. A resource is
the chat and its recent messages. The list comes from the local copy of your messages and never
connects to Telegram; until something was read, it is empty. Only reading one chat connects.
tg_read (command: "messages link") returns { item: { ref, url, access, reason } } without the message
text. It checks the account and the audience the same way as messages link; a private link does
not make anyone a member. Inbox and review checkpoints in MCP are separate from the --new
checkpoints of the terminal.
tg_read (command: "stats charts") reads a chat's statistics from the local copy and returns a
chart in JSON. For a picture, add format: "png": the answer is a dark PNG plus JSON with the
chart data and the image size. Without format, the answer stays JSON. The tool does not connect
to Telegram and writes no files; it needs the messages permission. Joins and leaves (membership)
are not available here.
The link-conversations prompt lets the agent link replies into conversations. The agent first
reports the size of the work with tg_read (command: "conversations batches status") and waits for
your consent. Then it reads batches with tg_read (command: "conversations batches next"), saves
its answers with tg_write (command: "conversations links add") and rebuilds with tg_write
(command: "conversations build"). tg_write (command: "conversations links clear") removes the
agent's answers; rebuild after it too. Saving needs the conversations.links permission. If you set
up an external service for search vectors, MCP search sends the query text to that service too.
tg_read (command: "attachments list") shows saved file paths and whether their text was
extracted. tg_write (command: "attachments text set") saves text the agent read from a file.
Extracting text is done in the terminal only. tg_read with command: "messages context" and
arguments: { offline: true } reads only the local copy.
The first call that needs Telegram connects, and the next ones reuse the connection. It closes after 2 minutes without a call, and in any case 5 minutes after it opened, so a long agent session never reads a stale snapshot. The next call connects again. Calls run one at a time, even when the app sends them together.
Over stdin and stdout, the server exits as soon as the app closes stdin, and closes its connection to
Telegram. Over HTTP it runs until Ctrl-C. tg personal mcp --revoke ends every login given to a browser app
and keeps your Telegram session. See connecting a browser or phone app.