Metadata-Version: 2.4
Name: news-reporter
Version: 0.1.0
Summary: MCP server for news aggregation, structured reports, and photocard generation. RSS + GDELT, no API keys required.
Project-URL: Homepage, https://github.com/SaimumIslam/news-reporter
Project-URL: Repository, https://github.com/SaimumIslam/news-reporter
Project-URL: Issues, https://github.com/SaimumIslam/news-reporter/issues
Project-URL: Changelog, https://github.com/SaimumIslam/news-reporter/releases
Author-email: Saimum Islam <saimumislam27@gmail.com>
Maintainer-email: Saimum Islam <saimumislam27@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai,claude,fastmcp,gdelt,llm,mcp,model-context-protocol,news,photocard,rss
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: Markdown
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: feedparser>=6.0
Requires-Dist: httpx>=0.27
Requires-Dist: lxml>=5.0
Requires-Dist: mcp[cli]>=1.0.0
Requires-Dist: pillow>=10.0
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# news-reporter

[![PyPI](https://img.shields.io/pypi/v/news-reporter.svg)](https://pypi.org/project/news-reporter/)
[![Python](https://img.shields.io/pypi/pyversions/news-reporter.svg)](https://pypi.org/project/news-reporter/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-1.0-blue.svg)](https://modelcontextprotocol.io)
[![smithery badge](https://smithery.ai/badge/@SaimumIslam/news-reporter)](https://smithery.ai/server/@SaimumIslam/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](https://github.com/modelcontextprotocol/python-sdk). Aggregates from RSS feeds + the [GDELT 2.0 DOC API](https://blog.gdeltproject.org/gdelt-doc-2-0-api-debuts/). 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.**
- **Both transports.** `stdio` for local, `streamable-http` for remote hosting.

---

## 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)

```bash
# 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`)

```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.

### Option B — Hosted (HTTP)

Use the Smithery-hosted version:

```json
{
  "mcpServers": {
    "news-reporter": {
      "url": "https://server.smithery.ai/@SaimumIslam/news-reporter/mcp"
    }
  }
}
```

No install. Updates automatically.

### Option C — Self-host with Docker

```bash
docker run -d -p 8000:8000 -v news-data:/data ghcr.io/saimumislam/news-reporter:latest
```

Or build from source:

```bash
git clone https://github.com/SaimumIslam/news-reporter
cd news-reporter
docker build -t news-reporter .
docker run -d -p 8000:8000 -v "$PWD/data:/data" news-reporter
```

The container runs `streamable-http` on port 8000 with `/data` for the SQLite cache.

---

## 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`

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

Returns:

```json
{
  "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 |
|---|---|---|
| `MCP_TRANSPORT` | `stdio` | `stdio`, `streamable-http`, or `sse` |
| `HOST` | `0.0.0.0` | Bind host (HTTP transport) |
| `PORT` | `8000` | Bind port (HTTP transport) |
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
| `NEWS_DB_PATH` | `./news.db` | SQLite cache path |
| `PHOTOCARD_OUTPUT_DIR` | `./photocards` | PNG output directory |

---

## Architecture

```
src/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

```sql
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

```bash
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.5s
```

### Running locally

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

# HTTP (for hosted use):
MCP_TRANSPORT=streamable-http PORT=8000 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 `src/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 `src/news_reporter/assets/fonts/` (without one, Pillow's bundled default is used)
- **Photocard palette**: edit `PALETTE` in `src/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)
- [ ] Auth header support for remote deployments
- [ ] Turso/LibSQL adapter for edge caching

PRs welcome. See `DEPLOY.md` for publish + hosting workflow.

---

## 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

- [Model Context Protocol](https://modelcontextprotocol.io) by Anthropic
- [FastMCP](https://github.com/modelcontextprotocol/python-sdk) Python SDK
- [GDELT Project](https://www.gdeltproject.org/) — global news event database
- [DejaVu Fonts](https://dejavu-fonts.github.io/) — bundled with Pillow
- RSS publishers: BBC, Guardian, Al Jazeera, NPR, DW, NYT, Hacker News

---

## License

MIT — see [LICENSE](LICENSE). Use it, fork it, ship it.

---

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