Skip to main content
Glama

Social Media MCP Server

by tayler-id
index.ts14 kB
import { createComponentLogger } from '../utils/logger.js'; import { UserIntent, SocialPlatform, ContentType } from '../types/index.js'; import axios from 'axios'; import config from '../config/index.js'; import rateLimitManager from '../rate-limit/manager.js'; import conversationManager from '../conversation/manager.js'; const logger = createComponentLogger('NLPProcessor'); /** * Natural language processor for parsing user intent from natural language instructions */ class NLPProcessor { /** * Parse user intent from natural language instruction */ async parseIntent(instruction: string, conversationId?: string): Promise<UserIntent> { logger.info('Parsing intent', { instruction, conversationId }); try { // If we have a conversation ID, get the existing intent if (conversationId) { const conversation = conversationManager.getConversation(conversationId); if (conversation) { logger.info('Using existing conversation', { conversationId }); return conversation.intent; } } // For now, use a simple rule-based approach // In a complete implementation, this would use an AI model // Check if we have OpenAI API key if (config.ai.openai.apiKey) { return await this.parseIntentWithOpenAI(instruction); } // Check if we have Anthropic API key if (config.ai.anthropic.apiKey) { return await this.parseIntentWithAnthropic(instruction); } // Fallback to rule-based parsing return this.parseIntentWithRules(instruction); } catch (error) { logger.error('Error parsing intent', { instruction, error: error instanceof Error ? error.message : String(error) }); // Fallback to rule-based parsing return this.parseIntentWithRules(instruction); } } /** * Parse intent with OpenAI */ private async parseIntentWithOpenAI(instruction: string): Promise<UserIntent> { logger.info('Parsing intent with OpenAI', { instruction }); try { // Use rate limit manager to handle API rate limits const result = await rateLimitManager.executeRequest({ api: 'openai', endpoint: 'completion', method: 'POST', priority: 'high', retryCount: 0, maxRetries: config.rateLimit.maxRetries, execute: async () => { // Make request to OpenAI API const response = await axios.post( 'https://api.openai.com/v1/chat/completions', { model: config.ai.openai.model, messages: [ { role: 'system', content: ` You are a natural language processing system that extracts structured information from user instructions. Extract the following information from the user's instruction: 1. Topic - What the post is about 2. Tone (optional) - The tone of the post (e.g., professional, casual, humorous) 3. Content Type (optional) - The type of content (announcement, news, promotion, engagement, educational, entertainment, thread) 4. Platforms - Which platforms to post to (twitter, mastodon, or all) 5. Media Requirements (optional) - Whether to include images or videos 6. Research Requirements (optional) - What research to include (hashtags, facts, trends, news) 7. Scheduling Requirements (optional) - When to post Respond with a JSON object containing these fields. `, }, { role: 'user', content: instruction, }, ], temperature: 0.1, }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.ai.openai.apiKey}`, }, } ); return response.data; } }); // Extract content from response const jsonString = result.choices[0].message.content.trim(); // Parse JSON const parsedIntent = JSON.parse(jsonString); // Convert to UserIntent const userIntent: UserIntent = { rawInput: instruction, topic: parsedIntent.topic || instruction, tone: parsedIntent.tone, contentType: this.parseContentType(parsedIntent.contentType), platforms: this.parsePlatforms(parsedIntent.platforms), mediaRequirements: parsedIntent.mediaRequirements, researchRequirements: parsedIntent.researchRequirements, schedulingRequirements: parsedIntent.schedulingRequirements, }; logger.info('Intent parsed successfully with OpenAI', { topic: userIntent.topic, platforms: userIntent.platforms, }); return userIntent; } catch (error) { logger.error('Error parsing intent with OpenAI', { instruction, error: error instanceof Error ? error.message : String(error) }); // Fallback to rule-based parsing return this.parseIntentWithRules(instruction); } } /** * Parse intent with Anthropic */ private async parseIntentWithAnthropic(instruction: string): Promise<UserIntent> { logger.info('Parsing intent with Anthropic', { instruction }); try { // Use rate limit manager to handle API rate limits const result = await rateLimitManager.executeRequest({ api: 'anthropic', endpoint: 'completion', method: 'POST', priority: 'high', retryCount: 0, maxRetries: config.rateLimit.maxRetries, execute: async () => { // Make request to Anthropic API const response = await axios.post( 'https://api.anthropic.com/v1/messages', { model: config.ai.anthropic.model, system: ` You are a natural language processing system that extracts structured information from user instructions. Extract the following information from the user's instruction: 1. Topic - What the post is about 2. Tone (optional) - The tone of the post (e.g., professional, casual, humorous) 3. Content Type (optional) - The type of content (announcement, news, promotion, engagement, educational, entertainment, thread) 4. Platforms - Which platforms to post to (twitter, mastodon, or all) 5. Media Requirements (optional) - Whether to include images or videos 6. Research Requirements (optional) - What research to include (hashtags, facts, trends, news) 7. Scheduling Requirements (optional) - When to post Respond with a JSON object containing these fields. `, messages: [ { role: 'user', content: instruction, }, ], temperature: 0.1, max_tokens: 1000, }, { headers: { 'Content-Type': 'application/json', 'x-api-key': config.ai.anthropic.apiKey, 'anthropic-version': '2023-06-01', }, } ); return response.data; } }); // Extract content from response const jsonString = result.content[0].text.trim(); // Parse JSON const parsedIntent = JSON.parse(jsonString); // Convert to UserIntent const userIntent: UserIntent = { rawInput: instruction, topic: parsedIntent.topic || instruction, tone: parsedIntent.tone, contentType: this.parseContentType(parsedIntent.contentType), platforms: this.parsePlatforms(parsedIntent.platforms), mediaRequirements: parsedIntent.mediaRequirements, researchRequirements: parsedIntent.researchRequirements, schedulingRequirements: parsedIntent.schedulingRequirements, }; logger.info('Intent parsed successfully with Anthropic', { topic: userIntent.topic, platforms: userIntent.platforms, }); return userIntent; } catch (error) { logger.error('Error parsing intent with Anthropic', { instruction, error: error instanceof Error ? error.message : String(error) }); // Fallback to rule-based parsing return this.parseIntentWithRules(instruction); } } /** * Parse intent with rule-based approach */ private parseIntentWithRules(instruction: string): UserIntent { logger.info('Parsing intent with rules', { instruction }); // Default intent const intent: UserIntent = { rawInput: instruction, topic: instruction, platforms: [SocialPlatform.TWITTER, SocialPlatform.MASTODON], }; // Extract platforms if (instruction.toLowerCase().includes('twitter')) { intent.platforms = [SocialPlatform.TWITTER]; } else if (instruction.toLowerCase().includes('mastodon')) { intent.platforms = [SocialPlatform.MASTODON]; } // Extract content type if (instruction.toLowerCase().includes('announce')) { intent.contentType = ContentType.ANNOUNCEMENT; } else if (instruction.toLowerCase().includes('news')) { intent.contentType = ContentType.NEWS; } else if (instruction.toLowerCase().includes('promot')) { intent.contentType = ContentType.PROMOTION; } else if (instruction.toLowerCase().includes('engage')) { intent.contentType = ContentType.ENGAGEMENT; } else if (instruction.toLowerCase().includes('educat')) { intent.contentType = ContentType.EDUCATIONAL; } else if (instruction.toLowerCase().includes('entertain')) { intent.contentType = ContentType.ENTERTAINMENT; } else if (instruction.toLowerCase().includes('thread')) { intent.contentType = ContentType.THREAD; } // Extract tone if (instruction.toLowerCase().includes('professional')) { intent.tone = 'professional'; } else if (instruction.toLowerCase().includes('casual')) { intent.tone = 'casual'; } else if (instruction.toLowerCase().includes('humor')) { intent.tone = 'humorous'; } else if (instruction.toLowerCase().includes('formal')) { intent.tone = 'formal'; } else if (instruction.toLowerCase().includes('friendly')) { intent.tone = 'friendly'; } // Extract research requirements intent.researchRequirements = { includeHashtags: instruction.toLowerCase().includes('hashtag'), includeFacts: instruction.toLowerCase().includes('fact'), includeTrends: instruction.toLowerCase().includes('trend'), includeNews: instruction.toLowerCase().includes('news'), }; // Extract media requirements intent.mediaRequirements = { includeImage: instruction.toLowerCase().includes('image') || instruction.toLowerCase().includes('picture'), includeVideo: instruction.toLowerCase().includes('video'), }; // Extract scheduling requirements intent.schedulingRequirements = { postImmediately: !instruction.toLowerCase().includes('schedule'), useOptimalTime: instruction.toLowerCase().includes('optimal'), }; logger.info('Intent parsed successfully with rules', { topic: intent.topic, platforms: intent.platforms, }); return intent; } /** * Parse content type */ private parseContentType(contentType?: string): ContentType | undefined { if (!contentType) return undefined; const contentTypeLower = contentType.toLowerCase(); if (contentTypeLower.includes('announce')) { return ContentType.ANNOUNCEMENT; } else if (contentTypeLower.includes('news')) { return ContentType.NEWS; } else if (contentTypeLower.includes('promot')) { return ContentType.PROMOTION; } else if (contentTypeLower.includes('engage')) { return ContentType.ENGAGEMENT; } else if (contentTypeLower.includes('educat')) { return ContentType.EDUCATIONAL; } else if (contentTypeLower.includes('entertain')) { return ContentType.ENTERTAINMENT; } else if (contentTypeLower.includes('thread')) { return ContentType.THREAD; } return undefined; } /** * Parse platforms */ private parsePlatforms(platforms?: string | string[]): SocialPlatform[] { if (!platforms) { return [SocialPlatform.TWITTER, SocialPlatform.MASTODON]; } if (typeof platforms === 'string') { const platformsLower = platforms.toLowerCase(); if (platformsLower === 'all') { return [SocialPlatform.TWITTER, SocialPlatform.MASTODON]; } else if (platformsLower.includes('twitter')) { return [SocialPlatform.TWITTER]; } else if (platformsLower.includes('mastodon')) { return [SocialPlatform.MASTODON]; } return [SocialPlatform.TWITTER, SocialPlatform.MASTODON]; } const result: SocialPlatform[] = []; for (const platform of platforms) { const platformLower = platform.toLowerCase(); if (platformLower === 'all') { return [SocialPlatform.TWITTER, SocialPlatform.MASTODON]; } else if (platformLower.includes('twitter')) { result.push(SocialPlatform.TWITTER); } else if (platformLower.includes('mastodon')) { result.push(SocialPlatform.MASTODON); } } return result.length > 0 ? result : [SocialPlatform.TWITTER, SocialPlatform.MASTODON]; } } // Export singleton instance export default new NLPProcessor();

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/tayler-id/social-media-mcp'

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