news-reporter
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.
stdiotransport, 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:
search_news("AI regulation EU", max_results=8)→ 8 articles from BBC, Guardian, NYT, GDELT global sourcesgenerate_report_structure(articles, "EU AI Regulation Update")→ fills in synthesis sections from raw evidencecreate_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_newsbudget: 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": "..."}toRSS_FEEDSinnews_reporter/config.py - Cache TTL: edit
SEARCH_CACHE_TTL_SECONDS/ARTICLE_CACHE_TTL_SECONDSinconfig.py - Custom font: drop a
.ttfnamedDejaVuSans-Bold.ttfintonews_reporter/assets/fonts/(without one, Pillow's bundled default is used) - Photocard palette: edit
PALETTEinnews_reporter/tools/photocard.py
Roadmap
-
topic_trendstool — GDELT volume timeseries for a query - Optional
og:imagescrape for richer photocard backgrounds - Multi-language UI (GDELT supports
sourcelangfilter)
PRs welcome.
Contributing
- Fork + clone
uv sync --extra dev- Make changes + add tests
uv run pytest -q(must stay green)- Open a PR
Bug reports + feature requests: https://github.com/SaimumIslam/news-reporter/issues
Credits
- Model Context Protocol by Anthropic
- FastMCP Python SDK
- GDELT Project — global news event database
- DejaVu Fonts — bundled with Pillow
- RSS publishers: BBC, Guardian, Al Jazeera, NPR, DW, NYT, Hacker News
License
MIT — see LICENSE. Use it, fork it, ship it.
If this project saves you time, a ⭐ on GitHub helps others discover it.
Metadata
Release files for news-reporter 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| news_reporter-0.1.1.tar.gz | 15.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| news_reporter-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.5 kB
Release files / news_reporter-0.1.1.tar.gz
| Download URL | news_reporter-0.1.1.tar.gz |
|---|---|
| Size | 15.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b50d1ff932ece7740aa5e0dee7ad90998bc6193ab54667960b33807da09e119
|
|
BLAKE2b-256 checksum How to use checksums |
33ce8ecb0a3a5d94a02524669319efa80bc451d9b94e060559afd6554c2bae7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / news_reporter-0.1.1-py3-none-any.whl
| Download URL | news_reporter-0.1.1-py3-none-any.whl |
|---|---|
| Size | 21.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e4bfdd4045281906cf91c54e124eb6cb7036c5a59eec59ead45c11d4c8744877
|
|
BLAKE2b-256 checksum How to use checksums |
9c3c5b36cb3ae47287a465134222819f21f17af2387c06f77f57441f1a478002
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|