Social Listening MCP Server

import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; interface ToolCall { name: string; arguments: Record<string, any>; } interface DateRange { start_date: string; end_date: string; } // Patterns for date expressions const DATE_PATTERNS = [ { pattern: /last (\d+) (day|days|week|weeks|month|months)/i, handler: (matches: RegExpMatchArray): DateRange => { const amount = parseInt(matches[1]); const unit = matches[2].toLowerCase(); const end = new Date(); const start = new Date(); switch (unit) { case 'day': case 'days': start.setDate(start.getDate() - amount); break; case 'week': case 'weeks': start.setDate(start.getDate() - (amount * 7)); break; case 'month': case 'months': start.setMonth(start.getMonth() - amount); break; } return { start_date: start.toISOString().split('T')[0], end_date: end.toISOString().split('T')[0] }; } }, { pattern: /last (week|month|year)/i, handler: (matches: RegExpMatchArray): DateRange => { const unit = matches[1].toLowerCase(); const end = new Date(); const start = new Date(); switch (unit) { case 'week': start.setDate(start.getDate() - 7); break; case 'month': start.setMonth(start.getMonth() - 1); break; case 'year': start.setFullYear(start.getFullYear() - 1); break; } return { start_date: start.toISOString().split('T')[0], end_date: end.toISOString().split('T')[0] }; } }, { pattern: /month of (\w+ \d{4})/i, handler: (matches: RegExpMatchArray): DateRange => { const date = new Date(matches[1]); if (isNaN(date.getTime())) { throw new Error('Invalid date format'); } const start = new Date(date.getFullYear(), date.getMonth(), 1); const end = new Date(date.getFullYear(), date.getMonth() + 1, 0); return { start_date: start.toISOString().split('T')[0], end_date: end.toISOString().split('T')[0] }; } } ]; // Extract confidence score from text function extractConfidence(text: string): number | undefined { const matches = text.match(/confidence (?:score )?(?:of )?(\d+(?:\.\d+)?)/i); if (matches) { const score = parseFloat(matches[1]); if (score >= 0 && score <= 1) { return score; } } return undefined; } // Extract categories from text function extractCategories(text: string): string[] { const categories = []; const categoryMap = { spam: ['spam', 'junk', 'unwanted'], promotional: ['promotional', 'promo', 'marketing', 'advertisement', 'ads'], support: ['support', 'help', 'assistance'], feedback: ['feedback', 'review', 'opinion'], inquiry: ['inquiry', 'question', 'ask'] }; for (const [category, keywords] of Object.entries(categoryMap)) { if (keywords.some(keyword => text.toLowerCase().includes(keyword))) { categories.push(category); } } return categories; } // Extract date range from text function extractDateRange(text: string): DateRange { for (const { pattern, handler } of DATE_PATTERNS) { const matches = text.match(pattern); if (matches) { return handler(matches); } } // Default to last 30 days if no date range found const end = new Date(); const start = new Date(); start.setDate(start.getDate() - 30); return { start_date: start.toISOString().split('T')[0], end_date: end.toISOString().split('T')[0] }; } // Extract limit from text function extractLimit(text: string): number | undefined { const matches = text.match(/(?:limit|top|first) (\d+)/i); if (matches) { return parseInt(matches[1]); } return undefined; } // Main function to process natural language prompts export function processNaturalLanguage(prompt: string): ToolCall[] { const text = prompt.toLowerCase(); const calls: ToolCall[] = []; // Check for AI filter configuration if (text.includes('configure ai') || text.includes('set up ai') || text.includes('update ai settings')) { const confidence = extractConfidence(text) || 0.7; const categories = extractCategories(text); calls.push({ name: 'configure_ai_filter', arguments: { enabled: true, min_confidence: confidence, categories } }); return calls; } // Check for webhook configuration if (text.includes('webhook') && (text.includes('configure') || text.includes('set up') || text.includes('update'))) { throw new McpError( ErrorCode.InvalidParams, 'Webhook configuration requires explicit endpoint URL and secret token parameters' ); } // Check for webhook status if (text.includes('webhook') && text.includes('status')) { calls.push({ name: 'get_webhook_status', arguments: {} }); return calls; } // Check for backfill request if (text.includes('backfill')) { const matches = text.match(/month of (\w+ \d{4})/i); if (matches) { const date = new Date(matches[1]); if (!isNaN(date.getTime())) { calls.push({ name: 'backfill_month', arguments: { year: date.getFullYear(), month: date.getMonth() + 1 } }); return calls; } } } // Check for sync request if (text.includes('sync') || text.includes('update') || text.includes('get new')) { calls.push({ name: 'sync_latest', arguments: {} }); return calls; } // Check for AI filtered mentions if (text.includes('ai') || text.includes('filter')) { const dateRange = extractDateRange(text); const confidence = extractConfidence(text); const categories = extractCategories(text); const limit = extractLimit(text); const args: Record<string, any> = { ...dateRange }; if (confidence !== undefined) { args.min_confidence = confidence; } if (categories.length > 0) { args.categories = categories; } if (limit !== undefined) { args.limit = limit; } calls.push({ name: 'get_ai_filtered_mentions', arguments: args }); return calls; } // Check for trend analysis if (text.includes('trend') || text.includes('analyze')) { const dateRange = extractDateRange(text); const args: Record<string, any> = { ...dateRange }; // Determine grouping if (text.includes('week')) { args.group_by = 'week'; } else if (text.includes('month')) { args.group_by = 'month'; } else { args.group_by = 'day'; } calls.push({ name: 'analyze_trends', arguments: args }); return calls; } // Check for top sources if (text.includes('top') || text.includes('source')) { const dateRange = extractDateRange(text); const limit = extractLimit(text); const args: Record<string, any> = { ...dateRange }; if (limit !== undefined) { args.limit = limit; } calls.push({ name: 'get_top_sources', arguments: args }); return calls; } throw new McpError( ErrorCode.InvalidParams, 'Could not determine the intended action from the prompt. Please try rephrasing your request.' ); }