Skip to main content
Glama

guardian_content_timeline

Track and analyze content trends and peaks for specific topics over time using The Guardian's article archives from 1999. Input search terms, date range, and interval to visualize topic activity.

Instructions

Analyze content timeline for a topic over time showing trends and peaks

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
from_dateYesStart date (YYYY-MM-DD)
intervalNoTime interval for analysis
queryYesTopic or search terms to analyze over time
sectionNoFilter by section (optional)
to_dateYesEnd date (YYYY-MM-DD)

Implementation Reference

  • Main handler function that executes the tool logic: validates input, generates time periods, queries Guardian API for each period, compiles timeline data with peaks, breakdowns, sample headlines, and trend analysis.
    export async function guardianContentTimeline(client: GuardianClient, args: any): Promise<string> { const params = ContentTimelineParamsSchema.parse(args); const fromDate = validateDate(params.from_date); const toDate = validateDate(params.to_date); if (!fromDate || !toDate) { throw new Error('Invalid date format. Use YYYY-MM-DD format.'); } const interval = params.interval || 'month'; // Generate time periods based on interval const periods = generateTimePeriods(fromDate, toDate, interval); let result = `Content Timeline for "${params.query}" (${fromDate} to ${toDate}):\n\n`; const timelineData: TimelineData[] = []; let totalArticles = 0; // Analyze each time period for (const period of periods) { const searchParams: Record<string, any> = { q: params.query, 'from-date': period.start, 'to-date': period.end, 'page-size': 10, // Get sample headlines 'show-fields': 'headline,firstPublicationDate', 'order-by': 'relevance' }; if (params.section) { searchParams.section = params.section; } try { const response = await client.search(searchParams); const articles = response.response.results; const count = response.response.total; const sampleHeadlines = articles.slice(0, 3).map(article => article.webTitle || 'Untitled'); timelineData.push({ period: period.label, count: count, sampleHeadlines: sampleHeadlines }); totalArticles += count; // Rate limiting: small delay between requests await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { timelineData.push({ period: period.label, count: 0, sampleHeadlines: [] }); } } // Find peak periods const maxCount = Math.max(...timelineData.map(d => d.count)); const peakPeriods = timelineData.filter(d => d.count === maxCount && d.count > 0); // Display timeline results result += `**Total Articles**: ${totalArticles}\n`; result += `**Analysis Period**: ${timelineData.length} ${interval}${timelineData.length !== 1 ? 's' : ''}\n\n`; if (peakPeriods.length > 0) { result += `**Peak Coverage** (${maxCount} articles):\n`; peakPeriods.forEach(peak => { result += `• ${peak.period}\n`; }); result += '\n'; } result += `**Timeline Breakdown**:\n`; timelineData.forEach(data => { const intensity = data.count === 0 ? '○' : data.count < maxCount * 0.3 ? '●' : data.count < maxCount * 0.7 ? '●●' : '●●●'; result += `${intensity} **${data.period}**: ${data.count} articles\n`; if (data.sampleHeadlines.length > 0) { data.sampleHeadlines.forEach(headline => { result += ` • ${headline}\n`; }); } result += '\n'; }); // Trend analysis if (timelineData.length >= 3) { const trend = analyzeTrend(timelineData); result += `**Trend Analysis**: ${trend}\n`; } return result; }
  • Zod input validation schema used by the handler to parse and validate arguments.
    export const ContentTimelineParamsSchema = z.object({ query: z.string(), from_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), to_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), interval: z.enum(['day', 'week', 'month', 'quarter']).optional(), section: z.string().optional(), });
  • Import and registration of the handler in the tools registry function registerTools, which maps tool name to handler.
    import { guardianContentTimeline } from './guardian-content-timeline.js'; import { guardianAuthorProfile } from './guardian-author-profile.js'; import { guardianTopicTrends } from './guardian-topic-trends.js'; import { guardianTopStoriesByDate } from './guardian-top-stories-by-date.js'; import { guardianRecommendLongreads } from './guardian-recommend-longreads.js'; export type ToolHandler = (args: any) => Promise<string>; export function registerTools(client: GuardianClient): Record<string, ToolHandler> { return { guardian_search: (args) => guardianSearch(client, args), guardian_get_article: (args) => guardianGetArticle(client, args), guardian_longread: (args) => guardianLongread(client, args), guardian_lookback: (args) => guardianLookback(client, args), guardian_browse_section: (args) => guardianBrowseSection(client, args), guardian_get_sections: (args) => guardianGetSections(client, args), guardian_search_tags: (args) => guardianSearchTags(client, args), guardian_search_by_length: (args) => guardianSearchByLength(client, args), guardian_search_by_author: (args) => guardianSearchByAuthor(client, args), guardian_find_related: (args) => guardianFindRelated(client, args), guardian_get_article_tags: (args) => guardianGetArticleTags(client, args), guardian_content_timeline: (args) => guardianContentTimeline(client, args),
  • Input schema for the tool as defined in the MCP ListTools response for client discovery.
    name: 'guardian_content_timeline', description: 'Analyze content timeline for a topic over time showing trends and peaks', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Topic or search terms to analyze over time', }, from_date: { type: 'string', description: 'Start date (YYYY-MM-DD)', }, to_date: { type: 'string', description: 'End date (YYYY-MM-DD)', }, interval: { type: 'string', description: 'Time interval for analysis', enum: ['day', 'week', 'month', 'quarter'], }, section: { type: 'string', description: 'Filter by section (optional)', }, }, required: ['query', 'from_date', 'to_date'], }, },
  • Helper function that generates the time periods (day/week/month/quarter) between from_date and to_date for the timeline analysis.
    function generateTimePeriods(fromDate: string, toDate: string, interval: string): TimelinePeriod[] { const periods: TimelinePeriod[] = []; const start = new Date(fromDate); const end = new Date(toDate); let current = new Date(start); while (current <= end) { let periodEnd = new Date(current); let label = ''; switch (interval) { case 'day': label = current.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); break; case 'week': periodEnd.setDate(current.getDate() + 6); if (periodEnd > end) periodEnd = new Date(end); label = `Week of ${current.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`; break; case 'month': periodEnd = new Date(current.getFullYear(), current.getMonth() + 1, 0); if (periodEnd > end) periodEnd = new Date(end); label = current.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); break; case 'quarter': const quarter = Math.floor(current.getMonth() / 3) + 1; periodEnd = new Date(current.getFullYear(), quarter * 3, 0); if (periodEnd > end) periodEnd = new Date(end); label = `Q${quarter} ${current.getFullYear()}`; break; } periods.push({ start: current.toISOString().substring(0, 10), end: periodEnd.toISOString().substring(0, 10), label: label }); // Move to next period switch (interval) { case 'day': current.setDate(current.getDate() + 1); break; case 'week': current.setDate(current.getDate() + 7); break; case 'month': current.setMonth(current.getMonth() + 1); current.setDate(1); break; case 'quarter': current.setMonth(current.getMonth() + 3); current.setDate(1); break; } } return periods; }

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