guardian_top_stories_by_date
Retrieve top stories from The Guardian archives for a specific date, ranked by editorial priority. Filter by section or adjust the number of stories for tailored results.
Instructions
Get intelligently ranked top stories for a specific date using editorial prioritization
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | Date to analyze (YYYY-MM-DD) | |
| section | No | Filter by section (optional) | |
| story_count | No | Number of top stories to return (default: 10, max: 20) |
Implementation Reference
- The main handler function that fetches Guardian articles for a specific date, applies intelligent scoring based on editorial factors (section, length, timing, keywords, tags, byline), ranks the top stories, and returns formatted output with daily analysis.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; }
- src/types/guardian.ts:175-179 (schema)Zod schema used for input parameter validation in the handler.export const TopStoriesByDateParamsSchema = z.object({ date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), story_count: z.number().min(1).max(20).optional(), section: z.string().optional(), });
- src/tools/index.ts:37-37 (registration)Registration of the tool name mapping to the camelCase handler function within the registerTools function.guardian_top_stories_by_date: (args) => guardianTopStoriesByDate(client, args),
- src/index.ts:491-512 (schema)MCP tool registration including name, description, and input schema for ListTools response.name: 'guardian_top_stories_by_date', description: 'Get intelligently ranked top stories for a specific date using editorial prioritization', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date to analyze (YYYY-MM-DD)', }, story_count: { type: 'integer', description: 'Number of top stories to return (default: 10, max: 20)', minimum: 1, maximum: 20, }, section: { type: 'string', description: 'Filter by section (optional)', }, }, required: ['date'], },
- Key helper function that scores individual stories based on multiple intelligent criteria for ranking.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 }; }