check_feeds
Verify RSS feed health and availability for debugging and monitoring Icelandic news sources across multiple languages and categories.
Instructions
Check the health and availability of RSS feeds. Useful for debugging and monitoring.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sources | No | Specific sources to check. If not specified, checks all sources. | |
| timeout | No | Timeout in milliseconds for each feed check (1000-30000) |
Implementation Reference
- src/index.ts:893-987 (handler)The handler function for 'check_feeds' tool. Loops through sources and feeds, attempts to fetch a small number of news items using fetchNews (without cache), with per-feed timeout using AbortController. Collects success/failure stats including item count, response time, and errors. Returns markdown summary and structured FeedHealth data.async ({ sources, timeout }) => { const checkedAt = new Date().toISOString(); const sourcesToCheck = sources && sources.length > 0 ? sources.filter(s => s in SOURCES) as SourceName[] : (Object.keys(SOURCES) as SourceName[]); const results: Array<{ source: string; sourceName: string; feed: string; feedDescription: string; status: "healthy" | "failed"; itemCount?: number; error?: string; responseTimeMs?: number; }> = []; for (const source of sourcesToCheck) { const sourceInfo = SOURCES[source]; for (const [feedKey, feedInfo] of Object.entries(sourceInfo.feeds)) { const startTime = Date.now(); try { // Use AbortController for timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetchNews(source, feedKey, 5, false); clearTimeout(timeoutId); results.push({ source, sourceName: sourceInfo.name, feed: feedKey, feedDescription: feedInfo.description, status: "healthy", itemCount: response.count, responseTimeMs: Date.now() - startTime, }); } catch (error) { results.push({ source, sourceName: sourceInfo.name, feed: feedKey, feedDescription: feedInfo.description, status: "failed", error: error instanceof Error ? error.message : String(error), responseTimeMs: Date.now() - startTime, }); } } } const healthyFeeds = results.filter(r => r.status === "healthy").length; const failedFeeds = results.filter(r => r.status === "failed").length; const healthResult = { checkedAt, totalFeeds: results.length, healthyFeeds, failedFeeds, results, }; // Format as markdown let markdown = `# Feed Health Check\n\n`; markdown += `*Checked at ${checkedAt}*\n\n`; markdown += `| Status | Count |\n|--------|-------|\n`; markdown += `| ✅ Healthy | ${healthyFeeds} |\n`; markdown += `| ❌ Failed | ${failedFeeds} |\n`; markdown += `| Total | ${results.length} |\n\n`; if (failedFeeds > 0) { markdown += `## Failed Feeds\n\n`; for (const result of results.filter(r => r.status === "failed")) { markdown += `- **${result.sourceName}** / ${result.feedDescription}: ${result.error}\n`; } markdown += "\n"; } markdown += `## All Results\n\n`; markdown += `| Source | Feed | Status | Items | Time (ms) |\n|--------|------|--------|-------|------------|\n`; for (const result of results) { const status = result.status === "healthy" ? "✅" : "❌"; const items = result.itemCount ?? "-"; const time = result.responseTimeMs ?? "-"; markdown += `| ${result.sourceName} | ${result.feed} | ${status} | ${items} | ${time} |\n`; } return { content: [{ type: "text" as const, text: markdown }], structuredContent: healthResult, }; }
- src/index.ts:858-873 (schema)Zod schema defining the structured output for the check_feeds tool, including overall stats and per-feed health results.const FeedHealthSchema = z.object({ checkedAt: z.string(), totalFeeds: z.number(), healthyFeeds: z.number(), failedFeeds: z.number(), results: z.array(z.object({ source: z.string(), sourceName: z.string(), feed: z.string(), feedDescription: z.string(), status: z.enum(["healthy", "failed"]), itemCount: z.number().optional(), error: z.string().optional(), responseTimeMs: z.number().optional(), })), });
- src/index.ts:875-892 (registration)MCP server registration of the 'check_feeds' tool, specifying description, input schema (sources array optional, timeout default 10s), and output schema.server.registerTool( "check_feeds", { description: "Check the health and availability of RSS feeds. Useful for debugging and monitoring.", inputSchema: { sources: z .array(z.string()) .optional() .describe("Specific sources to check. If not specified, checks all sources."), timeout: z .number() .min(1000) .max(30000) .default(10000) .describe("Timeout in milliseconds for each feed check (1000-30000)"), }, outputSchema: FeedHealthSchema, },
- src/index.ts:336-398 (helper)Key helper function called by check_feeds to test each feed by fetching 5 recent items (cache bypassed). Handles RSS parsing, error throwing for invalid feeds, and returns NewsResponse.async function fetchNews( source: SourceName, feed: string, limit: number = 10, useCache: boolean = true ): Promise<NewsResponse> { const sourceInfo = SOURCES[source]; const feedInfo = sourceInfo.feeds[feed]; if (!feedInfo) { throw new Error(`Unknown feed "${feed}" for source "${source}"`); } // Check cache first (cache stores max items, we slice later) if (useCache) { const cached = getFromCache(source, feed); if (cached) { // Return cached data with limit applied return { ...cached, count: Math.min(cached.items.length, limit), items: cached.items.slice(0, limit), }; } } const result = await parser.parseURL(feedInfo.url); const fetchedAt = new Date().toISOString(); // Fetch more items for cache (up to 50) const maxItems = 50; const items: NewsItem[] = result.items.slice(0, maxItems).map((item) => ({ title: item.title || "No title", link: item.link || "", description: item.contentSnippet || item.content || "No description", pubDate: item.pubDate ? new Date(item.pubDate).toISOString() : fetchedAt, creator: (item["dc:creator"] as string | undefined) || null, source, sourceName: sourceInfo.name, feed, feedDescription: feedInfo.description, })); const fullResponse: NewsResponse = { source, sourceName: sourceInfo.name, feed, feedDescription: feedInfo.description, fetchedAt, count: items.length, items, }; // Store full response in cache setCache(source, feed, fullResponse); // Return with requested limit return { ...fullResponse, count: Math.min(items.length, limit), items: items.slice(0, limit), }; }