Skip to main content

MCP server for news aggregation, structured reports, and photocard generation. RSS + GDELT, no API keys required.

Project description

news-reporter

PyPI Python License: MIT MCP

A free, zero-API-key Model Context Protocol server that lets any LLM search global news, build structured reports, and generate social-ready photocards.

Built with FastMCP. Aggregates from RSS feeds + the GDELT 2.0 DOC API. SQLite cache cuts repeat upstream calls.


Why use this

  • Free forever. No NewsAPI key. No DALL-E. No Cloudinary. RSS + GDELT only.
  • Three tools, one server. Search → structure → visualize, no chaining services.
  • Cached. SQLite layer dedupes articles and skips redundant fetches inside a 1-hour window.
  • Drop-in for Claude Desktop, Cursor, Cline, Continue, Zed, or any MCP client.
  • Zero config. stdio transport, works out of the box with any MCP client.

Tools

Tool What it does Returns
search_news(query, max_results, date_from, date_to) Searches RSS + GDELT in parallel, dedupes, caches List of {url, source, headline, published_at, snippet}
generate_report_structure(articles, topic) Builds a Markdown skeleton with timeline + raw evidence sections for the LLM to fill in Markdown string
create_photocard(headline, subtitle, output_path) Renders a 1080×1080 PNG with gradient background + auto-fitted headline {path, base64, width, height, mime_type}

Install

Option A — Local (stdio)

# Run directly without installing (recommended):
uvx news-reporter

# Or install globally:
pipx install news-reporter

Then add to your MCP client config:

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)

{
  "mcpServers": {
    "news-reporter": {
      "command": "uvx",
      "args": ["news-reporter"]
    }
  }
}

Restart Claude Desktop. Three tools appear in the tool picker.

Cursor / Cline / Continue / Zed

Same JSON, in their respective MCP config files. See https://modelcontextprotocol.io/clients.


Example workflow

User asks Claude: "Get me the latest on EU AI regulation and make a photocard."

Claude calls:

  1. search_news("AI regulation EU", max_results=8) → 8 articles from BBC, Guardian, NYT, GDELT global sources
  2. generate_report_structure(articles, "EU AI Regulation Update") → fills in synthesis sections from raw evidence
  3. create_photocard("EU Passes Sweeping AI Regulation", subtitle="May 2026") → 1080×1080 PNG ready for Twitter/Instagram

Total: ~10 seconds, $0 in API costs.


Tool reference

search_news

search_news(
  query: str,
  max_results: int = 10,
  date_from: str | None = None,   # "YYYY-MM-DD"
  date_to: str | None = None,
) -> dict

Returns:

{
  "query": "AI regulation EU",
  "count": 5,
  "articles": [
    {
      "url": "https://www.bbc.com/news/...",
      "source": "BBC World",
      "headline": "EU passes new AI regulation framework",
      "published_at": "2026-05-10T09:30:00+00:00",
      "snippet": "The European Union approved..."
    }
  ],
  "partial": false,
  "errors": [],
  "cache_hit": false
}

partial: true means at least one source failed (rate-limited, timeout, etc) but others succeeded. The tool never raises.

generate_report_structure

Returns Markdown with five sections:

# Report: <topic>
_Generated: <timestamp> | Sources: <N> articles from <M> outlets_

## Executive Summary
<!-- LLM: synthesize 3-5 sentences -->

## Key Events / Timeline
- 2026-05-10 — Headline (Source) — URL
...

## Conflicting Perspectives
<!-- LLM: identify divergent framings -->

## Impact Analysis
<!-- LLM: short, medium, long-term impacts -->

## Source Articles (raw)
1. Headline — Source — Published — URL — snippet
...

The HTML-comment placeholders are written for the consumer LLM to expand using the structured raw evidence below.

create_photocard

  • Canvas: 1080×1080 (Instagram-ready)
  • Background: vertical gradient seeded by sha256(headline) → same topic always renders same colors
  • Headline: bundled DejaVuSans Bold, auto-wrapped, auto-fit shrink loop (84px → 36px, max 4 lines)
  • Subtitle: optional second line
  • Watermark: bottom-right "news-reporter"
  • Output: writes PNG + returns base64 — caller can embed without filesystem access

