Skip to main content
Glama
simple-server.ts167 kB
import 'dotenv/config'; import express from 'express'; import cors from 'cors'; import TelegramBot from 'node-telegram-bot-api'; import { WordPressAPI, WordPressConfig } from './wordpress-api.js'; const app = express(); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); app.use(cors()); // Get environment variables with fallback defaults for Railway const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '8198346055:AAG01qXWGBwP4qzDlkZztPwshDdYw_DLFN0'; const CHANNEL_ID = process.env.TELEGRAM_CHANNEL_ID || '@mymcptest'; const PEXELS_API_KEY = process.env.PEXELS_API_KEY || 'j0CGX7JRiZOmEz1bEYvxZeRn1cS7qdFy5351PmDtZ01Wnby18AIeWt32'; const YANDEX_CLIENT_ID = process.env.YANDEX_CLIENT_ID || '11221f6ebd2d47649d42d9f4b282a876'; const YANDEX_CLIENT_SECRET = process.env.YANDEX_CLIENT_SECRET || 'eb793370893544d683bf277d14bfd842'; const YANDEX_LOGIN = process.env.YANDEX_LOGIN || 'bobi-dk91'; let YANDEX_OAUTH_TOKEN = process.env.YANDEX_OAUTH_TOKEN || ''; // WordPress Configuration const WORDPRESS_URL = process.env.WORDPRESS_URL || ''; const WORDPRESS_USERNAME = process.env.WORDPRESS_USERNAME || ''; const WORDPRESS_PASSWORD = process.env.WORDPRESS_PASSWORD || ''; const WORDPRESS_APPLICATION_PASSWORD = process.env.WORDPRESS_APPLICATION_PASSWORD || ''; // Initialize WordPress API let wordpressAPI: WordPressAPI | null = null; if (WORDPRESS_URL && WORDPRESS_USERNAME && (WORDPRESS_PASSWORD || WORDPRESS_APPLICATION_PASSWORD)) { try { const wpConfig: WordPressConfig = { url: WORDPRESS_URL, username: WORDPRESS_USERNAME, password: WORDPRESS_PASSWORD, applicationPassword: WORDPRESS_APPLICATION_PASSWORD }; wordpressAPI = new WordPressAPI(wpConfig); console.log('🌐 WordPress API initialized'); } catch (error) { console.error('❌ Failed to initialize WordPress API:', error); } } // Log environment status if (process.env.TELEGRAM_BOT_TOKEN) { console.log('🔧 Using TELEGRAM_BOT_TOKEN from environment variables'); } else { console.log('⚠️ Using default TELEGRAM_BOT_TOKEN (consider setting environment variable)'); } if (process.env.TELEGRAM_CHANNEL_ID) { console.log('🔧 Using TELEGRAM_CHANNEL_ID from environment variables'); } else { console.log('⚠️ Using default TELEGRAM_CHANNEL_ID (consider setting environment variable)'); } console.log('🔧 Environment variables:'); console.log('TELEGRAM_BOT_TOKEN:', TELEGRAM_BOT_TOKEN ? 'SET' : 'NOT SET'); console.log('TELEGRAM_CHANNEL_ID:', CHANNEL_ID); console.log('PEXELS_API_KEY:', PEXELS_API_KEY ? 'SET' : 'NOT SET'); console.log('YANDEX_CLIENT_ID:', YANDEX_CLIENT_ID ? 'SET' : 'NOT SET'); console.log('YANDEX_OAUTH_TOKEN:', YANDEX_OAUTH_TOKEN ? 'SET' : 'NOT SET'); console.log('WORDPRESS_URL:', WORDPRESS_URL ? 'SET' : 'NOT SET'); console.log('WORDPRESS_USERNAME:', WORDPRESS_USERNAME ? 'SET' : 'NOT SET'); console.log('WORDPRESS_PASSWORD:', WORDPRESS_PASSWORD ? 'SET' : 'NOT SET'); console.log('WORDPRESS_APPLICATION_PASSWORD:', WORDPRESS_APPLICATION_PASSWORD ? 'SET' : 'NOT SET'); // Initialize Telegram Bot let bot; try { bot = new TelegramBot(TELEGRAM_BOT_TOKEN, { polling: false }); console.log('🤖 Telegram Bot initialized'); } catch (error) { console.error('❌ Failed to initialize Telegram Bot:', error); bot = null; } // Pexels API helper functions async function pexelsRequest(endpoint: string, params: any = {}) { const url = new URL(`https://api.pexels.com/v1${endpoint}`); Object.keys(params).forEach(key => { if (params[key] !== undefined && params[key] !== null) { url.searchParams.append(key, params[key].toString()); } }); const response = await fetch(url.toString(), { headers: { 'Authorization': PEXELS_API_KEY, 'User-Agent': 'Telegram-MCP-Server/2.1.0' } }); if (!response.ok) { throw new Error(`Pexels API error: ${response.statusText}`); } return await response.json(); } // Yandex Wordstat API helper functions async function yandexWordstatRequest(method: string, params: any = {}) { if (!YANDEX_OAUTH_TOKEN) { throw new Error('Yandex OAuth token not set. Please authorize first using /yandex/auth endpoint'); } const requestBody = { method: method, params: params }; const response = await fetch('https://api.direct.yandex.com/json/v5/keywordsresearch', { method: 'POST', headers: { 'Authorization': `Bearer ${YANDEX_OAUTH_TOKEN}`, 'Client-Login': YANDEX_LOGIN, 'Content-Type': 'application/json', 'Accept-Language': 'ru', 'skipReportHeader': 'true', 'skipColumnHeader': 'true' }, body: JSON.stringify(requestBody) }); const responseText = await response.text(); if (!response.ok) { throw new Error(`Yandex API error (${response.status}): ${responseText}`); } try { return JSON.parse(responseText); } catch (e) { throw new Error(`Failed to parse Yandex API response: ${responseText}`); } } // OAuth helper to exchange code for token async function getYandexOAuthToken(code: string) { const params = new URLSearchParams({ grant_type: 'authorization_code', code: code, client_id: YANDEX_CLIENT_ID, client_secret: YANDEX_CLIENT_SECRET }); const response = await fetch('https://oauth.yandex.ru/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() }); if (!response.ok) { throw new Error(`OAuth error: ${response.statusText}`); } const data = await response.json(); YANDEX_OAUTH_TOKEN = data.access_token; return data; } // Root endpoint - GET app.get('/', (req, res) => { res.json({ name: 'telegram-mcp-server', version: '2.3.0', status: 'running', description: 'Telegram MCP Server v2.2.0 with Pexels, Yandex Wordstat & WordPress - HTTP API for ChatGPT and Make.com', environment: { TELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN ? 'SET' : 'NOT SET', TELEGRAM_CHANNEL_ID: CHANNEL_ID, WORDPRESS_URL: WORDPRESS_URL ? 'SET' : 'NOT SET', WORDPRESS_USERNAME: WORDPRESS_USERNAME ? 'SET' : 'NOT SET', PEXELS_API_KEY: PEXELS_API_KEY ? 'SET' : 'NOT SET', YANDEX_OAUTH_TOKEN: YANDEX_OAUTH_TOKEN ? 'SET' : 'NOT SET', NODE_ENV: process.env.NODE_ENV || 'development', PORT: process.env.PORT || 8080 }, endpoints: { info: '/mcp', tools: '/mcp/tools/list', call: '/mcp/tools/call', health: '/health' }, channel: CHANNEL_ID }); }); // MCP tools/list endpoint - GET (for direct access) app.get('/tools/list', (req, res) => { res.json({ jsonrpc: '2.0', id: null, result: { tools: [ { name: 'send_message', description: 'Send a text message to the Telegram channel', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Message text to send', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message (HTML or Markdown)', }, }, required: ['text'], }, }, { name: 'send_photo', description: 'Send a photo to the Telegram channel', inputSchema: { type: 'object', properties: { photo: { type: 'string', description: 'Photo URL or file path', }, caption: { type: 'string', description: 'Photo caption', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption', }, }, required: ['photo'], }, }, { name: 'send_video', description: 'Send a video to the Telegram channel', inputSchema: { type: 'object', properties: { video: { type: 'string', description: 'Video URL or file path', }, caption: { type: 'string', description: 'Video caption', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption', }, }, required: ['video'], }, }, { name: 'send_document', description: 'Send a document to the Telegram channel', inputSchema: { type: 'object', properties: { document: { type: 'string', description: 'Document URL or file path', }, caption: { type: 'string', description: 'Document caption', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption', }, }, required: ['document'], }, }, { name: 'send_poll', description: 'Send a poll to the Telegram channel', inputSchema: { type: 'object', properties: { question: { type: 'string', description: 'Poll question', }, options: { type: 'array', items: { type: 'string' }, description: 'Poll options', }, is_anonymous: { type: 'boolean', description: 'Whether the poll is anonymous', }, }, required: ['question', 'options'], }, }, { name: 'send_reaction', description: 'Send a reaction to a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to react to', }, reaction: { type: 'string', description: 'Reaction emoji', }, }, required: ['message_id', 'reaction'], }, }, { name: 'edit_message', description: 'Edit a message text', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to edit', }, text: { type: 'string', description: 'New message text', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message', }, }, required: ['message_id', 'text'], }, }, { name: 'delete_message', description: 'Delete a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to delete', }, }, required: ['message_id'], }, }, { name: 'pin_message', description: 'Pin a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to pin', }, }, required: ['message_id'], }, }, { name: 'unpin_message', description: 'Unpin a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to unpin', }, }, required: ['message_id'], }, }, { name: 'get_channel_info', description: 'Get channel information', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_channel_stats', description: 'Get channel statistics', inputSchema: { type: 'object', properties: {}, }, }, ] } }); }); // Root endpoint - POST (for Make.com MCP connection) app.post('/', async (req, res) => { console.log('📨 POST request to root endpoint:', JSON.stringify(req.body, null, 2)); // Handle MCP protocol requests const { jsonrpc, method, params, id } = req.body; // Validate MCP request format if (!jsonrpc || jsonrpc !== '2.0') { console.log('❌ Invalid JSON-RPC version:', jsonrpc); res.status(400).json({ jsonrpc: '2.0', id: id || null, error: { code: -32600, message: 'Invalid Request: jsonrpc must be "2.0"' } }); return; } if (!method) { console.log('❌ Missing method in request'); res.status(400).json({ jsonrpc: '2.0', id: id || null, error: { code: -32600, message: 'Invalid Request: method is required' } }); return; } if (jsonrpc === '2.0' && method) { // This is an MCP request switch (method) { case 'initialize': res.json({ jsonrpc: '2.0', id, result: { protocolVersion: '2024-11-05', capabilities: { tools: {} }, serverInfo: { name: 'telegram-mcp-server', version: '2.2.0' } } }); break; case 'tools/list': res.json({ jsonrpc: '2.0', id, result: { tools: [ { name: 'send_message', description: 'Send a text message to the Telegram channel', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Message text to send', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message (HTML or Markdown)', }, }, required: ['text'], }, }, { name: 'send_photo', description: 'Send a photo to the Telegram channel', inputSchema: { type: 'object', properties: { photo: { type: 'string', description: 'Photo URL or file path', }, caption: { type: 'string', description: 'Photo caption', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption', }, }, required: ['photo'], }, }, { name: 'send_video', description: 'Send a video to the Telegram channel', inputSchema: { type: 'object', properties: { video: { type: 'string', description: 'Video URL or file path', }, caption: { type: 'string', description: 'Video caption', }, duration: { type: 'number', description: 'Video duration in seconds', }, width: { type: 'number', description: 'Video width', }, height: { type: 'number', description: 'Video height', }, }, required: ['video'], }, }, { name: 'send_document', description: 'Send a document to the Telegram channel', inputSchema: { type: 'object', properties: { document: { type: 'string', description: 'Document URL or file path', }, caption: { type: 'string', description: 'Document caption', }, filename: { type: 'string', description: 'Custom filename for the document', }, }, required: ['document'], }, }, { name: 'send_poll', description: 'Send a poll to the Telegram channel', inputSchema: { type: 'object', properties: { question: { type: 'string', description: 'Poll question', }, options: { type: 'array', items: { type: 'string' }, description: 'Poll options (2-10 options)', }, is_anonymous: { type: 'boolean', description: 'Whether the poll is anonymous', }, type: { type: 'string', enum: ['quiz', 'regular'], description: 'Poll type', }, correct_option_id: { type: 'number', description: 'Correct option ID for quiz polls', }, explanation: { type: 'string', description: 'Explanation for quiz polls', }, }, required: ['question', 'options'], }, }, { name: 'send_reaction', description: 'Send a reaction to a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to react to', }, emoji: { type: 'string', description: 'Emoji to react with', }, }, required: ['message_id', 'emoji'], }, }, { name: 'edit_message', description: 'Edit a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to edit', }, text: { type: 'string', description: 'New message text', }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message', }, }, required: ['message_id', 'text'], }, }, { name: 'delete_message', description: 'Delete a message from the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to delete', }, }, required: ['message_id'], }, }, { name: 'pin_message', description: 'Pin a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to pin', }, disable_notification: { type: 'boolean', description: 'Whether to disable notification for pinned message', }, }, required: ['message_id'], }, }, { name: 'unpin_message', description: 'Unpin a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to unpin (optional, unpins all if not specified)', }, }, }, }, { name: 'get_channel_info', description: 'Get information about the Telegram channel', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_channel_stats', description: 'Get channel statistics and member count', inputSchema: { type: 'object', properties: {}, }, }, { name: 'pexels_search_photos', description: 'Search for photos on Pexels by query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (e.g., "nature", "cats", "city")', }, per_page: { type: 'number', description: 'Number of results per page (1-80, default: 15)', }, page: { type: 'number', description: 'Page number (default: 1)', }, orientation: { type: 'string', enum: ['landscape', 'portrait', 'square'], description: 'Photo orientation filter', }, size: { type: 'string', enum: ['large', 'medium', 'small'], description: 'Minimum photo size', }, color: { type: 'string', description: 'Desired photo color (e.g., "red", "blue", "#FF0000")', }, }, required: ['query'], }, }, { name: 'pexels_get_photo', description: 'Get a specific photo by ID from Pexels', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Photo ID from Pexels', }, }, required: ['id'], }, }, { name: 'pexels_curated_photos', description: 'Get curated photos from Pexels', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of results per page (1-80, default: 15)', }, page: { type: 'number', description: 'Page number (default: 1)', }, }, }, }, { name: 'pexels_search_videos', description: 'Search for videos on Pexels by query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (e.g., "nature", "ocean", "city")', }, per_page: { type: 'number', description: 'Number of results per page (1-80, default: 15)', }, page: { type: 'number', description: 'Page number (default: 1)', }, orientation: { type: 'string', enum: ['landscape', 'portrait', 'square'], description: 'Video orientation filter', }, size: { type: 'string', enum: ['large', 'medium', 'small'], description: 'Minimum video size', }, }, required: ['query'], }, }, { name: 'pexels_popular_videos', description: 'Get popular videos from Pexels', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of results per page (1-80, default: 15)', }, page: { type: 'number', description: 'Page number (default: 1)', }, }, }, }, { name: 'yandex_wordstat_search', description: 'Search Yandex Wordstat for keyword statistics', inputSchema: { type: 'object', properties: { phrases: { type: 'array', items: { type: 'string' }, description: 'Keywords to search (1-10 phrases)', }, geo_ids: { type: 'array', items: { type: 'number' }, description: 'Geo IDs (225=Russia, 213=Moscow, 2=SPb)', }, }, required: ['phrases'], }, }, { name: 'yandex_wordstat_keywords', description: 'Get keyword suggestions from Yandex Wordstat', inputSchema: { type: 'object', properties: { phrase: { type: 'string', description: 'Base keyword for suggestions', }, geo_ids: { type: 'array', items: { type: 'number' }, description: 'Geo IDs (default: [225] Russia)', }, }, required: ['phrase'], }, }, { name: 'yandex_wordstat_related', description: 'Get related search queries from Yandex Wordstat', inputSchema: { type: 'object', properties: { phrase: { type: 'string', description: 'Base keyword for related queries', }, geo_ids: { type: 'array', items: { type: 'number' }, description: 'Geo IDs (default: [225] Russia)', }, }, required: ['phrase'], }, }, // WordPress Posts { name: 'wordpress_get_posts', description: 'Get WordPress posts with optional filtering', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Posts per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, status: { type: 'string', description: 'Post status (publish, draft, private, pending)' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' }, author: { type: 'number', description: 'Author ID' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['date', 'id', 'include', 'relevance', 'slug', 'title'], description: 'Sort by field' } } } }, { name: 'wordpress_get_post', description: 'Get a specific WordPress post by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' } }, required: ['id'] } }, { name: 'wordpress_create_post', description: 'Create a new WordPress post', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Post title' }, content: { type: 'string', description: 'Post content (HTML)' }, excerpt: { type: 'string', description: 'Post excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Post status' }, slug: { type: 'string', description: 'Post slug' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' }, featured_media: { type: 'number', description: 'Featured media ID' }, author: { type: 'number', description: 'Author ID' }, sticky: { type: 'boolean', description: 'Make post sticky' }, comment_status: { type: 'string', enum: ['open', 'closed'], description: 'Comment status' }, ping_status: { type: 'string', enum: ['open', 'closed'], description: 'Ping status' } }, required: ['title', 'content'] } }, { name: 'wordpress_update_post', description: 'Update an existing WordPress post', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' }, title: { type: 'string', description: 'Post title' }, content: { type: 'string', description: 'Post content (HTML)' }, excerpt: { type: 'string', description: 'Post excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Post status' }, slug: { type: 'string', description: 'Post slug' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' }, featured_media: { type: 'number', description: 'Featured media ID' }, author: { type: 'number', description: 'Author ID' }, sticky: { type: 'boolean', description: 'Make post sticky' }, comment_status: { type: 'string', enum: ['open', 'closed'], description: 'Comment status' }, ping_status: { type: 'string', enum: ['open', 'closed'], description: 'Ping status' } }, required: ['id'] } }, { name: 'wordpress_delete_post', description: 'Delete a WordPress post', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, // WordPress Pages { name: 'wordpress_get_pages', description: 'Get WordPress pages with optional filtering', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Pages per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, status: { type: 'string', description: 'Page status (publish, draft, private, pending)' }, parent: { type: 'number', description: 'Parent page ID' }, author: { type: 'number', description: 'Author ID' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['date', 'id', 'include', 'relevance', 'slug', 'title'], description: 'Sort by field' } } } }, { name: 'wordpress_get_page', description: 'Get a specific WordPress page by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' } }, required: ['id'] } }, { name: 'wordpress_create_page', description: 'Create a new WordPress page', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Page title' }, content: { type: 'string', description: 'Page content (HTML)' }, excerpt: { type: 'string', description: 'Page excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Page status' }, slug: { type: 'string', description: 'Page slug' }, parent: { type: 'number', description: 'Parent page ID' }, menu_order: { type: 'number', description: 'Menu order' }, featured_media: { type: 'number', description: 'Featured media ID' }, author: { type: 'number', description: 'Author ID' }, template: { type: 'string', description: 'Page template' }, comment_status: { type: 'string', enum: ['open', 'closed'], description: 'Comment status' }, ping_status: { type: 'string', enum: ['open', 'closed'], description: 'Ping status' } }, required: ['title', 'content'] } }, { name: 'wordpress_update_page', description: 'Update an existing WordPress page', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' }, title: { type: 'string', description: 'Page title' }, content: { type: 'string', description: 'Page content (HTML)' }, excerpt: { type: 'string', description: 'Page excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Page status' }, slug: { type: 'string', description: 'Page slug' }, parent: { type: 'number', description: 'Parent page ID' }, menu_order: { type: 'number', description: 'Menu order' }, featured_media: { type: 'number', description: 'Featured media ID' }, author: { type: 'number', description: 'Author ID' }, template: { type: 'string', description: 'Page template' }, comment_status: { type: 'string', enum: ['open', 'closed'], description: 'Comment status' }, ping_status: { type: 'string', enum: ['open', 'closed'], description: 'Ping status' } }, required: ['id'] } }, { name: 'wordpress_delete_page', description: 'Delete a WordPress page', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, // WordPress Media { name: 'wordpress_get_media', description: 'Get WordPress media items with optional filtering', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Media per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, media_type: { type: 'string', enum: ['image', 'video', 'audio', 'application'], description: 'Media type' }, mime_type: { type: 'string', description: 'MIME type filter' }, parent: { type: 'number', description: 'Parent post/page ID' }, author: { type: 'number', description: 'Author ID' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['date', 'id', 'include', 'relevance', 'slug', 'title'], description: 'Sort by field' } } } }, { name: 'wordpress_get_media_item', description: 'Get a specific WordPress media item by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' } }, required: ['id'] } }, { name: 'wordpress_upload_media', description: 'Upload a media file to WordPress', inputSchema: { type: 'object', properties: { file_url: { type: 'string', description: 'URL of the file to upload' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_url', 'filename'] } }, { name: 'wordpress_upload_media_binary', description: 'Upload a media file directly as binary data to WordPress (more efficient for large files)', inputSchema: { type: 'object', properties: { file_data: { type: 'string', description: 'Base64 encoded file data' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, mime_type: { type: 'string', description: 'MIME type of the file (e.g., image/png, image/jpeg)' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_data', 'filename', 'mime_type'] } }, { name: 'wordpress_update_media_metadata', description: 'Update media metadata (alt_text, caption, description)', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['id'] } }, { name: 'wordpress_upload_media_direct', description: 'Upload a media file directly as binary data to WordPress without JSON (for large files)', inputSchema: { type: 'object', properties: { file_data: { type: 'string', description: 'Base64 encoded file data' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, mime_type: { type: 'string', description: 'MIME type of the file (e.g., image/png, image/jpeg)' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_data', 'filename', 'mime_type'] } }, { name: 'wordpress_update_media', description: 'Update a WordPress media item', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['id'] } }, { name: 'wordpress_delete_media', description: 'Delete a WordPress media item', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, // WordPress Categories { name: 'wordpress_get_categories', description: 'Get WordPress categories', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Categories per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, hide_empty: { type: 'boolean', description: 'Hide empty categories' }, parent: { type: 'number', description: 'Parent category ID' }, post: { type: 'number', description: 'Post ID to get categories for' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['id', 'include', 'name', 'slug', 'term_group', 'description', 'count'], description: 'Sort by field' } } } }, { name: 'wordpress_get_category', description: 'Get a specific WordPress category by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' } }, required: ['id'] } }, { name: 'wordpress_create_category', description: 'Create a new WordPress category', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Category name' }, description: { type: 'string', description: 'Category description' }, slug: { type: 'string', description: 'Category slug' }, parent: { type: 'number', description: 'Parent category ID' } }, required: ['name'] } }, { name: 'wordpress_update_category', description: 'Update a WordPress category', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' }, name: { type: 'string', description: 'Category name' }, description: { type: 'string', description: 'Category description' }, slug: { type: 'string', description: 'Category slug' }, parent: { type: 'number', description: 'Parent category ID' } }, required: ['id'] } }, { name: 'wordpress_delete_category', description: 'Delete a WordPress category', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' }, force: { type: 'boolean', description: 'Force delete' } }, required: ['id'] } }, // WordPress Tags { name: 'wordpress_get_tags', description: 'Get WordPress tags', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Tags per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, hide_empty: { type: 'boolean', description: 'Hide empty tags' }, post: { type: 'number', description: 'Post ID to get tags for' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['id', 'include', 'name', 'slug', 'term_group', 'description', 'count'], description: 'Sort by field' } } } }, { name: 'wordpress_get_tag', description: 'Get a specific WordPress tag by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' } }, required: ['id'] } }, { name: 'wordpress_create_tag', description: 'Create a new WordPress tag', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tag name' }, description: { type: 'string', description: 'Tag description' }, slug: { type: 'string', description: 'Tag slug' } }, required: ['name'] } }, { name: 'wordpress_update_tag', description: 'Update a WordPress tag', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' }, name: { type: 'string', description: 'Tag name' }, description: { type: 'string', description: 'Tag description' }, slug: { type: 'string', description: 'Tag slug' } }, required: ['id'] } }, { name: 'wordpress_delete_tag', description: 'Delete a WordPress tag', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' }, force: { type: 'boolean', description: 'Force delete' } }, required: ['id'] } }, // WordPress Users { name: 'wordpress_get_users', description: 'Get WordPress users', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Users per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, roles: { type: 'array', items: { type: 'string' }, description: 'User roles' }, who: { type: 'string', enum: ['authors'], description: 'Filter by user type' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['id', 'include', 'name', 'registered_date', 'slug', 'email', 'url'], description: 'Sort by field' } } } }, { name: 'wordpress_get_user', description: 'Get a specific WordPress user by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'User ID' } }, required: ['id'] } }, { name: 'wordpress_create_user', description: 'Create a new WordPress user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'Username' }, email: { type: 'string', description: 'Email address' }, password: { type: 'string', description: 'Password' }, first_name: { type: 'string', description: 'First name' }, last_name: { type: 'string', description: 'Last name' }, name: { type: 'string', description: 'Display name' }, url: { type: 'string', description: 'Website URL' }, description: { type: 'string', description: 'User description' }, roles: { type: 'array', items: { type: 'string' }, description: 'User roles' } }, required: ['username', 'email', 'password'] } }, { name: 'wordpress_update_user', description: 'Update a WordPress user', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'User ID' }, username: { type: 'string', description: 'Username' }, email: { type: 'string', description: 'Email address' }, password: { type: 'string', description: 'Password' }, first_name: { type: 'string', description: 'First name' }, last_name: { type: 'string', description: 'Last name' }, name: { type: 'string', description: 'Display name' }, url: { type: 'string', description: 'Website URL' }, description: { type: 'string', description: 'User description' }, roles: { type: 'array', items: { type: 'string' }, description: 'User roles' } }, required: ['id'] } }, { name: 'wordpress_delete_user', description: 'Delete a WordPress user', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'User ID' }, force: { type: 'boolean', description: 'Force delete' }, reassign: { type: 'number', description: 'Reassign posts to this user ID' } }, required: ['id'] } }, // WordPress Comments { name: 'wordpress_get_comments', description: 'Get WordPress comments', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Page number (default: 1)' }, per_page: { type: 'number', description: 'Comments per page (default: 10, max: 100)' }, search: { type: 'string', description: 'Search query' }, post: { type: 'number', description: 'Post ID' }, status: { type: 'string', enum: ['hold', 'approve', 'spam', 'trash'], description: 'Comment status' }, parent: { type: 'number', description: 'Parent comment ID' }, author: { type: 'number', description: 'Author ID' }, order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, orderby: { type: 'string', enum: ['date', 'date_gmt', 'id', 'include', 'post', 'parent', 'type'], description: 'Sort by field' } } } }, { name: 'wordpress_get_comment', description: 'Get a specific WordPress comment by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' } }, required: ['id'] } }, { name: 'wordpress_create_comment', description: 'Create a new WordPress comment', inputSchema: { type: 'object', properties: { post: { type: 'number', description: 'Post ID' }, content: { type: 'string', description: 'Comment content' }, author_name: { type: 'string', description: 'Author name' }, author_email: { type: 'string', description: 'Author email' }, author_url: { type: 'string', description: 'Author website' }, parent: { type: 'number', description: 'Parent comment ID' }, status: { type: 'string', enum: ['hold', 'approve', 'spam', 'trash'], description: 'Comment status' } }, required: ['post', 'content'] } }, { name: 'wordpress_update_comment', description: 'Update a WordPress comment', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' }, content: { type: 'string', description: 'Comment content' }, author_name: { type: 'string', description: 'Author name' }, author_email: { type: 'string', description: 'Author email' }, author_url: { type: 'string', description: 'Author website' }, status: { type: 'string', enum: ['hold', 'approve', 'spam', 'trash'], description: 'Comment status' } }, required: ['id'] } }, { name: 'wordpress_delete_comment', description: 'Delete a WordPress comment', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, // WordPress Site Info { name: 'wordpress_get_site_info', description: 'Get WordPress site information', inputSchema: { type: 'object', properties: {} } }, { name: 'wordpress_get_settings', description: 'Get WordPress site settings', inputSchema: { type: 'object', properties: {} } }, { name: 'wordpress_search', description: 'Search WordPress content', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, type: { type: 'string', enum: ['post', 'page', 'attachment', 'any'], description: 'Content type to search' } }, required: ['query'] } }, { name: 'wordpress_test_connection', description: 'Test WordPress connection', inputSchema: { type: 'object', properties: {} } }, ] } }); break; case 'tools/call': const { name, arguments: args } = params || {}; if (!name) { res.json({ jsonrpc: '2.0', id, error: { code: -32602, message: 'Tool name is required' } }); return; } let result; switch (name) { case 'send_message': { const { text, parse_mode = 'HTML' } = args || {}; if (!text) { result = { success: false, error: 'Text is required for send_message' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { // Ensure proper UTF-8 encoding const encodedText = Buffer.from(text, 'utf8').toString('utf8'); const response = await bot.sendMessage(CHANNEL_ID, encodedText, { parse_mode: parse_mode as any, }); result = { success: true, message: 'Message sent to Telegram successfully!', message_id: response.message_id, text: response.text, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send message: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_photo': { const { photo, caption, parse_mode = 'HTML' } = args || {}; if (!photo) { result = { success: false, error: 'Photo URL is required for send_photo' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const response = await bot.sendPhoto(CHANNEL_ID, photo, { caption, parse_mode: parse_mode as any, }); result = { success: true, message: 'Photo sent to Telegram successfully!', message_id: response.message_id, photo: response.photo, caption: response.caption, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send photo: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_photo_binary': { const { file_data, filename, caption, parse_mode = 'HTML' } = args || {}; if (!file_data || !filename) { result = { success: false, error: 'File data and filename are required for send_photo_binary' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { // Convert base64 to Buffer const fileBuffer = Buffer.from(file_data, 'base64'); // Create a readable stream from buffer const { Readable } = await import('stream'); const stream = new Readable({ read() { this.push(fileBuffer); this.push(null); } }); const response = await bot.sendPhoto(CHANNEL_ID, stream, { caption, parse_mode: parse_mode as any, }); result = { success: true, message: 'Binary photo sent to Telegram successfully!', message_id: response.message_id, photo: response.photo, caption: response.caption, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send binary photo: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_video': { const { video, caption, duration, width, height, parse_mode = 'HTML' } = args || {}; if (!video) { result = { success: false, error: 'Video URL is required for send_video' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const response = await bot.sendVideo(CHANNEL_ID, video, { caption, duration, width, height, parse_mode: parse_mode as any, }); result = { success: true, message: 'Video sent to Telegram successfully!', message_id: response.message_id, video: response.video, caption: response.caption, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send video: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_document': { const { document, caption, filename, parse_mode = 'HTML' } = args || {}; if (!document) { result = { success: false, error: 'Document URL is required for send_document' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const response = await bot.sendDocument(CHANNEL_ID, document, { caption, parse_mode: parse_mode as any, }); result = { success: true, message: 'Document sent to Telegram successfully!', message_id: response.message_id, document: response.document, caption: response.caption, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send document: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_poll': { const { question, options, is_anonymous = true, type = 'regular', correct_option_id, explanation } = args || {}; if (!question || !options || options.length < 2) { result = { success: false, error: 'Question and at least 2 options are required for send_poll' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const pollOptions: any = { question, options, is_anonymous, type }; if (type === 'quiz' && correct_option_id !== undefined) { pollOptions.correct_option_id = correct_option_id; if (explanation) { pollOptions.explanation = explanation; } } const response = await bot.sendPoll(CHANNEL_ID, question, options, pollOptions); result = { success: true, message: 'Poll sent to Telegram successfully!', message_id: response.message_id, poll: response.poll, channel: CHANNEL_ID, date: response.date, timestamp: new Date(response.date * 1000).toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to send poll: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'send_reaction': { const { message_id, emoji } = args || {}; if (!message_id || !emoji) { result = { success: false, error: 'Message ID and emoji are required for send_reaction' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const response = await bot.setMessageReaction(CHANNEL_ID, message_id, { reaction: [{ type: 'emoji', emoji }] }); result = { success: true, message: 'Reaction sent successfully!', message_id, emoji, channel: CHANNEL_ID }; } catch (error: any) { result = { success: false, error: `Failed to send reaction: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'edit_message': { const { message_id, text, parse_mode = 'HTML' } = args || {}; if (!message_id || !text) { result = { success: false, error: 'Message ID and text are required for edit_message' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { // Ensure proper UTF-8 encoding const encodedText = Buffer.from(text, 'utf8').toString('utf8'); const response = await bot.editMessageText(encodedText, { chat_id: CHANNEL_ID, message_id, parse_mode: parse_mode as any, }); result = { success: true, message: 'Message edited successfully!', message_id, text: typeof response === 'object' && response ? response.text : text, channel: CHANNEL_ID }; } catch (error: any) { result = { success: false, error: `Failed to edit message: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'delete_message': { const { message_id } = args || {}; if (!message_id) { result = { success: false, error: 'Message ID is required for delete_message' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { await bot.deleteMessage(CHANNEL_ID, message_id); result = { success: true, message: 'Message deleted successfully!', message_id, channel: CHANNEL_ID }; } catch (error: any) { result = { success: false, error: `Failed to delete message: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'pin_message': { const { message_id, disable_notification = false } = args || {}; if (!message_id) { result = { success: false, error: 'Message ID is required for pin_message' }; } else if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { await bot.pinChatMessage(CHANNEL_ID, message_id, { disable_notification }); result = { success: true, message: 'Message pinned successfully!', message_id, channel: CHANNEL_ID }; } catch (error: any) { result = { success: false, error: `Failed to pin message: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'unpin_message': { const { message_id } = args || {}; if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { if (message_id) { await bot.unpinChatMessage(CHANNEL_ID, message_id); result = { success: true, message: 'Message unpinned successfully!', message_id, channel: CHANNEL_ID }; } else { await bot.unpinAllChatMessages(CHANNEL_ID); result = { success: true, message: 'All messages unpinned successfully!', channel: CHANNEL_ID }; } } catch (error: any) { result = { success: false, error: `Failed to unpin message: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'get_channel_info': { if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const chat = await bot.getChat(CHANNEL_ID); result = { success: true, channel: CHANNEL_ID, title: chat.title, type: chat.type, description: chat.description || 'No description', username: chat.username || 'No username', id: chat.id }; } catch (error: any) { result = { success: false, error: `Failed to get channel info: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'get_channel_stats': { if (!bot) { result = { success: false, error: 'Telegram Bot not initialized. Check bot token.' }; } else { try { const chat = await bot.getChat(CHANNEL_ID); const memberCount = await bot.getChatMemberCount(CHANNEL_ID); result = { success: true, channel: CHANNEL_ID, title: chat.title, member_count: memberCount, type: chat.type, username: chat.username || 'No username', id: chat.id, timestamp: new Date().toLocaleString() }; } catch (error: any) { result = { success: false, error: `Failed to get channel stats: ${error.message}`, details: error.response?.body || error.message }; } } break; } case 'pexels_search_photos': { const { query, per_page = 15, page = 1, orientation, size, color } = args || {}; if (!query) { result = { success: false, error: 'Query is required for pexels_search_photos' }; } else { try { const params: any = { query, per_page, page }; if (orientation) params.orientation = orientation; if (size) params.size = size; if (color) params.color = color; const data = await pexelsRequest('/search', params); result = { success: true, total_results: data.total_results, page: data.page, per_page: data.per_page, photos: data.photos.map((photo: any) => ({ id: photo.id, width: photo.width, height: photo.height, url: photo.url, photographer: photo.photographer, photographer_url: photo.photographer_url, src: { original: photo.src.original, large: photo.src.large, medium: photo.src.medium, small: photo.src.small, tiny: photo.src.tiny }, alt: photo.alt })) }; } catch (error: any) { result = { success: false, error: `Failed to search photos: ${error.message}` }; } } break; } case 'pexels_get_photo': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Photo ID is required for pexels_get_photo' }; } else { try { const photo = await pexelsRequest(`/photos/${id}`); result = { success: true, photo: { id: photo.id, width: photo.width, height: photo.height, url: photo.url, photographer: photo.photographer, photographer_url: photo.photographer_url, src: { original: photo.src.original, large: photo.src.large, medium: photo.src.medium, small: photo.src.small, tiny: photo.src.tiny }, alt: photo.alt } }; } catch (error: any) { result = { success: false, error: `Failed to get photo: ${error.message}` }; } } break; } case 'pexels_curated_photos': { const { per_page = 15, page = 1 } = args || {}; try { const data = await pexelsRequest('/curated', { per_page, page }); result = { success: true, page: data.page, per_page: data.per_page, photos: data.photos.map((photo: any) => ({ id: photo.id, width: photo.width, height: photo.height, url: photo.url, photographer: photo.photographer, photographer_url: photo.photographer_url, src: { original: photo.src.original, large: photo.src.large, medium: photo.src.medium, small: photo.src.small, tiny: photo.src.tiny }, alt: photo.alt })) }; } catch (error: any) { result = { success: false, error: `Failed to get curated photos: ${error.message}` }; } break; } case 'pexels_search_videos': { const { query, per_page = 15, page = 1, orientation, size } = args || {}; if (!query) { result = { success: false, error: 'Query is required for pexels_search_videos' }; } else { try { const params: any = { query, per_page, page }; if (orientation) params.orientation = orientation; if (size) params.size = size; const data = await pexelsRequest('/videos/search', params); result = { success: true, total_results: data.total_results, page: data.page, per_page: data.per_page, videos: data.videos.map((video: any) => ({ id: video.id, width: video.width, height: video.height, url: video.url, duration: video.duration, user: { name: video.user.name, url: video.user.url }, video_files: video.video_files.map((file: any) => ({ id: file.id, quality: file.quality, file_type: file.file_type, width: file.width, height: file.height, link: file.link })) })) }; } catch (error: any) { result = { success: false, error: `Failed to search videos: ${error.message}` }; } } break; } case 'pexels_popular_videos': { const { per_page = 15, page = 1 } = args || {}; try { const data = await pexelsRequest('/videos/popular', { per_page, page }); result = { success: true, page: data.page, per_page: data.per_page, videos: data.videos.map((video: any) => ({ id: video.id, width: video.width, height: video.height, url: video.url, duration: video.duration, user: { name: video.user.name, url: video.user.url }, video_files: video.video_files.map((file: any) => ({ id: file.id, quality: file.quality, file_type: file.file_type, width: file.width, height: file.height, link: file.link })) })) }; } catch (error: any) { result = { success: false, error: `Failed to get popular videos: ${error.message}` }; } break; } case 'yandex_wordstat_search': { const { phrases, geo_ids = [225] } = args || {}; if (!phrases || !Array.isArray(phrases) || phrases.length === 0) { result = { success: false, error: 'Phrases array is required for yandex_wordstat_search (1-10 phrases)' }; } else { try { const response = await yandexWordstatRequest('get', { SelectionCriteria: { GeoIds: geo_ids }, Phrases: phrases, FieldNames: ['Keyword', 'Shows'] }); result = { success: true, data: response.result || response, phrases: phrases, geo_ids: geo_ids, full_api_response: response, note: 'Wordstat data for specified phrases and regions' }; } catch (error: any) { result = { success: false, error: `Failed to get Wordstat data: ${error.message}` }; } } break; } case 'yandex_wordstat_keywords': { const { phrase, geo_ids = [225] } = args || {}; if (!phrase) { result = { success: false, error: 'Phrase is required for yandex_wordstat_keywords' }; } else { try { const response = await yandexWordstatRequest('get', { SelectionCriteria: { GeoIds: geo_ids }, Phrases: [phrase], FieldNames: ['Keyword', 'Shows'] }); result = { success: true, phrase: phrase, geo_ids: geo_ids, full_response: response, keywords: response.result || response, note: 'Keyword suggestions based on Wordstat data' }; } catch (error: any) { result = { success: false, error: `Failed to get keywords: ${error.message}`, details: error.stack }; } } break; } case 'yandex_wordstat_related': { const { phrase, geo_ids = [225] } = args || {}; if (!phrase) { result = { success: false, error: 'Phrase is required for yandex_wordstat_related' }; } else { try { const response = await yandexWordstatRequest('get', { SelectionCriteria: { GeoIds: geo_ids }, Phrases: [phrase], FieldNames: ['Keyword', 'Shows'] }); // Extract related queries from response const relatedQueries = response.result?.SearchedWith || []; result = { success: true, phrase: phrase, geo_ids: geo_ids, full_response: response, related_queries: relatedQueries, count: relatedQueries.length, note: 'Related search queries from Yandex Wordstat' }; } catch (error: any) { result = { success: false, error: `Failed to get related queries: ${error.message}` }; } } break; } // WordPress Posts case 'wordpress_get_posts': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const posts = await wordpressAPI.getPosts(args || {}); result = { success: true, posts: posts, count: posts.length, note: 'WordPress posts retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get posts: ${error.message}` }; } } break; } case 'wordpress_get_post': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Post ID is required for wordpress_get_post' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const post = await wordpressAPI.getPost(id); result = { success: true, post: post, note: 'WordPress post retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get post: ${error.message}` }; } } break; } case 'wordpress_create_post': { const { title, content, ...otherFields } = args || {}; if (!title || !content) { result = { success: false, error: 'Title and content are required for wordpress_create_post' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const post = await wordpressAPI.createPost({ title, content, ...otherFields }); result = { success: true, post: post, note: 'WordPress post created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create post: ${error.message}` }; } } break; } case 'wordpress_update_post': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Post ID is required for wordpress_update_post' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const post = await wordpressAPI.updatePost(id, updateFields); result = { success: true, post: post, note: 'WordPress post updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update post: ${error.message}` }; } } break; } case 'wordpress_delete_post': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Post ID is required for wordpress_delete_post' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const post = await wordpressAPI.deletePost(id, force); result = { success: true, post: post, note: 'WordPress post deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete post: ${error.message}` }; } } break; } // WordPress Pages case 'wordpress_get_pages': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const pages = await wordpressAPI.getPages(args || {}); result = { success: true, pages: pages, count: pages.length, note: 'WordPress pages retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get pages: ${error.message}` }; } } break; } case 'wordpress_get_page': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Page ID is required for wordpress_get_page' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const page = await wordpressAPI.getPage(id); result = { success: true, page: page, note: 'WordPress page retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get page: ${error.message}` }; } } break; } case 'wordpress_create_page': { const { title, content, ...otherFields } = args || {}; if (!title || !content) { result = { success: false, error: 'Title and content are required for wordpress_create_page' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const page = await wordpressAPI.createPage({ title, content, ...otherFields }); result = { success: true, page: page, note: 'WordPress page created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create page: ${error.message}` }; } } break; } case 'wordpress_update_page': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Page ID is required for wordpress_update_page' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const page = await wordpressAPI.updatePage(id, updateFields); result = { success: true, page: page, note: 'WordPress page updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update page: ${error.message}` }; } } break; } case 'wordpress_delete_page': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Page ID is required for wordpress_delete_page' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const page = await wordpressAPI.deletePage(id, force); result = { success: true, page: page, note: 'WordPress page deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete page: ${error.message}` }; } } break; } // WordPress Media case 'wordpress_get_media': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.getMedia(args || {}); result = { success: true, media: media, count: media.length, note: 'WordPress media retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get media: ${error.message}` }; } } break; } case 'wordpress_get_media_item': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Media ID is required for wordpress_get_media_item' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.getMediaItem(id); result = { success: true, media: media, note: 'WordPress media item retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get media item: ${error.message}` }; } } break; } case 'wordpress_upload_media': { const { file_url, filename, ...otherFields } = args || {}; if (!file_url || !filename) { result = { success: false, error: 'File URL and filename are required for wordpress_upload_media' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.uploadMedia(file_url, filename, otherFields.title, otherFields.alt_text, otherFields.caption, otherFields.description); result = { success: true, media: media, note: 'WordPress media uploaded successfully' }; } catch (error: any) { result = { success: false, error: `Failed to upload media: ${error.message}` }; } } break; } case 'wordpress_upload_media_binary': { const { file_data, filename, mime_type, ...otherFields } = args || {}; if (!file_data || !filename || !mime_type) { result = { success: false, error: 'File data, filename, and mime_type are required for wordpress_upload_media_binary' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { // Convert base64 to Buffer const fileBuffer = Buffer.from(file_data, 'base64'); const media = await wordpressAPI.uploadMediaBinary(fileBuffer, filename, mime_type, otherFields.title, otherFields.alt_text, otherFields.caption, otherFields.description); result = { success: true, media: media, note: 'WordPress media uploaded successfully via binary upload' }; } catch (error: any) { result = { success: false, error: `Failed to upload media binary: ${error.message}` }; } } break; } case 'wordpress_update_media_metadata': { const { id, ...metadata } = args || {}; if (!id) { result = { success: false, error: 'Media ID is required for wordpress_update_media_metadata' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.updateMediaMetadata(id, metadata); result = { success: true, media: media, note: 'WordPress media metadata updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update media metadata: ${error.message}` }; } } break; } case 'wordpress_upload_media_direct': { const { file_data, filename, mime_type, ...otherFields } = args || {}; if (!file_data || !filename || !mime_type) { result = { success: false, error: 'File data, filename, and mime_type are required for wordpress_upload_media_direct' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { // Convert base64 to Buffer const fileBuffer = Buffer.from(file_data, 'base64'); const media = await wordpressAPI.uploadMediaDirect(fileBuffer, filename, mime_type, otherFields.title, otherFields.alt_text, otherFields.caption, otherFields.description); result = { success: true, media: media, note: 'WordPress media uploaded successfully via direct binary upload' }; } catch (error: any) { result = { success: false, error: `Failed to upload media direct: ${error.message}` }; } } break; } case 'wordpress_update_media': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Media ID is required for wordpress_update_media' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.updateMedia(id, updateFields); result = { success: true, media: media, note: 'WordPress media updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update media: ${error.message}` }; } } break; } case 'wordpress_delete_media': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Media ID is required for wordpress_delete_media' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const media = await wordpressAPI.deleteMedia(id, force); result = { success: true, media: media, note: 'WordPress media deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete media: ${error.message}` }; } } break; } // WordPress Categories case 'wordpress_get_categories': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const categories = await wordpressAPI.getCategories(args || {}); result = { success: true, categories: categories, count: categories.length, note: 'WordPress categories retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get categories: ${error.message}` }; } } break; } case 'wordpress_get_category': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Category ID is required for wordpress_get_category' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const category = await wordpressAPI.getCategory(id); result = { success: true, category: category, note: 'WordPress category retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get category: ${error.message}` }; } } break; } case 'wordpress_create_category': { const { name, ...otherFields } = args || {}; if (!name) { result = { success: false, error: 'Name is required for wordpress_create_category' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const category = await wordpressAPI.createCategory({ name, ...otherFields }); result = { success: true, category: category, note: 'WordPress category created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create category: ${error.message}` }; } } break; } case 'wordpress_update_category': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Category ID is required for wordpress_update_category' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const category = await wordpressAPI.updateCategory(id, updateFields); result = { success: true, category: category, note: 'WordPress category updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update category: ${error.message}` }; } } break; } case 'wordpress_delete_category': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Category ID is required for wordpress_delete_category' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const category = await wordpressAPI.deleteCategory(id, force); result = { success: true, category: category, note: 'WordPress category deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete category: ${error.message}` }; } } break; } // WordPress Tags case 'wordpress_get_tags': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const tags = await wordpressAPI.getTags(args || {}); result = { success: true, tags: tags, count: tags.length, note: 'WordPress tags retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get tags: ${error.message}` }; } } break; } case 'wordpress_get_tag': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Tag ID is required for wordpress_get_tag' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const tag = await wordpressAPI.getTag(id); result = { success: true, tag: tag, note: 'WordPress tag retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get tag: ${error.message}` }; } } break; } case 'wordpress_create_tag': { const { name, ...otherFields } = args || {}; if (!name) { result = { success: false, error: 'Name is required for wordpress_create_tag' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const tag = await wordpressAPI.createTag({ name, ...otherFields }); result = { success: true, tag: tag, note: 'WordPress tag created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create tag: ${error.message}` }; } } break; } case 'wordpress_update_tag': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Tag ID is required for wordpress_update_tag' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const tag = await wordpressAPI.updateTag(id, updateFields); result = { success: true, tag: tag, note: 'WordPress tag updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update tag: ${error.message}` }; } } break; } case 'wordpress_delete_tag': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Tag ID is required for wordpress_delete_tag' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const tag = await wordpressAPI.deleteTag(id, force); result = { success: true, tag: tag, note: 'WordPress tag deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete tag: ${error.message}` }; } } break; } // WordPress Users case 'wordpress_get_users': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const users = await wordpressAPI.getUsers(args || {}); result = { success: true, users: users, count: users.length, note: 'WordPress users retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get users: ${error.message}` }; } } break; } case 'wordpress_get_user': { const { id } = args || {}; if (!id) { result = { success: false, error: 'User ID is required for wordpress_get_user' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const user = await wordpressAPI.getUser(id); result = { success: true, user: user, note: 'WordPress user retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get user: ${error.message}` }; } } break; } case 'wordpress_create_user': { const { username, email, password, ...otherFields } = args || {}; if (!username || !email || !password) { result = { success: false, error: 'Username, email, and password are required for wordpress_create_user' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const user = await wordpressAPI.createUser({ username, email, password, ...otherFields }); result = { success: true, user: user, note: 'WordPress user created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create user: ${error.message}` }; } } break; } case 'wordpress_update_user': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'User ID is required for wordpress_update_user' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const user = await wordpressAPI.updateUser(id, updateFields); result = { success: true, user: user, note: 'WordPress user updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update user: ${error.message}` }; } } break; } case 'wordpress_delete_user': { const { id, force = false, reassign } = args || {}; if (!id) { result = { success: false, error: 'User ID is required for wordpress_delete_user' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const user = await wordpressAPI.deleteUser(id, force, reassign); result = { success: true, user: user, note: 'WordPress user deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete user: ${error.message}` }; } } break; } // WordPress Comments case 'wordpress_get_comments': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const comments = await wordpressAPI.getComments(args || {}); result = { success: true, comments: comments, count: comments.length, note: 'WordPress comments retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get comments: ${error.message}` }; } } break; } case 'wordpress_get_comment': { const { id } = args || {}; if (!id) { result = { success: false, error: 'Comment ID is required for wordpress_get_comment' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const comment = await wordpressAPI.getComment(id); result = { success: true, comment: comment, note: 'WordPress comment retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get comment: ${error.message}` }; } } break; } case 'wordpress_create_comment': { const { post, content, ...otherFields } = args || {}; if (!post || !content) { result = { success: false, error: 'Post ID and content are required for wordpress_create_comment' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const comment = await wordpressAPI.createComment({ post, content, ...otherFields }); result = { success: true, comment: comment, note: 'WordPress comment created successfully' }; } catch (error: any) { result = { success: false, error: `Failed to create comment: ${error.message}` }; } } break; } case 'wordpress_update_comment': { const { id, ...updateFields } = args || {}; if (!id) { result = { success: false, error: 'Comment ID is required for wordpress_update_comment' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const comment = await wordpressAPI.updateComment(id, updateFields); result = { success: true, comment: comment, note: 'WordPress comment updated successfully' }; } catch (error: any) { result = { success: false, error: `Failed to update comment: ${error.message}` }; } } break; } case 'wordpress_delete_comment': { const { id, force = false } = args || {}; if (!id) { result = { success: false, error: 'Comment ID is required for wordpress_delete_comment' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const comment = await wordpressAPI.deleteComment(id, force); result = { success: true, comment: comment, note: 'WordPress comment deleted successfully' }; } catch (error: any) { result = { success: false, error: `Failed to delete comment: ${error.message}` }; } } break; } // WordPress Site Info case 'wordpress_get_site_info': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const siteInfo = await wordpressAPI.getSiteInfo(); result = { success: true, site_info: siteInfo, note: 'WordPress site information retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get site info: ${error.message}` }; } } break; } case 'wordpress_get_settings': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const settings = await wordpressAPI.getSettings(); result = { success: true, settings: settings, note: 'WordPress settings retrieved successfully' }; } catch (error: any) { result = { success: false, error: `Failed to get settings: ${error.message}` }; } } break; } case 'wordpress_search': { const { query, type } = args || {}; if (!query) { result = { success: false, error: 'Query is required for wordpress_search' }; } else if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const searchResults = await wordpressAPI.search(query, type); result = { success: true, search_results: searchResults, count: searchResults.length, query: query, type: type || 'any', note: 'WordPress search completed successfully' }; } catch (error: any) { result = { success: false, error: `Failed to search: ${error.message}` }; } } break; } case 'wordpress_test_connection': { if (!wordpressAPI) { result = { success: false, error: 'WordPress API not initialized. Please check WordPress configuration.' }; } else { try { const isConnected = await wordpressAPI.testConnection(); result = { success: isConnected, connected: isConnected, wordpress_url: WORDPRESS_URL, note: isConnected ? 'WordPress connection successful' : 'WordPress connection failed' }; } catch (error: any) { result = { success: false, error: `Failed to test connection: ${error.message}` }; } } break; } default: result = { success: false, error: `Unknown tool: ${name}` }; } res.json({ jsonrpc: '2.0', id, result: { content: [ { type: 'text', text: JSON.stringify(result) } ] } }); break; case 'notifications/initialized': // MCP client initialization notification res.json({ jsonrpc: '2.0', id, result: {} }); break; default: res.json({ jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } }); } } else { // Invalid MCP request format console.log('❌ Invalid MCP request format:', req.body); res.status(400).json({ jsonrpc: '2.0', id: id || null, error: { code: -32600, message: 'Invalid Request: not a valid MCP request' } }); } }); // Health check endpoint app.get('/health', async (req, res) => { let bot_connected = false; let bot_username = null; if (bot) { try { const botInfo = await bot.getMe(); bot_connected = true; bot_username = botInfo.username; } catch (error) { console.error('Bot health check failed:', error); } } res.json({ status: 'healthy', timestamp: new Date().toISOString(), channel: CHANNEL_ID, bot_token_set: !!TELEGRAM_BOT_TOKEN, bot_connected: bot_connected, bot_username: bot_username, version: '2.3.0', mcp_server: true, pexels_enabled: !!PEXELS_API_KEY, yandex_oauth: !!YANDEX_OAUTH_TOKEN, wordpress_enabled: !!wordpressAPI, wordpress_url: WORDPRESS_URL || 'Not configured', environment: process.env.NODE_ENV || 'development' }); }); // Yandex OAuth endpoints app.get('/yandex/auth', (req, res) => { const redirectUri = `${req.protocol}://${req.get('host')}/yandex/callback`; const authUrl = `https://oauth.yandex.ru/authorize?response_type=code&client_id=${YANDEX_CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=direct:api`; res.json({ message: 'Yandex OAuth Authorization with Direct API scope', instructions: 'Open this URL in browser to authorize', auth_url: authUrl, callback_url: redirectUri, scope: 'direct:api', note: 'After authorization, you will be redirected to callback URL with code' }); }); app.get('/yandex/callback', async (req, res) => { const code = req.query.code as string; if (!code) { return res.status(400).json({ error: 'Authorization code is required', message: 'No code provided in callback' }); } try { const tokenData = await getYandexOAuthToken(code); res.json({ success: true, message: 'Yandex OAuth token obtained successfully!', token_type: tokenData.token_type, expires_in: tokenData.expires_in, note: 'Token has been saved. You can now use Yandex Wordstat tools!' }); } catch (error: any) { res.status(500).json({ success: false, error: error.message, note: 'Failed to exchange code for token' }); } }); app.post('/yandex/set-token', (req, res) => { const { token } = req.body; if (!token) { return res.status(400).json({ error: 'Token is required', usage: 'POST /yandex/set-token with body: { "token": "your_token" }' }); } YANDEX_OAUTH_TOKEN = token; res.json({ success: true, message: 'Yandex OAuth token set successfully!', note: 'You can now use Yandex Wordstat tools' }); }); app.get('/yandex/status', (req, res) => { const redirectUri = `${req.protocol}://${req.get('host')}/yandex/callback`; res.json({ client_id: YANDEX_CLIENT_ID, login: YANDEX_LOGIN, token_set: !!YANDEX_OAUTH_TOKEN, token_preview: YANDEX_OAUTH_TOKEN ? `${YANDEX_OAUTH_TOKEN.substring(0, 20)}...` : 'Not set', auth_url: `https://oauth.yandex.ru/authorize?response_type=code&client_id=${YANDEX_CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=direct:api`, callback_url: redirectUri, scope: 'direct:api' }); }); app.get('/yandex/get-token', (req, res) => { if (!YANDEX_OAUTH_TOKEN) { return res.json({ error: 'Token not set', message: 'Please authorize first' }); } res.json({ token: YANDEX_OAUTH_TOKEN, note: 'Save this as YANDEX_OAUTH_TOKEN environment variable in Railway' }); }); // MCP Server Info endpoint app.get('/mcp', (req, res) => { res.json({ name: 'telegram-mcp-server', version: '2.1.0', capabilities: { tools: {} } }); }); // List tools endpoint app.get('/mcp/tools/list', (req, res) => { res.json({ tools: [ { name: 'send_message', description: 'Send a text message to the Telegram channel', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Message text to send' }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message' } }, required: ['text'] } }, { name: 'send_photo', description: 'Send a photo to the Telegram channel', inputSchema: { type: 'object', properties: { photo: { type: 'string', description: 'Photo URL or file path' }, caption: { type: 'string', description: 'Photo caption' }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption' } }, required: ['photo'] } }, { name: 'send_photo_binary', description: 'Send a photo to the Telegram channel using binary data', inputSchema: { type: 'object', properties: { file_data: { type: 'string', description: 'Base64 encoded image data' }, filename: { type: 'string', description: 'Filename for the image' }, caption: { type: 'string', description: 'Photo caption' }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the caption' } }, required: ['file_data', 'filename'] } }, { name: 'send_video', description: 'Send a video to the Telegram channel', inputSchema: { type: 'object', properties: { video: { type: 'string', description: 'Video URL or file path' }, caption: { type: 'string', description: 'Video caption' }, duration: { type: 'number', description: 'Video duration in seconds' }, width: { type: 'number', description: 'Video width' }, height: { type: 'number', description: 'Video height' } }, required: ['video'] } }, { name: 'send_document', description: 'Send a document to the Telegram channel', inputSchema: { type: 'object', properties: { document: { type: 'string', description: 'Document URL or file path' }, caption: { type: 'string', description: 'Document caption' }, filename: { type: 'string', description: 'Custom filename for the document' } }, required: ['document'] } }, { name: 'send_poll', description: 'Send a poll to the Telegram channel', inputSchema: { type: 'object', properties: { question: { type: 'string', description: 'Poll question' }, options: { type: 'array', items: { type: 'string' }, description: 'Poll options (2-10 options)' }, is_anonymous: { type: 'boolean', description: 'Whether the poll is anonymous' }, type: { type: 'string', enum: ['quiz', 'regular'], description: 'Poll type' }, correct_option_id: { type: 'number', description: 'Correct option ID for quiz polls' }, explanation: { type: 'string', description: 'Explanation for quiz polls' } }, required: ['question', 'options'] } }, { name: 'send_reaction', description: 'Send a reaction to a message', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to react to' }, emoji: { type: 'string', description: 'Emoji to react with' } }, required: ['message_id', 'emoji'] } }, { name: 'edit_message', description: 'Edit a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to edit' }, text: { type: 'string', description: 'New message text' }, parse_mode: { type: 'string', enum: ['HTML', 'Markdown'], description: 'Parse mode for the message' } }, required: ['message_id', 'text'] } }, { name: 'delete_message', description: 'Delete a message from the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to delete' } }, required: ['message_id'] } }, { name: 'pin_message', description: 'Pin a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to pin' }, disable_notification: { type: 'boolean', description: 'Whether to disable notification for pinned message' } }, required: ['message_id'] } }, { name: 'unpin_message', description: 'Unpin a message in the Telegram channel', inputSchema: { type: 'object', properties: { message_id: { type: 'number', description: 'Message ID to unpin (optional, unpins all if not specified)' } } } }, { name: 'get_channel_info', description: 'Get information about the Telegram channel', inputSchema: { type: 'object', properties: {} } }, { name: 'get_channel_stats', description: 'Get channel statistics and member count', inputSchema: { type: 'object', properties: {} } }, // WordPress tools { name: 'wordpress_upload_media', description: 'Upload a media file to WordPress', inputSchema: { type: 'object', properties: { file_url: { type: 'string', description: 'URL of the file to upload' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_url', 'filename'] } }, { name: 'wordpress_upload_media_binary', description: 'Upload a media file directly as binary data to WordPress (more efficient for large files)', inputSchema: { type: 'object', properties: { file_data: { type: 'string', description: 'Base64 encoded file data' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, mime_type: { type: 'string', description: 'MIME type of the file (e.g., image/png, image/jpeg)' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_data', 'filename', 'mime_type'] } }, { name: 'wordpress_upload_media_direct', description: 'Upload a media file directly as binary data to WordPress without JSON (for large files)', inputSchema: { type: 'object', properties: { file_data: { type: 'string', description: 'Base64 encoded file data' }, filename: { type: 'string', description: 'Filename for the uploaded file' }, mime_type: { type: 'string', description: 'MIME type of the file (e.g., image/png, image/jpeg)' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['file_data', 'filename', 'mime_type'] } }, { name: 'wordpress_update_media_metadata', description: 'Update media metadata (alt_text, caption, description)', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['id'] } }, { name: 'wordpress_update_media', description: 'Update a WordPress media item', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, title: { type: 'string', description: 'Media title' }, alt_text: { type: 'string', description: 'Alt text for images' }, caption: { type: 'string', description: 'Media caption' }, description: { type: 'string', description: 'Media description' } }, required: ['id'] } }, { name: 'wordpress_get_media', description: 'Get a WordPress media item by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' } }, required: ['id'] } }, { name: 'wordpress_list_media', description: 'List WordPress media items', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, media_type: { type: 'string', description: 'Media type (image, video, audio, application)' }, mime_type: { type: 'string', description: 'MIME type filter' } } } }, { name: 'wordpress_delete_media', description: 'Delete a WordPress media item', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Media ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, { name: 'wordpress_create_post', description: 'Create a new WordPress post', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Post title' }, content: { type: 'string', description: 'Post content (HTML)' }, excerpt: { type: 'string', description: 'Post excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Post status' }, featured_media: { type: 'number', description: 'Featured media ID' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' }, meta: { type: 'object', description: 'Custom meta fields' } }, required: ['title', 'content'] } }, { name: 'wordpress_update_post', description: 'Update a WordPress post', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' }, title: { type: 'string', description: 'Post title' }, content: { type: 'string', description: 'Post content (HTML)' }, excerpt: { type: 'string', description: 'Post excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Post status' }, featured_media: { type: 'number', description: 'Featured media ID' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' }, meta: { type: 'object', description: 'Custom meta fields' } }, required: ['id'] } }, { name: 'wordpress_get_post', description: 'Get a WordPress post by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' } }, required: ['id'] } }, { name: 'wordpress_list_posts', description: 'List WordPress posts', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, status: { type: 'string', description: 'Post status' }, categories: { type: 'array', items: { type: 'number' }, description: 'Category IDs' }, tags: { type: 'array', items: { type: 'number' }, description: 'Tag IDs' } } } }, { name: 'wordpress_delete_post', description: 'Delete a WordPress post', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Post ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, { name: 'wordpress_create_page', description: 'Create a new WordPress page', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Page title' }, content: { type: 'string', description: 'Page content (HTML)' }, excerpt: { type: 'string', description: 'Page excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Page status' }, featured_media: { type: 'number', description: 'Featured media ID' }, parent: { type: 'number', description: 'Parent page ID' }, menu_order: { type: 'number', description: 'Menu order' }, template: { type: 'string', description: 'Page template' }, meta: { type: 'object', description: 'Custom meta fields' } }, required: ['title', 'content'] } }, { name: 'wordpress_update_page', description: 'Update a WordPress page', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' }, title: { type: 'string', description: 'Page title' }, content: { type: 'string', description: 'Page content (HTML)' }, excerpt: { type: 'string', description: 'Page excerpt' }, status: { type: 'string', enum: ['publish', 'draft', 'private', 'pending'], description: 'Page status' }, featured_media: { type: 'number', description: 'Featured media ID' }, parent: { type: 'number', description: 'Parent page ID' }, menu_order: { type: 'number', description: 'Menu order' }, template: { type: 'string', description: 'Page template' }, meta: { type: 'object', description: 'Custom meta fields' } }, required: ['id'] } }, { name: 'wordpress_get_page', description: 'Get a WordPress page by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' } }, required: ['id'] } }, { name: 'wordpress_list_pages', description: 'List WordPress pages', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, status: { type: 'string', description: 'Page status' }, parent: { type: 'number', description: 'Parent page ID' } } } }, { name: 'wordpress_delete_page', description: 'Delete a WordPress page', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Page ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, { name: 'wordpress_create_category', description: 'Create a new WordPress category', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Category name' }, description: { type: 'string', description: 'Category description' }, parent: { type: 'number', description: 'Parent category ID' }, slug: { type: 'string', description: 'Category slug' } }, required: ['name'] } }, { name: 'wordpress_update_category', description: 'Update a WordPress category', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' }, name: { type: 'string', description: 'Category name' }, description: { type: 'string', description: 'Category description' }, parent: { type: 'number', description: 'Parent category ID' }, slug: { type: 'string', description: 'Category slug' } }, required: ['id'] } }, { name: 'wordpress_get_category', description: 'Get a WordPress category by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' } }, required: ['id'] } }, { name: 'wordpress_list_categories', description: 'List WordPress categories', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, parent: { type: 'number', description: 'Parent category ID' }, hide_empty: { type: 'boolean', description: 'Hide empty categories' } } } }, { name: 'wordpress_delete_category', description: 'Delete a WordPress category', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Category ID' }, force: { type: 'boolean', description: 'Force delete' } }, required: ['id'] } }, { name: 'wordpress_create_tag', description: 'Create a new WordPress tag', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Tag name' }, description: { type: 'string', description: 'Tag description' }, slug: { type: 'string', description: 'Tag slug' } }, required: ['name'] } }, { name: 'wordpress_update_tag', description: 'Update a WordPress tag', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' }, name: { type: 'string', description: 'Tag name' }, description: { type: 'string', description: 'Tag description' }, slug: { type: 'string', description: 'Tag slug' } }, required: ['id'] } }, { name: 'wordpress_get_tag', description: 'Get a WordPress tag by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' } }, required: ['id'] } }, { name: 'wordpress_list_tags', description: 'List WordPress tags', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, hide_empty: { type: 'boolean', description: 'Hide empty tags' } } } }, { name: 'wordpress_delete_tag', description: 'Delete a WordPress tag', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Tag ID' }, force: { type: 'boolean', description: 'Force delete' } }, required: ['id'] } }, { name: 'wordpress_get_user', description: 'Get a WordPress user by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'User ID' } }, required: ['id'] } }, { name: 'wordpress_list_users', description: 'List WordPress users', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, roles: { type: 'array', items: { type: 'string' }, description: 'User roles' } } } }, { name: 'wordpress_create_comment', description: 'Create a new WordPress comment', inputSchema: { type: 'object', properties: { post: { type: 'number', description: 'Post ID' }, author_name: { type: 'string', description: 'Author name' }, author_email: { type: 'string', description: 'Author email' }, content: { type: 'string', description: 'Comment content' }, status: { type: 'string', enum: ['hold', 'approve', 'spam', 'trash'], description: 'Comment status' }, parent: { type: 'number', description: 'Parent comment ID' } }, required: ['post', 'author_name', 'author_email', 'content'] } }, { name: 'wordpress_update_comment', description: 'Update a WordPress comment', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' }, author_name: { type: 'string', description: 'Author name' }, author_email: { type: 'string', description: 'Author email' }, content: { type: 'string', description: 'Comment content' }, status: { type: 'string', enum: ['hold', 'approve', 'spam', 'trash'], description: 'Comment status' } }, required: ['id'] } }, { name: 'wordpress_get_comment', description: 'Get a WordPress comment by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' } }, required: ['id'] } }, { name: 'wordpress_list_comments', description: 'List WordPress comments', inputSchema: { type: 'object', properties: { per_page: { type: 'number', description: 'Number of items per page (default: 10)' }, page: { type: 'number', description: 'Page number (default: 1)' }, search: { type: 'string', description: 'Search term' }, status: { type: 'string', description: 'Comment status' }, post: { type: 'number', description: 'Post ID' }, parent: { type: 'number', description: 'Parent comment ID' } } } }, { name: 'wordpress_delete_comment', description: 'Delete a WordPress comment', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Comment ID' }, force: { type: 'boolean', description: 'Force delete (bypass trash)' } }, required: ['id'] } }, { name: 'wordpress_get_site_info', description: 'Get WordPress site information', inputSchema: { type: 'object', properties: {} } } ] }); }); // Call tool endpoint app.post('/mcp/tools/call', async (req, res) => { try { const { name, arguments: args } = req.body; if (!name) { return res.status(400).json({ error: 'Tool name is required', content: [ { type: 'text', text: JSON.stringify({ success: false, error: 'Tool name is required', }), }, ], isError: true, }); } let result; switch (name) { case 'send_message': { const { text } = args || {}; if (!text) { throw new Error('Text is required for send_message'); } result = { success: true, message: 'Message would be sent to Telegram', text: text, channel: CHANNEL_ID, note: 'This is a test response. Telegram integration requires proper bot setup.' }; break; } case 'get_channel_info': { result = { success: true, channel: CHANNEL_ID, title: 'Test Channel', type: 'channel', description: 'This is a test response. Real channel info requires proper bot setup.', note: 'This is a test response. Real channel info requires proper bot setup.' }; break; } default: throw new Error(`Unknown tool: ${name}`); } res.json({ content: [ { type: 'text', text: JSON.stringify(result), }, ], }); } catch (error: any) { console.error('Tool execution error:', error); res.status(500).json({ content: [ { type: 'text', text: JSON.stringify({ success: false, error: error.message, tool: req.body.name, }), }, ], isError: true, }); } }); // Start the server async function main() { try { const port = process.env.PORT || 8080; app.listen(port, () => { console.log('🚀 Telegram MCP Server v2.3.0 with Pexels, Yandex Wordstat & WordPress running on port', port); console.log(`🌐 API URL: http://localhost:${port}`); console.log(`📖 MCP Info: http://localhost:${port}/mcp`); console.log(`🔧 Tools: http://localhost:${port}/mcp/tools/list`); console.log(`💚 Health: http://localhost:${port}/health`); console.log('✅ Ready for testing!'); console.log(`📱 Target channel: ${CHANNEL_ID}`); console.log(`🔑 Bot token: ${TELEGRAM_BOT_TOKEN ? 'SET' : 'NOT SET'}`); console.log(`🌐 WordPress: ${wordpressAPI ? 'CONNECTED' : 'NOT CONFIGURED'}`); if (wordpressAPI) { console.log(` URL: ${WORDPRESS_URL}`); console.log(` User: ${WORDPRESS_USERNAME}`); } }); } catch (error) { console.error('❌ Server startup error:', error); process.exit(1); } } main().catch((error) => { console.error('❌ Fatal error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bobidk91-ops/telegram-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server