Skip to main content
Glama
guardian-top-stories-by-date.ts8.59 kB
import { GuardianClient } from '../api/guardian-client.js'; import { TopStoriesByDateParamsSchema } from '../types/guardian.js'; import { validateDate } from '../utils/formatters.js'; interface StoryScore { article: any; score: number; reasons: string[]; } export async function guardianTopStoriesByDate(client: GuardianClient, args: any): Promise<string> { const params = TopStoriesByDateParamsSchema.parse(args); const date = validateDate(params.date); if (!date) { throw new Error(`Invalid date format: ${params.date}. Use YYYY-MM-DD format.`); } const storyCount = params.story_count || 10; // Get all articles from the specified date const searchParams: Record<string, any> = { 'from-date': date, 'to-date': date, 'page-size': 200, // Get as many as possible for intelligent ranking 'show-fields': 'headline,standfirst,byline,wordcount,firstPublicationDate', 'show-tags': 'keyword,type', 'order-by': 'newest' }; if (params.section) { searchParams.section = params.section; } const response = await client.search(searchParams); const articles = response.response.results; if (articles.length === 0) { return `No articles found for ${date}${params.section ? ` in section "${params.section}"` : ''}.`; } // Score and rank articles using intelligent prioritization const scoredStories = articles.map(article => scoreStory(article, date)); const topStories = scoredStories .sort((a, b) => b.score - a.score) .slice(0, storyCount); // Format results let result = `Top ${storyCount} Stories for ${formatDateForDisplay(date)}:\n`; if (params.section) { result += `Section: ${params.section}\n`; } result += `\n**Intelligent Story Ranking** (based on editorial importance, complexity, and newsworthiness)\n\n`; topStories.forEach((story, index) => { const article = story.article; const rank = index + 1; const trophy = rank === 1 ? '🏆 ' : rank === 2 ? '🥈 ' : rank === 3 ? '🥉 ' : `${rank}. `; result += `${trophy}**${article.webTitle || 'Untitled'}**\n`; result += `Score: ${story.score.toFixed(1)} (${story.reasons.join(', ')})\n`; if (article.fields) { const { fields } = article; if (fields.byline) { result += `By: ${fields.byline}\n`; } if (fields.standfirst) { result += `Summary: ${fields.standfirst}\n`; } if (fields.wordcount) { result += `Length: ${fields.wordcount} words\n`; } } result += `Section: ${article.sectionName || 'Unknown'}\n`; result += `URL: ${article.webUrl || 'N/A'}\n`; result += `Guardian ID: ${article.id || 'N/A'}\n\n`; }); // Add analysis of the day's news landscape result += `**Day's News Analysis**:\n`; result += `• Total articles: ${articles.length}\n`; const sectionBreakdown = getSectionBreakdown(articles); result += `• Top sections: ${sectionBreakdown.slice(0, 3).map(s => `${s.section} (${s.count})`).join(', ')}\n`; const avgScore = topStories.reduce((sum, story) => sum + story.score, 0) / topStories.length; result += `• Average story importance: ${avgScore.toFixed(1)}/100\n`; const complexStories = topStories.filter(s => s.reasons.includes('high complexity')).length; if (complexStories > 0) { result += `• Major breaking news events: ${complexStories}\n`; } return result; } function scoreStory(article: any, date: string): StoryScore { let score = 0; const reasons: string[] = []; // Base score from section importance const sectionScore = getSectionImportance(article.sectionName); score += sectionScore.score; if (sectionScore.reason) reasons.push(sectionScore.reason); // Word count indicates story complexity and importance const wordCount = article.fields?.wordcount ? parseInt(article.fields.wordcount) : 0; if (wordCount > 1500) { score += 20; reasons.push('high complexity'); } else if (wordCount > 800) { score += 10; reasons.push('detailed coverage'); } else if (wordCount < 300) { score -= 5; // Brief items are less likely to be top stories } // Breaking news patterns (published early in the day often indicates urgency) if (article.fields?.firstPublicationDate) { const pubTime = new Date(article.fields.firstPublicationDate); const hour = pubTime.getHours(); if (hour >= 6 && hour <= 10) { score += 15; reasons.push('morning breaking news'); } else if (hour >= 11 && hour <= 14) { score += 10; reasons.push('midday update'); } } // Headline analysis for importance indicators const headline = article.webTitle?.toLowerCase() || ''; const importanceKeywords = [ { words: ['breaking', 'urgent', 'exclusive'], score: 25, label: 'breaking news' }, { words: ['crisis', 'scandal', 'investigation'], score: 20, label: 'major story' }, { words: ['election', 'vote', 'poll'], score: 18, label: 'political significance' }, { words: ['dies', 'dead', 'death', 'killed'], score: 18, label: 'major news' }, { words: ['wins', 'victory', 'defeat'], score: 15, label: 'significant outcome' }, { words: ['announces', 'reveals', 'confirms'], score: 12, label: 'official news' }, { words: ['budget', 'economy', 'recession'], score: 15, label: 'economic importance' }, { words: ['war', 'attack', 'conflict'], score: 22, label: 'international crisis' }, { words: ['climate', 'environment', 'warming'], score: 12, label: 'environmental story' } ]; for (const keyword of importanceKeywords) { if (keyword.words.some(word => headline.includes(word))) { score += keyword.score; reasons.push(keyword.label); break; // Only count one category per headline } } // Tag analysis for topic importance if (article.tags) { const importantTags = article.tags.filter((tag: any) => ['politics', 'world', 'brexit', 'trump', 'climate', 'coronavirus', 'economy'].some(important => tag.id.toLowerCase().includes(important) ) ); if (importantTags.length > 0) { score += importantTags.length * 8; reasons.push('major topic'); } } // Byline analysis - senior correspondents often handle bigger stories const byline = article.fields?.byline?.toLowerCase() || ''; const seniorIndicators = ['editor', 'correspondent', 'chief', 'political', 'foreign', 'diplomatic']; if (seniorIndicators.some(indicator => byline.includes(indicator))) { score += 10; reasons.push('senior correspondent'); } // Ensure score is within reasonable bounds score = Math.max(0, Math.min(100, score)); return { article, score, reasons }; } function getSectionImportance(sectionName: string): { score: number; reason?: string } { const section = sectionName?.toLowerCase() || ''; // Priority sections based on editorial importance const sectionScores: Record<string, { score: number; reason: string }> = { 'politics': { score: 30, reason: 'politics priority' }, 'world': { score: 28, reason: 'world news priority' }, 'uk news': { score: 25, reason: 'national news' }, 'us news': { score: 25, reason: 'us news priority' }, 'business': { score: 22, reason: 'business importance' }, 'environment': { score: 20, reason: 'environmental priority' }, 'society': { score: 18, reason: 'social importance' }, 'science': { score: 16, reason: 'science priority' }, 'technology': { score: 15, reason: 'tech significance' }, 'culture': { score: 12, reason: 'cultural story' }, 'sport': { score: 10, reason: 'sports story' }, 'lifestyle': { score: 8, reason: 'lifestyle content' }, 'opinion': { score: 5, reason: 'opinion piece' } }; for (const [key, value] of Object.entries(sectionScores)) { if (section.includes(key)) { return value; } } return { score: 10 }; // Default base score } function getSectionBreakdown(articles: any[]): { section: string; count: number }[] { const sectionCounts: Record<string, number> = {}; articles.forEach(article => { const section = article.sectionName || 'Unknown'; sectionCounts[section] = (sectionCounts[section] || 0) + 1; }); return Object.entries(sectionCounts) .map(([section, count]) => ({ section, count })) .sort((a, b) => b.count - a.count); } function formatDateForDisplay(date: string): string { const dateObj = new Date(date); return dateObj.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); }

Implementation Reference

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/jbenton/guardian-mcp-server'

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