mcp-telegram-bot 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+ .env
9
+ .venv
10
+ venv/
11
+ env/
12
+ *.pyc
13
+ *.pyo
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .mypy_cache/
17
+ htmlcov/
18
+ .coverage
19
+ .coverage.*
20
+ *.log
21
+ .DS_Store
22
+ .idea/
23
+ .vscode/
24
+ *.swp
25
+ *.swo
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dario Clavijo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-telegram-bot
3
+ Version: 0.1.0
4
+ Summary: MCP server that exposes a Telegram bot
5
+ Project-URL: Homepage, https://github.com/daedalus/mcp-telegram-bot
6
+ Project-URL: Repository, https://github.com/daedalus/mcp-telegram-bot
7
+ Project-URL: Issues, https://github.com/daedalus/mcp-telegram-bot/issues
8
+ Author-email: Dario Clavijo <clavijodario@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.11
12
+ Requires-Dist: fastmcp
13
+ Requires-Dist: python-telegram-bot>=20.0
14
+ Provides-Extra: all
15
+ Requires-Dist: hatch; extra == 'all'
16
+ Requires-Dist: hypothesis; extra == 'all'
17
+ Requires-Dist: mypy; extra == 'all'
18
+ Requires-Dist: pytest; extra == 'all'
19
+ Requires-Dist: pytest-asyncio; extra == 'all'
20
+ Requires-Dist: pytest-cov; extra == 'all'
21
+ Requires-Dist: pytest-mock; extra == 'all'
22
+ Requires-Dist: ruff; extra == 'all'
23
+ Provides-Extra: dev
24
+ Requires-Dist: hatch; extra == 'dev'
25
+ Requires-Dist: mypy; extra == 'dev'
26
+ Requires-Dist: ruff; extra == 'dev'
27
+ Provides-Extra: lint
28
+ Requires-Dist: mypy; extra == 'lint'
29
+ Requires-Dist: ruff; extra == 'lint'
30
+ Provides-Extra: test
31
+ Requires-Dist: hypothesis; extra == 'test'
32
+ Requires-Dist: pytest; extra == 'test'
33
+ Requires-Dist: pytest-asyncio; extra == 'test'
34
+ Requires-Dist: pytest-cov; extra == 'test'
35
+ Requires-Dist: pytest-mock; extra == 'test'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # mcp-telegram-bot
39
+
40
+ > MCP server that exposes a Telegram bot
41
+
42
+ [![PyPI](https://img.shields.io/pypi/v/mcp-telegram-bot.svg)](https://pypi.org/project/mcp-telegram-bot/)
43
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-telegram-bot.svg)](https://pypi.org/project/mcp-telegram-bot/)
44
+ [![Coverage](https://codecov.io/gh/daedalus/mcp-telegram-bot/branch/main/graph/badge.svg)](https://codecov.io/gh/daedalus/mcp-telegram-bot)
45
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install mcp-telegram-bot
51
+ ```
52
+
53
+ ## Setup
54
+
55
+ 1. Create a Telegram bot by talking to @BotFather on Telegram
56
+ 2. Get your bot token
57
+ 3. Set the `TELEGRAM_BOT_TOKEN` environment variable
58
+
59
+ ## Usage
60
+
61
+ ```bash
62
+ export TELELEGRAM_BOT_TOKEN="your-bot-token-here"
63
+ mcp-telegram-bot
64
+ ```
65
+
66
+ ### MCP Tools
67
+
68
+ - **send_message**: Send a message to a Telegram chat
69
+ - **get_me**: Get bot information
70
+ - **get_updates**: Get recent updates from Telegram
71
+
72
+ ### MCP Resources
73
+
74
+ - **bot://status**: Get bot status
75
+
76
+ mcp-name: io.github.daedalus/mcp-telegram-bot
77
+
78
+ ## Development
79
+
80
+ ```bash
81
+ git clone https://github.com/daedalus/mcp-telegram-bot.git
82
+ cd mcp-telegram-bot
83
+ pip install -e ".[test]"
84
+
85
+ # run tests
86
+ pytest
87
+
88
+ # format
89
+ ruff format src/ tests/
90
+
91
+ # lint
92
+ ruff check src/ tests/
93
+
94
+ # type check
95
+ mypy src/
96
+ ```
97
+
98
+ ## License
99
+
100
+ MIT
@@ -0,0 +1,63 @@
1
+ # mcp-telegram-bot
2
+
3
+ > MCP server that exposes a Telegram bot
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/mcp-telegram-bot.svg)](https://pypi.org/project/mcp-telegram-bot/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/mcp-telegram-bot.svg)](https://pypi.org/project/mcp-telegram-bot/)
7
+ [![Coverage](https://codecov.io/gh/daedalus/mcp-telegram-bot/branch/main/graph/badge.svg)](https://codecov.io/gh/daedalus/mcp-telegram-bot)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install mcp-telegram-bot
14
+ ```
15
+
16
+ ## Setup
17
+
18
+ 1. Create a Telegram bot by talking to @BotFather on Telegram
19
+ 2. Get your bot token
20
+ 3. Set the `TELEGRAM_BOT_TOKEN` environment variable
21
+
22
+ ## Usage
23
+
24
+ ```bash
25
+ export TELELEGRAM_BOT_TOKEN="your-bot-token-here"
26
+ mcp-telegram-bot
27
+ ```
28
+
29
+ ### MCP Tools
30
+
31
+ - **send_message**: Send a message to a Telegram chat
32
+ - **get_me**: Get bot information
33
+ - **get_updates**: Get recent updates from Telegram
34
+
35
+ ### MCP Resources
36
+
37
+ - **bot://status**: Get bot status
38
+
39
+ mcp-name: io.github.daedalus/mcp-telegram-bot
40
+
41
+ ## Development
42
+
43
+ ```bash
44
+ git clone https://github.com/daedalus/mcp-telegram-bot.git
45
+ cd mcp-telegram-bot
46
+ pip install -e ".[test]"
47
+
48
+ # run tests
49
+ pytest
50
+
51
+ # format
52
+ ruff format src/ tests/
53
+
54
+ # lint
55
+ ruff check src/ tests/
56
+
57
+ # type check
58
+ mypy src/
59
+ ```
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,89 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mcp-telegram-bot"
7
+ version = "0.1.0"
8
+ description = "MCP server that exposes a Telegram bot"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Dario Clavijo", email = "clavijodario@gmail.com"}
14
+ ]
15
+ dependencies = ["fastmcp", "python-telegram-bot>=20.0"]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "ruff",
20
+ "mypy",
21
+ "hatch",
22
+ ]
23
+ test = [
24
+ "pytest",
25
+ "pytest-cov",
26
+ "pytest-mock",
27
+ "pytest-asyncio",
28
+ "hypothesis",
29
+ ]
30
+ lint = [
31
+ "ruff",
32
+ "mypy",
33
+ ]
34
+ all = ["mcp-telegram-bot[dev,test,lint]"]
35
+
36
+ [project.scripts]
37
+ mcp-telegram-bot = "mcp_telegram_bot.__main__:main"
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/daedalus/mcp-telegram-bot"
41
+ Repository = "https://github.com/daedalus/mcp-telegram-bot"
42
+ Issues = "https://github.com/daedalus/mcp-telegram-bot/issues"
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["src/mcp_telegram_bot"]
46
+
47
+ [tool.hatch.build.targets.sdist]
48
+ include = ["src/mcp_telegram_bot"]
49
+
50
+ [tool.ruff]
51
+ line-length = 88
52
+ target-version = "py311"
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "F", "W", "I", "UP", "ANN", "TCH", "N", "C4", "ARG"]
56
+ ignore = ["E501"]
57
+
58
+ [tool.ruff.lint.per-file-ignores]
59
+ "__init__.py" = ["F401"]
60
+ "tests/*" = ["ARG001", "ANN001", "ANN201"]
61
+
62
+ [tool.ruff.lint.pydocstyle]
63
+ convention = "google"
64
+
65
+ [tool.mypy]
66
+ python_version = "3.11"
67
+ strict = true
68
+ warn_return_any = true
69
+ warn_unused_ignores = true
70
+
71
+ [tool.pytest.ini_options]
72
+ testpaths = ["tests"]
73
+ addopts = "-v --tb=short --cov=src --cov-fail-under=80"
74
+ filterwarnings = ["ignore::DeprecationWarning"]
75
+
76
+ [tool.coverage.run]
77
+ source = ["src"]
78
+ branch = true
79
+
80
+ [tool.coverage.report]
81
+ exclude = ["tests/*", "*/__init__.py"]
82
+ exclude_lines = [
83
+ "pragma: no cover",
84
+ "def __repr__",
85
+ "raise AssertionError",
86
+ "raise NotImplementedError",
87
+ "if __name__ == .__main__.:",
88
+ "if TYPE_CHECKING:",
89
+ ]
@@ -0,0 +1,4 @@
1
+ __version__ = "0.1.0"
2
+ __all__ = ["mcp"]
3
+
4
+ from .mcp import mcp
@@ -0,0 +1,11 @@
1
+ from mcp_telegram_bot import mcp
2
+
3
+
4
+ def main() -> int:
5
+ """Main entry point for the MCP Telegram Bot server."""
6
+ mcp.run()
7
+ return 0
8
+
9
+
10
+ if __name__ == "__main__":
11
+ raise SystemExit(main())
@@ -0,0 +1,120 @@
1
+ import os
2
+ from datetime import datetime
3
+ from typing import Any
4
+
5
+ import fastmcp
6
+ from telegram import Bot
7
+ from telegram.error import TelegramError
8
+
9
+ mcp = fastmcp.FastMCP("mcp-telegram-bot")
10
+
11
+ _bot: Bot | None = None
12
+ _last_update_time: datetime | None = None
13
+
14
+
15
+ def get_bot() -> Bot:
16
+ """Get or create Telegram bot instance."""
17
+ global _bot
18
+ token = os.environ.get("TELEGRAM_BOT_TOKEN")
19
+ if not token:
20
+ raise ValueError("TELEGRAM_BOT_TOKEN environment variable is not set")
21
+ if _bot is None:
22
+ _bot = Bot(token=token)
23
+ return _bot
24
+
25
+
26
+ @mcp.tool()
27
+ async def send_message(chat_id: str, text: str) -> dict[str, Any]:
28
+ """Send a message to a specified Telegram chat.
29
+
30
+ Args:
31
+ chat_id: The Telegram chat ID to send the message to. Can be a numeric ID or username with @ prefix.
32
+ text: The message text to send.
33
+
34
+ Returns:
35
+ JSON response from Telegram API containing message details.
36
+
37
+ Raises:
38
+ ValueError: If chat_id is invalid or text is empty.
39
+ TelegramError: If Telegram API returns an error.
40
+ """
41
+ if not chat_id:
42
+ raise ValueError("chat_id cannot be empty")
43
+ if not text:
44
+ raise ValueError("text cannot be empty")
45
+
46
+ bot = get_bot()
47
+ try:
48
+ message = await bot.send_message(chat_id=chat_id, text=text)
49
+ return message.to_dict()
50
+ except TelegramError as e:
51
+ raise RuntimeError(f"Telegram API error: {e.message}") from e
52
+
53
+
54
+ @mcp.tool()
55
+ async def get_me() -> dict[str, Any]:
56
+ """Get bot information.
57
+
58
+ Returns:
59
+ JSON response containing bot information (id, username, first_name, etc.).
60
+ """
61
+ bot = get_bot()
62
+ try:
63
+ bot_info = await bot.get_me()
64
+ return bot_info.to_dict()
65
+ except TelegramError as e:
66
+ raise RuntimeError(f"Telegram API error: {e.message}") from e
67
+
68
+
69
+ @mcp.tool()
70
+ async def get_updates(
71
+ limit: int = 10, offset: int | None = None
72
+ ) -> list[dict[str, Any]]:
73
+ """Get recent updates from Telegram.
74
+
75
+ Args:
76
+ limit: Maximum number of updates to return (1-100). Defaults to 10.
77
+ offset: Identifier of the first returned update. Only updates with id greater than offset are returned.
78
+
79
+ Returns:
80
+ List of update dictionaries from Telegram API.
81
+
82
+ Raises:
83
+ TelegramError: If Telegram API returns an error.
84
+ """
85
+ global _last_update_time
86
+
87
+ if limit < 1 or limit > 100:
88
+ raise ValueError("limit must be between 1 and 100")
89
+
90
+ bot = get_bot()
91
+ try:
92
+ updates = await bot.get_updates(limit=limit, offset=offset)
93
+ _last_update_time = datetime.now()
94
+ return [update.to_dict() for update in updates]
95
+ except TelegramError as e:
96
+ raise RuntimeError(f"Telegram API error: {e.message}") from e
97
+
98
+
99
+ @mcp.resource("bot://status")
100
+ async def bot_status() -> dict[str, Any]:
101
+ """Get bot status including running state and information.
102
+
103
+ Returns:
104
+ JSON containing: is_running, bot_info, last_update_time
105
+ """
106
+ global _last_update_time
107
+
108
+ bot_info = None
109
+ try:
110
+ bot_info = await get_me()
111
+ except RuntimeError:
112
+ pass
113
+
114
+ return {
115
+ "is_running": True,
116
+ "bot_info": bot_info,
117
+ "last_update_time": _last_update_time.isoformat()
118
+ if _last_update_time
119
+ else None,
120
+ }
File without changes