#
tokens: 2730/50000 7/7 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── config.example.json
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── telegram-server
│       └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Ignore config file with sensitive data
config.json

# Ignore build artifacts
build/
node_modules/

# Ignore environment files
.env
.env.*

# Ignore IDE files
.vscode/
.idea/

# Ignore OS files
.DS_Store
Thumbs.db

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Telegram MCP Server

A Model Context Protocol (MCP) server that provides Telegram integration.

## Features
- Send messages through Telegram
- Secure chat ID validation
- Easy configuration

## Installation

1. Clone the repository
2. Follow the [setup instructions](userInstructions/telegram_server_setup.md)
3. Configure your bot token and chat ID
4. Install dependencies: `npm install`
5. Build and run: `npm run build && npm start`

## Requirements
- Node.js 18+
- Telegram bot token
- Valid chat ID

## License
MIT

```

--------------------------------------------------------------------------------
/config.example.json:
--------------------------------------------------------------------------------

```json
{
  "telegram": {
    "botToken": "YOUR_TELEGRAM_BOT_TOKEN_HERE",
    "allowedChatIds": [
      1234567890
    ]
  }
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "strict": true,
    "outDir": "./build",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "telegram-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "main": "build/index.js",
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "start": "node build/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "node-telegram-bot-api": "^0.61.0"
  },
  "devDependencies": {
    "@types/node": "^22.10.7",
    "@types/node-telegram-bot-api": "^0.64.7",
    "typescript": "^5.3.3"
  }
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import TelegramBot from 'node-telegram-bot-api';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

const TELEGRAM_TOKEN = process.env.TELEGRAM_BOT_TOKEN as string;
if (!TELEGRAM_TOKEN) {
  throw new Error('TELEGRAM_BOT_TOKEN environment variable is required');
}

class TelegramServer {
  private server: Server;
  private bot: TelegramBot;

  constructor() {
    this.server = new Server(
      {
        name: 'telegram-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.bot = new TelegramBot(TELEGRAM_TOKEN, { polling: true });
    this.setupBotHandlers();
    this.setupServerHandlers();
  }

  private setupBotHandlers() {
    this.bot.on('message', (msg: TelegramBot.Message) => {
      const chatId = msg.chat.id;
      console.log(`Received message from chat ID: ${chatId}`);
      if (!msg.text) return;
      
      // Echo messages back to user
      this.bot.sendMessage(chatId, `You said: ${msg.text}`);
    });
  }

  private setupServerHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'send_message',
          description: 'Send a message through Telegram',
          inputSchema: {
            type: 'object',
            properties: {
              chatId: { type: 'number' },
              message: { type: 'string' }
            },
            required: ['chatId', 'message']
          }
        }
      ]
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'send_message') {
        if (!request.params.arguments || typeof request.params.arguments !== 'object') {
          throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
        }
        
        const { chatId, message } = request.params.arguments;
        if (typeof chatId !== 'number' || typeof message !== 'string') {
          throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
        }
        await this.bot.sendMessage(chatId, message);
        return {
          content: [{
            type: 'text',
            text: 'Message sent successfully'
          }]
        };
      }
      throw new McpError(ErrorCode.MethodNotFound, 'Unknown tool');
    });

    // Error handling
    this.server.onerror = (error) => console.error('[MCP Error]', error);
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Telegram MCP server running on stdio');
  }
}

const server = new TelegramServer();
server.run().catch(console.error);

