# Directory Structure
```
├── .gitignore
├── config.example.json
├── package-lock.json
├── package.json
├── README.md
├── src
│ ├── index.ts
│ └── telegram-server
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Ignore config file with sensitive data
2 | config.json
3 |
4 | # Ignore build artifacts
5 | build/
6 | node_modules/
7 |
8 | # Ignore environment files
9 | .env
10 | .env.*
11 |
12 | # Ignore IDE files
13 | .vscode/
14 | .idea/
15 |
16 | # Ignore OS files
17 | .DS_Store
18 | Thumbs.db
19 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Telegram MCP Server
2 |
3 | A Model Context Protocol (MCP) server that provides Telegram integration.
4 |
5 | ## Features
6 | - Send messages through Telegram
7 | - Secure chat ID validation
8 | - Easy configuration
9 |
10 | ## Installation
11 |
12 | 1. Clone the repository
13 | 2. Follow the [setup instructions](userInstructions/telegram_server_setup.md)
14 | 3. Configure your bot token and chat ID
15 | 4. Install dependencies: `npm install`
16 | 5. Build and run: `npm run build && npm start`
17 |
18 | ## Requirements
19 | - Node.js 18+
20 | - Telegram bot token
21 | - Valid chat ID
22 |
23 | ## License
24 | MIT
25 |
```
--------------------------------------------------------------------------------
/config.example.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "telegram": {
3 | "botToken": "YOUR_TELEGRAM_BOT_TOKEN_HERE",
4 | "allowedChatIds": [
5 | 1234567890
6 | ]
7 | }
8 | }
9 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "ESNext",
5 | "moduleResolution": "node",
6 | "resolveJsonModule": true,
7 | "esModuleInterop": true,
8 | "strict": true,
9 | "outDir": "./build",
10 | "rootDir": "./src"
11 | },
12 | "include": ["src/**/*"]
13 | }
14 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "telegram-mcp-server",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
8 | "start": "node build/index.js"
9 | },
10 | "dependencies": {
11 | "@modelcontextprotocol/sdk": "^1.0.0",
12 | "node-telegram-bot-api": "^0.61.0"
13 | },
14 | "devDependencies": {
15 | "@types/node": "^22.10.7",
16 | "@types/node-telegram-bot-api": "^0.64.7",
17 | "typescript": "^5.3.3"
18 | }
19 | }
20 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import TelegramBot from 'node-telegram-bot-api';
5 | import {
6 | CallToolRequestSchema,
7 | ErrorCode,
8 | ListResourcesRequestSchema,
9 | ListResourceTemplatesRequestSchema,
10 | ListToolsRequestSchema,
11 | McpError,
12 | ReadResourceRequestSchema,
13 | } from '@modelcontextprotocol/sdk/types.js';
14 |
15 | const TELEGRAM_TOKEN = process.env.TELEGRAM_BOT_TOKEN as string;
16 | if (!TELEGRAM_TOKEN) {
17 | throw new Error('TELEGRAM_BOT_TOKEN environment variable is required');
18 | }
19 |
20 | class TelegramServer {
21 | private server: Server;
22 | private bot: TelegramBot;
23 |
24 | constructor() {
25 | this.server = new Server(
26 | {
27 | name: 'telegram-server',
28 | version: '1.0.0',
29 | },
30 | {
31 | capabilities: {
32 | resources: {},
33 | tools: {},
34 | },
35 | }
36 | );
37 |
38 | this.bot = new TelegramBot(TELEGRAM_TOKEN, { polling: true });
39 | this.setupBotHandlers();
40 | this.setupServerHandlers();
41 | }
42 |
43 | private setupBotHandlers() {
44 | this.bot.on('message', (msg: TelegramBot.Message) => {
45 | const chatId = msg.chat.id;
46 | console.log(`Received message from chat ID: ${chatId}`);
47 | if (!msg.text) return;
48 |
49 | // Echo messages back to user
50 | this.bot.sendMessage(chatId, `You said: ${msg.text}`);
51 | });
52 | }
53 |
54 | private setupServerHandlers() {
55 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
56 | tools: [
57 | {
58 | name: 'send_message',
59 | description: 'Send a message through Telegram',
60 | inputSchema: {
61 | type: 'object',
62 | properties: {
63 | chatId: { type: 'number' },
64 | message: { type: 'string' }
65 | },
66 | required: ['chatId', 'message']
67 | }
68 | }
69 | ]
70 | }));
71 |
72 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
73 | if (request.params.name === 'send_message') {
74 | if (!request.params.arguments || typeof request.params.arguments !== 'object') {
75 | throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
76 | }
77 |
78 | const { chatId, message } = request.params.arguments;
79 | if (typeof chatId !== 'number' || typeof message !== 'string') {
80 | throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
81 | }
82 | await this.bot.sendMessage(chatId, message);
83 | return {
84 | content: [{
85 | type: 'text',
86 | text: 'Message sent successfully'
87 | }]
88 | };
89 | }
90 | throw new McpError(ErrorCode.MethodNotFound, 'Unknown tool');
91 | });
92 |
93 | // Error handling
94 | this.server.onerror = (error) => console.error('[MCP Error]', error);
95 | process.on('SIGINT', async () => {
96 | await this.server.close();
97 | process.exit(0);
98 | });
99 | }
100 |
101 | async run() {
102 | const transport = new StdioServerTransport();
103 | await this.server.connect(transport);
104 | console.error('Telegram MCP server running on stdio');
105 | }
106 | }
107 |
108 | const server = new TelegramServer();
109 | server.run().catch(console.error);
110 |
```
--------------------------------------------------------------------------------
/src/telegram-server/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import TelegramBot from 'node-telegram-bot-api';
5 | import {
6 | CallToolRequestSchema,
7 | ErrorCode,
8 | ListToolsRequestSchema,
9 | McpError
10 | } from '@modelcontextprotocol/sdk/types.js';
11 |
12 | // Extended error codes
13 | const ExtendedErrorCode = {
14 | ...ErrorCode,
15 | Timeout: 1000
16 | } as const;
17 |
18 | import config from '../../config.json';
19 | import path from 'path';
20 |
21 | if (!config?.telegram?.botToken) {
22 | throw new Error('Missing telegram.botToken in config.json');
23 | }
24 |
25 | if (!config.telegram.allowedChatIds || !Array.isArray(config.telegram.allowedChatIds)) {
26 | throw new Error('Missing or invalid telegram.allowedChatIds in config.json');
27 | }
28 |
29 | const TELEGRAM_TOKEN = config.telegram.botToken;
30 | const ALLOWED_CHAT_IDS = new Set(config.telegram.allowedChatIds);
31 |
32 | interface PendingRequest {
33 | resolve: (value: string) => void;
34 | reject: (reason?: any) => void;
35 | timeout: NodeJS.Timeout;
36 | }
37 |
38 | class TelegramServer {
39 | private server: Server;
40 | private bot: TelegramBot;
41 | private pendingRequests: Map<number, PendingRequest> = new Map();
42 | private requestTimeout = 300000; // 5 minutes
43 |
44 | constructor() {
45 | this.server = new Server({
46 | name: 'telegram-server',
47 | version: '2.0.0',
48 | }, {
49 | capabilities: {
50 | resources: {},
51 | tools: {},
52 | },
53 | });
54 |
55 | this.bot = new TelegramBot(TELEGRAM_TOKEN, { polling: true });
56 | console.log('Telegram bot initialized successfully');
57 | this.setupBotHandlers();
58 | this.setupServerHandlers();
59 | }
60 |
61 | private setupBotHandlers() {
62 | this.bot.on('message', (msg) => {
63 | console.log(`Received message from chat ${msg.chat.id}: ${msg.text}`);
64 | const chatId = msg.chat.id;
65 | if (!ALLOWED_CHAT_IDS.has(chatId)) {
66 | console.warn(`Received message from unauthorized chat ID: ${chatId}`);
67 | return;
68 | }
69 | const messageText = msg.text ?? '';
70 |
71 | // Skip if we don't have valid text content
72 | if (!messageText.trim()) {
73 | if (this.pendingRequests.has(chatId)) {
74 | const pendingRequest = this.pendingRequests.get(chatId);
75 | if (pendingRequest) {
76 | clearTimeout(pendingRequest.timeout);
77 | this.pendingRequests.delete(chatId);
78 | pendingRequest.reject(new McpError(ErrorCode.InvalidParams, 'Invalid message type'));
79 | }
80 | }
81 | return;
82 | }
83 |
84 | // Check if this is a response to a pending request
85 | const pendingRequest = this.pendingRequests.get(chatId);
86 | if (pendingRequest) {
87 | clearTimeout(pendingRequest.timeout);
88 | this.pendingRequests.delete(chatId);
89 | pendingRequest.resolve(messageText);
90 | return;
91 | }
92 |
93 | // Default behavior for non-request messages
94 | console.log(`Sending response to chat ${chatId}`);
95 | this.bot.sendMessage(chatId, `You said: ${messageText}`);
96 | });
97 | }
98 |
99 | private setupServerHandlers() {
100 | console.log('Setting up MCP server handlers');
101 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
102 | tools: [
103 | {
104 | name: 'send_message',
105 | description: 'Send a message through Telegram',
106 | inputSchema: {
107 | type: 'object',
108 | properties: {
109 | chatId: { type: 'number' },
110 | message: { type: 'string' }
111 | },
112 | required: ['chatId', 'message']
113 | }
114 | },
115 | {
116 | name: 'request_user_input',
117 | description: 'Request user input through Telegram',
118 | inputSchema: {
119 | type: 'object',
120 | properties: {
121 | chatId: { type: 'number' },
122 | prompt: { type: 'string' }
123 | },
124 | required: ['chatId', 'prompt']
125 | }
126 | }
127 | ]
128 | }));
129 |
130 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
131 | switch (request.params.name) {
132 | case 'send_message':
133 | return this.handleSendMessage(request.params.arguments);
134 | case 'request_user_input':
135 | return this.handleRequestUserInput(request.params.arguments);
136 | default:
137 | throw new McpError(ErrorCode.MethodNotFound, 'Unknown tool');
138 | }
139 | });
140 |
141 | // Error handling
142 | this.server.onerror = (error) => {
143 | console.error('[MCP Error]', error);
144 | console.log('Attempting to recover from error...');
145 | };
146 | process.on('SIGINT', async () => {
147 | await this.server.close();
148 | process.exit(0);
149 | });
150 | }
151 |
152 | private async handleSendMessage(args: any) {
153 | if (!args || typeof args !== 'object') {
154 | throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
155 | }
156 | const { chatId, message } = args;
157 | if (typeof chatId !== 'number' || typeof message !== 'string') {
158 | throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
159 | }
160 | await this.bot.sendMessage(chatId, message);
161 | return {
162 | content: [{
163 | type: 'text',
164 | text: 'Message sent successfully'
165 | }]
166 | };
167 | }
168 |
169 | private async handleRequestUserInput(args: any) {
170 | if (!args || typeof args !== 'object') {
171 | throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
172 | }
173 | const { chatId, prompt } = args;
174 | if (typeof chatId !== 'number' || typeof prompt !== 'string') {
175 | throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
176 | }
177 |
178 | // Send the prompt to the user
179 | await this.bot.sendMessage(chatId, prompt);
180 |
181 | // Create and return a promise that resolves when we get a response
182 | return new Promise<string>((resolve, reject) => {
183 | const timeout = setTimeout(() => {
184 | this.pendingRequests.delete(chatId);
185 | reject(new McpError(ExtendedErrorCode.Timeout, 'Request timed out'));
186 | }, this.requestTimeout);
187 |
188 | this.pendingRequests.set(chatId, {
189 | resolve,
190 | reject,
191 | timeout
192 | });
193 | }).then(response => ({
194 | content: [{
195 | type: 'text',
196 | text: response
197 | }]
198 | }));
199 | }
200 |
201 | async run() {
202 | const transport = new StdioServerTransport();
203 | await this.server.connect(transport);
204 | console.log('Telegram MCP server running on stdio');
205 | console.log('Bot username:', this.bot.getMe());
206 | console.log('MCP server initialized with tools:', [
207 | 'send_message',
208 | 'request_user_input'
209 | ]);
210 | }
211 | }
212 |
213 | const server = new TelegramServer();
214 | server.run().catch(console.error);
215 |
```