Configuration

All optional. Set via env or .env file.

Variable Default Purpose
LOG_LEVEL INFO DEBUG, INFO, WARNING, ERROR
NEWS_DB_PATH ./news.db SQLite cache path
PHOTOCARD_OUTPUT_DIR ./photocards PNG output directory

Architecture

news_reporter/
├── server.py          FastMCP entrypoint, registers 3 tools, transport switch
├── config.py          paths, RSS feed list, TTL constants
├── db.py              SQLite cache (auto-init on first connect)
├── models.py          Article + SearchResult dataclasses
├── sources/
│   ├── rss.py         feedparser + httpx parallel fetch
│   ├── gdelt.py       GDELT 2.0 DOC API client
│   └── scrape.py      BeautifulSoup full-text extractor
├── tools/
│   ├── search.py      search_news impl
│   ├── report.py      generate_report_structure impl
│   └── photocard.py   create_photocard impl (Pillow)
└── assets/fonts/      drop a TTF here to override the default font

Cache schema

articles(url PK, source, headline, published_at, snippet, full_text, fetched_at)
search_cache(query_hash PK, query, article_urls_json, cached_at)

query_hash = sha256(lower(query) + date_from + date_to). Search TTL 1h. Article TTL 24h.

Reliability

  • Per-source HTTP timeout: 8s
  • Total search_news budget: 15s
  • GDELT 429 → exponential backoff (3 attempts, 1/2/4s)
  • Any source failure → partial: true + per-source error in response; other sources still returned
  • Tools never raise to the MCP layer

Development

git clone https://github.com/SaimumIslam/news-reporter
cd news-reporter
brew install uv             # if not installed
uv sync --extra dev
uv run pytest -q            # 36 tests, ~0.6s

Running locally

# stdio (for MCP clients):
uv run news-reporter

# Live debugger:
npx @modelcontextprotocol/inspector uv run news-reporter

Customization

  • Add an RSS source: append {"name": "...", "url": "..."} to RSS_FEEDS in news_reporter/config.py
  • Cache TTL: edit SEARCH_CACHE_TTL_SECONDS / ARTICLE_CACHE_TTL_SECONDS in config.py
  • Custom font: drop a .ttf named DejaVuSans-Bold.ttf into news_reporter/assets/fonts/ (without one, Pillow's bundled default is used)
  • Photocard palette: edit PALETTE in news_reporter/tools/photocard.py

Roadmap

  • topic_trends tool — GDELT volume timeseries for a query
  • Optional og:image scrape for richer photocard backgrounds
  • Multi-language UI (GDELT supports sourcelang filter)

PRs welcome.


Contributing

  1. Fork + clone
  2. uv sync --extra dev
  3. Make changes + add tests
  4. uv run pytest -q (must stay green)
  5. Open a PR

Bug reports + feature requests: https://github.com/SaimumIslam/news-reporter/issues


Credits


License

MIT — see LICENSE. Use it, fork it, ship it.


If this project saves you time, a ⭐ on GitHub helps others discover it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

news_reporter-0.1.1.tar.gz (15.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

news_reporter-0.1.1-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file news_reporter-0.1.1.tar.gz.

File metadata

  • Download URL: news_reporter-0.1.1.tar.gz
  • Upload date:
  • Size: 15.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for news_reporter-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6b50d1ff932ece7740aa5e0dee7ad90998bc6193ab54667960b33807da09e119
MD5 7700195be37f6924dd4c4e5150562c53
BLAKE2b-256 33ce8ecb0a3a5d94a02524669319efa80bc451d9b94e060559afd6554c2bae7d

See more details on using hashes here.

File details

Details for the file news_reporter-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: news_reporter-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for news_reporter-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e4bfdd4045281906cf91c54e124eb6cb7036c5a59eec59ead45c11d4c8744877
MD5 5e7950f41cfae792106dea2fd7afbd51
BLAKE2b-256 9c3c5b36cb3ae47287a465134222819f21f17af2387c06f77f57441f1a478002

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page