```

--------------------------------------------------------------------------------
/src/telegram-server/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import TelegramBot from 'node-telegram-bot-api';
import { 
  CallToolRequestSchema, 
  ErrorCode, 
  ListToolsRequestSchema, 
  McpError 
} from '@modelcontextprotocol/sdk/types.js';

// Extended error codes
const ExtendedErrorCode = {
  ...ErrorCode,
  Timeout: 1000
} as const;

import config from '../../config.json';
import path from 'path';

if (!config?.telegram?.botToken) {
  throw new Error('Missing telegram.botToken in config.json');
}

if (!config.telegram.allowedChatIds || !Array.isArray(config.telegram.allowedChatIds)) {
  throw new Error('Missing or invalid telegram.allowedChatIds in config.json');
}

const TELEGRAM_TOKEN = config.telegram.botToken;
const ALLOWED_CHAT_IDS = new Set(config.telegram.allowedChatIds);

interface PendingRequest {
  resolve: (value: string) => void;
  reject: (reason?: any) => void;
  timeout: NodeJS.Timeout;
}

class TelegramServer {
  private server: Server;
  private bot: TelegramBot;
  private pendingRequests: Map<number, PendingRequest> = new Map();
  private requestTimeout = 300000; // 5 minutes

  constructor() {
    this.server = new Server({
      name: 'telegram-server',
      version: '2.0.0',
    }, {
      capabilities: {
        resources: {},
        tools: {},
      },
    });

    this.bot = new TelegramBot(TELEGRAM_TOKEN, { polling: true });
    console.log('Telegram bot initialized successfully');
    this.setupBotHandlers();
    this.setupServerHandlers();
  }

  private setupBotHandlers() {
    this.bot.on('message', (msg) => {
      console.log(`Received message from chat ${msg.chat.id}: ${msg.text}`);
      const chatId = msg.chat.id;
      if (!ALLOWED_CHAT_IDS.has(chatId)) {
        console.warn(`Received message from unauthorized chat ID: ${chatId}`);
        return;
      }
      const messageText = msg.text ?? '';
      
      // Skip if we don't have valid text content
      if (!messageText.trim()) {
        if (this.pendingRequests.has(chatId)) {
          const pendingRequest = this.pendingRequests.get(chatId);
          if (pendingRequest) {
            clearTimeout(pendingRequest.timeout);
            this.pendingRequests.delete(chatId);
            pendingRequest.reject(new McpError(ErrorCode.InvalidParams, 'Invalid message type'));
          }
        }
        return;
      }

      // Check if this is a response to a pending request
      const pendingRequest = this.pendingRequests.get(chatId);
      if (pendingRequest) {
        clearTimeout(pendingRequest.timeout);
        this.pendingRequests.delete(chatId);
        pendingRequest.resolve(messageText);
        return;
      }

      // Default behavior for non-request messages
      console.log(`Sending response to chat ${chatId}`);
      this.bot.sendMessage(chatId, `You said: ${messageText}`);
    });
  }

  private setupServerHandlers() {
    console.log('Setting up MCP server handlers');
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'send_message',
          description: 'Send a message through Telegram',
          inputSchema: {
            type: 'object',
            properties: {
              chatId: { type: 'number' },
              message: { type: 'string' }
            },
            required: ['chatId', 'message']
          }
        },
        {
          name: 'request_user_input',
          description: 'Request user input through Telegram',
          inputSchema: {
            type: 'object',
            properties: {
              chatId: { type: 'number' },
              prompt: { type: 'string' }
            },
            required: ['chatId', 'prompt']
          }
        }
      ]
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'send_message':
          return this.handleSendMessage(request.params.arguments);
        case 'request_user_input':
          return this.handleRequestUserInput(request.params.arguments);
        default:
          throw new McpError(ErrorCode.MethodNotFound, 'Unknown tool');
      }
    });

    // Error handling
    this.server.onerror = (error) => {
      console.error('[MCP Error]', error);
      console.log('Attempting to recover from error...');
    };
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  private async handleSendMessage(args: any) {
    if (!args || typeof args !== 'object') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
    }
    const { chatId, message } = args;
    if (typeof chatId !== 'number' || typeof message !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
    }
    await this.bot.sendMessage(chatId, message);
    return {
      content: [{
        type: 'text',
        text: 'Message sent successfully'
      }]
    };
  }

  private async handleRequestUserInput(args: any) {
    if (!args || typeof args !== 'object') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid arguments');
    }
    const { chatId, prompt } = args;
    if (typeof chatId !== 'number' || typeof prompt !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid argument types');
    }

    // Send the prompt to the user
    await this.bot.sendMessage(chatId, prompt);

    // Create and return a promise that resolves when we get a response
    return new Promise<string>((resolve, reject) => {
      const timeout = setTimeout(() => {
        this.pendingRequests.delete(chatId);
        reject(new McpError(ExtendedErrorCode.Timeout, 'Request timed out'));
      }, this.requestTimeout);

      this.pendingRequests.set(chatId, {
        resolve,
        reject,
        timeout
      });
    }).then(response => ({
      content: [{
        type: 'text',
        text: response
      }]
    }));
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.log('Telegram MCP server running on stdio');
    console.log('Bot username:', this.bot.getMe());
    console.log('MCP server initialized with tools:', [
      'send_message',
      'request_user_input'
    ]);
  }
}

const server = new TelegramServer();
server.run().catch(console.error);

```