Skip to main content
Glama

guardian_top_stories_by_date

Retrieve top-ranked Guardian news stories for a specific date using editorial prioritization, with optional filtering by section and story count.

Instructions

Get intelligently ranked top stories for a specific date using editorial prioritization

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dateYesDate to analyze (YYYY-MM-DD)
story_countNoNumber of top stories to return (default: 10, max: 20)
sectionNoFilter by section (optional)

Implementation Reference

  • Main handler function implementing the tool: parses args, validates date, searches Guardian API for articles on that date, scores them using intelligent criteria (section, length, pub time, headline keywords, tags, byline), ranks top stories, provides analysis, and formats output.
    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; }
  • Zod input schema used for validating tool arguments: requires 'date' in YYYY-MM-DD format, optional 'story_count' (1-20), optional 'section'.
    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(), }); export const RecommendLongreadsParamsSchema = z.object({ count: z.number().min(1).max(10).optional(), context: z.string().optional(), from_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(), topic_preference: z.string().optional(), }); export type ContentTimelineParams = z.infer<typeof ContentTimelineParamsSchema>; export type AuthorProfileParams = z.infer<typeof AuthorProfileParamsSchema>; export type TopicTrendsParams = z.infer<typeof TopicTrendsParamsSchema>; export type TopStoriesByDateParams = z.infer<typeof TopStoriesByDateParamsSchema>;
  • Tool registration in the registerTools function, mapping the tool name to its handler.
    guardian_top_stories_by_date: (args) => guardianTopStoriesByDate(client, args),
  • src/index.ts:491-512 (registration)
    MCP server tool list registration including name, description, and input schema for ListToolsRequest.
    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'], },
  • Core helper function scoring each story's importance for ranking based on section priority, article length, publication timing, headline analysis, tags, and author seniority.
    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 }; }

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