Skip to main content

🇨🇭 Part of the Swiss Public Data MCP Portfolio

📰 news-monitor-mcp

Version License: MIT Python 3.11+ MCP Data Source CI

MCP server for global news monitoring, media analysis and sentiment tracking via WorldNewsAPI — full-text search across 150+ countries, German/English sentiment analysis, top headlines, GL briefings, newspaper front pages and geo-search. API key required.

🇩🇪 Deutsche Version


Overview

news-monitor-mcp transforms any AI assistant into a proactive media intelligence agent. The server connects LLMs like Claude with global news data: from Swiss institutional reputation monitoring to weekly leadership briefings and trend detection across categories.

Source: WorldNewsAPI (worldnewsapi.com) — the only freely available news API with German-language sentiment analysis.

API key required. Get a free key at worldnewsapi.com/console — free plan: 50 points/day, no credit card, backlink to worldnewsapi.com required (checked 2026-08-14).

Anchor demo query: "How has the Schulamt Zürich been portrayed in the media over the last 30 days, and what is the overall sentiment?"


Features

  • 🔍 Full-text search – 150+ countries, 50+ languages, Boolean queries and exact phrase matching
  • 📊 Sentiment analysis – German and English only (WorldNewsAPI unique feature); scores from −1 (negative) to +1 (positive)
  • 📰 Top headlines – clustered by country and language, ranked by number of sources reporting
  • 📋 Media briefing – multi-topic weekly report with sentiment overview for GL / leadership updates
  • 🗞️ Newspaper front pages – digital covers from 6,000+ publications in 125 countries
  • 📡 Trend radar – category-based trend detection (politics, technology, education, …) per country
  • 📍 Geo-search – location-specific news (Zürich, Bern, Basel, Kanton Zürich, …)
  • ☁️ Dual transport – stdio for Claude Desktop, Streamable HTTP for cloud deployment
# Tool Description
1 news_search Full-text news search in 150+ countries
2 news_top_headlines Top headlines by country and language
3 news_sentiment_monitor Sentiment analysis for entity or topic
4 news_media_briefing Multi-topic weekly briefing report
5 news_retrieve_article Fetch full article by ID
6 news_search_sources Find available news sources by name/country
7 news_front_pages Digital newspaper front pages — paid plan required, see below
8 news_trend_radar Category-based trend detection per country
9 news_geo_search Location-specific news search
10 news_alert_create Create a persistent alert (sentiment / volume / keyword)
11 news_alert_list List configured alerts with status
12 news_alert_check Evaluate alerts against current data
13 news_alert_delete Permanently remove an alert
14 news_cache_stats Cache hit-rate and entries by type
15 news_cache_clear Clear cache (entirely or per tool type)

Demo

Media Briefing Demo

"Create a media briefing for: AI in education, Volksschule Zürich, school digitalisation"


Data Sources

Source API Type Content
WorldNewsAPI REST JSON 150+ countries, 50+ languages, full text, sentiment

Prerequisites


Installation

# Recommended: uvx (no install step needed)
uvx news-monitor-mcp

# Alternative: pip
pip install news-monitor-mcp

Quickstart

# Start the server (stdio mode for Claude Desktop)
WORLD_NEWS_API_KEY=your-key uvx news-monitor-mcp

Try it immediately in Claude Desktop:

"Show me the top news from Switzerland today" "How is the Schulamt Zürich covered in German-language media this month?" "Create a media briefing on: Volksschule Zürich, AI in education, school digitalisation"


Configuration

Environment Variables

Variable Default Description
WORLD_NEWS_API_KEY – Required. API key from worldnewsapi.com
MCP_TRANSPORT stdio Transport: stdio or streamable_http
MCP_HOST 127.0.0.1 HTTP bind host. Use 0.0.0.0 only inside a container.
MCP_PORT 8000 Port for HTTP transport
MCP_BEARER_TOKEN – Required in --http mode. Bearer token clients must present in Authorization: Bearer <token>. Generate via python -c "import secrets; print(secrets.token_urlsafe(32))".
MCP_ALLOWED_ORIGINS – Optional CSV allowlist for the Origin header (DNS-rebinding protection). Example: https://claude.ai.
LOG_LEVEL INFO Log level: DEBUG / INFO / WARNING / ERROR. Logs are emitted as JSON to stderr with automatic redaction of api-key= query params and Authorization: Bearer headers.
NEWS_MONITOR_ALERTS_DIR ~/.news-monitor-mcp Directory that holds alerts.json. The parent dir must not be a symlink (refused at startup as a defense against path-injection). File is created with mode 0o600, directory with 0o700.
NEWS_MONITOR_ALERTS_FILE – (Back-compat) explicit path to the alerts file. Same symlink check applies. Prefer NEWS_MONITOR_ALERTS_DIR.
MCP_ALERT_RETENTION_DAYS 90 Alerts older than this many days are deleted on server start (Privacy default per docs/privacy-dsg.md). Set to 0 to disable retention.
MCP_CACHE_MAX_PER_TYPE 1000 Maximum cache entries per tool type. When exceeded, the least-recently-used entry of that type is evicted. Set to 0 to disable the cap (unbounded growth — only safe for short-lived processes).
MCP_CACHE_SWEEP_SECONDS 300 Interval for the background task that removes TTL-expired entries from the cache. Set to 0 to disable the sweep (expired entries are still pruned lazily on news_cache_stats).

Claude Desktop Configuration

{
  "mcpServers": {
    "news-monitor": {
      "command": "uvx",
      "args": ["news-monitor-mcp"],
      "env": {
        "WORLD_NEWS_API_KEY": "your-api-key-here"
      }
    }
  }
}

Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

After restarting Claude Desktop, all tools are available. Example queries:

  • "Show me the top Swiss news today"
  • "What is the media sentiment on AI in education this month?"
  • "Create a weekly briefing for: Schulamt Zürich, Volksschule, KI Bildung"
  • "Find all German-language articles about school digitalisation in the last 14 days"
  • "Show me the front pages of Swiss newspapers today"

Cloud Deployment (Streamable HTTP)

For use via claude.ai in the browser (e.g. on managed workstations without local software):

Authentication is mandatory. The HTTP transport refuses any request without a valid Authorization: Bearer <token> header. Generate a token once and keep it secret:

python -c "import secrets; print(secrets.token_urlsafe(32))"

Render.com (recommended):

  1. Push/fork the repository to GitHub
  2. On render.com: New Web Service → connect GitHub repo
  3. Set the following environment variables in the Render dashboard:
    • WORLD_NEWS_API_KEY — your WorldNewsAPI key
    • MCP_BEARER_TOKEN — the token generated above
    • MCP_HOST=0.0.0.0 — bind on all interfaces inside the container
    • MCP_ALLOWED_ORIGINS=https://claude.ai (optional, recommended)
  4. In claude.ai under Settings → MCP Servers, add the URL https://your-app.onrender.com/mcp and configure the Bearer token as the auth header.
# Local HTTP mode (binds 127.0.0.1 by default)
WORLD_NEWS_API_KEY=your-key \
  MCP_BEARER_TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(32))") \
  news-monitor-mcp --http --port 8000

# Verify auth is enforced
curl -i http://127.0.0.1:8000/mcp                                  # → 401
curl -i -H "Authorization: Bearer $MCP_BEARER_TOKEN" http://127.0.0.1:8000/mcp

Scaling notes

This server is currently single-process / single-replica:

  • The TTL cache lives in process memory (NewsCache). If you run multiple Render or Kubernetes replicas, each replica has its own cache — hit-rates drop linearly with the replica count.
  • Alerts persist to a local alerts.json (defaults to /data inside the container). Multiple replicas mounting the same persistent volume serialize via fcntl.flock, but for true cluster operation a shared store (Redis / Postgres) is needed — see the open finding SCALE-STATEFUL.
  • A pending confirmation (input_required, see MCP Protocol Version) carries a requestState that the SDK seals with a key held only by this process, valid for 10 minutes. A retry that reaches another replica, arrives after a restart, or comes later than that is rejected with -32602 Invalid or expired requestState — nothing is deleted, the client calls again and the person is asked again. Running several replicas would need a shared key (request_state_security= on MCPServer), which this server does not configure.
  • On Render Free Tier, the container sleeps after ~15 minutes of inactivity and loses non-persistent state. Attach a Persistent Disk for /data if you need alerts to survive restarts. For Render Free + alerts you must accept that the cache is lost on every wake-up.

The MCP_CACHE_MAX_PER_TYPE cap (default 1000 entries / type) and the background sweep (MCP_CACHE_SWEEP_SECONDS, default 5 min) prevent the in-process cache from growing without bound.

Container image

A non-root multi-stage Dockerfile is included and built on every CI run. Inside the container the server defaults to --http, binds 0.0.0.0:8000, persists alerts under /data, and refuses to start if MCP_BEARER_TOKEN is missing.

docker build -t news-monitor-mcp .

docker run --rm -p 8000:8000 \
  -e WORLD_NEWS_API_KEY=your-key \
  -e MCP_BEARER_TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(32))") \
  -e MCP_ALLOWED_ORIGINS=https://claude.ai \
  -v news-monitor-data:/data \
  news-monitor-mcp

Architecture

┌─────────────────┐    ┌──────────────────────────┐    ┌──────────────────────────┐
│  Claude / AI    │────▶│   News Monitor MCP        │────▶│   WorldNewsAPI           │
│  (MCP Host)     │◀────│   (MCP Server)            │◀────│   REST JSON API          │
└─────────────────┘    │                            │    │   150+ countries         │
                       │  9 Tools                   │    │   50+ languages          │
                       │  Stdio | Streamable HTTP   │    │   Sentiment DE/EN        │
                       └──────────────────────────┘    └──────────────────────────┘

Project Structure

news-monitor-mcp/
├── src/
│   └── news_monitor_mcp/
│       ├── __init__.py
│       └── server.py          # All 9 tools
├── tests/
│   ├── __init__.py
│   └── test_server.py         # 20 tests (unit + live)
├── pyproject.toml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md                  # This file (English)
└── README.de.md               # German version

MCP Protocol Version

This server speaks two protocol eras over the same endpoint. The client's first request on a connection decides which one applies; a later claim from the other era is refused.

Era Revision Who reaches it
initialize handshake 2024-11-05 … 2025-11-25 What today's clients speak. The server answers with the revision asked for, or with the 2025-11-25 ceiling when the request asks for something newer.
Per-request envelope 2026-07-28 A request carrying the 2026-07-28 _meta envelope opens a modern connection.

Both revisions are pinned in tests/test_protocol_version.py and asserted against the installed SDK, so a Dependabot bump of mcp cannot move either one silently. The handshake ceiling is measured against a live initialize through the assembled ASGI stack, not read off a constant name.

Note that the SDK's LATEST_PROTOCOL_VERSION is an alias for the modern era, not for the handshake era — pinning against it alone would leave the era that current clients actually negotiate free to drift.

Update policy. When the gate fails, do not edit the constant blindly: read the spec changelog between the two revisions, verify the server still behaves, then move the constant, this section, README.de.md and CHANGELOG.md together.

What the server contributes in 2026-07-28

The SDK carries the envelope, server/discover and the error codes. Three things it does not get right on its own, measured through the assembled ASGI stack in tests/test_spec_2026_07_28.py:

  • Identity. Without a handshake, serverInfo travels in server/discover and in the _meta of every result. It used to carry "version": ""; it now names version (from the package metadata), title, description and website.
  • No sessions, in either era. The spec removed Mcp-Session-Id (SEP-2567). The HTTP transport now runs stateless for handshake clients too: this server keeps no per-connection state — alerts are addressed by server-minted IDs passed as tool arguments, the cache is process-wide. A session no longer pins a client to one process; what still does is listed under Scaling notes.
  • Human confirmation via input_required (SEP-2322) for the two destructive tools, news_alert_delete and news_cache_clear. When a 2026-07-28 client declares form elicitation, the server asks the person and only acts on their answer — a confirm=true set by the model no longer skips the question. Clients without form elicitation, and every handshake client, keep the confirm flow unchanged.

Not changed on purpose: server/discover still lists prompts and resources capabilities, because MCPServer registers both handlers unconditionally; both lists are empty.


Testing

# Unit + contract tests (no network) — this is what CI runs
PYTHONPATH=src pytest tests/ -m "not live"

# Live tests. The route check needs no key; the data tests skip without one.
PYTHONPATH=src pytest tests/ -m "live"

# Re-record the route inventory (writes tests/fixtures/ + PROVENANCE.md)
PYTHONPATH=src python scripts/record_fixtures.py

256 tests — 249 offline, 7 live (2 of which need no API key).

The live tests run daily at 06:17 UTC via .github/workflows/live-tests.yml, not on push: they measure the source, which changes independently of this repo.

Three live tests never ran

Until 2026-08-08 the repo's three live tests carried @pytest.mark.live but no @pytest.mark.asyncio. Under pytest-asyncio's strict default that does not mean "skipped" — it means async def functions are not natively supported. They never executed, and anyone running -m live got three errors that said nothing about the source. CI excludes -m live, so nothing reported it.

asyncio_mode = "auto" now makes a forgotten marker unable to cause this.

And running would not have shown much either:

assert "Volksschule" in result or "Ergebnisse" in result

The second branch matches the tool's own results heading, so the disjunction could not fail. assert "Top-Schlagzeilen" in result and assert "Sentiment" in result likewise matched only the template. All three now assert something that can fail, and they skip rather than fail when no key is set — "red" should mean something is wrong, not that you have no key.

What is verified without a key, and what is not

tests/fixtures/api_routen.json records, for each of the five paths the tools build, the status code and content type measured without a key. The gateway routes before authenticating:

Path Response
the five paths the server builds 401, application/json
a freely invented path (control) 404, text/html

So a 401 means "this route exists". Without the control it would only mean "I got a 401" — and that is not a given: epl.bag.admin.ch elsewhere in this portfolio answers 401 for invented paths too. The recorder therefore re-measures the control on every run and aborts if it stops discriminating.

Still open, and marked as such in PROVENANCE.md: whether the query parameter names the server sends are correct. The API answers 401 regardless of parameters, so no key means no verification. In global-education-mcp in this same portfolio exactly that went wrong — two filters were silently inert because unknown parameters were answered with HTTP 200 and dropped. That check is outstanding, not done.

Empty result vs. changed response shape

data.get("news", []) answers two entirely different cases the same way: "the source found nothing" and "the source answers differently than we assume". The second becomes "0 results" — complete, plausible, formatted and wrong. That is not hypothetical: in global-education-mcp the envelope had been renamed, so every answer came back empty while 128 tests stayed green.

articles_of() now reads the envelope. An empty news stays an empty list — a statement by the source. A missing news is not a statement about the news but about the response, and is reported as such.


Example Use Cases

Schulamt / Institutional Communication

"How has the Schulamt Zürich been portrayed in media over the last 30 days?"
→ news_sentiment_monitor(entity="Schulamt Zürich", language="de", days_back=30)

"Create a weekly media briefing for leadership"
→ news_media_briefing(topics=["Volksschule Zürich", "KI Bildung", "Schuldigitalisierung"])

"What are Swiss media reporting on school digitalisation?"
→ news_search(query="Schuldigitalisierung", language="de", source_country="ch")

KI-Fachgruppe / AI Working Group

"What are the current tech trends in Swiss press this week?"
→ news_trend_radar(category="technology", source_country="ch", language="de")

"How are AI developments in education covered internationally?"
→ news_search(query="AI education classroom", language="en", number=20)

"Compare Swiss and German media coverage of AI regulation"
→ news_search(query="KI Regulierung", source_country="ch", language="de")
→ news_search(query="KI Regulierung", source_country="de", language="de")

City Administration / Location Research

"What is being reported about Zürich school infrastructure?"
→ news_geo_search(location="Zürich", query="Schule")

"Show today's front pages of Swiss newspapers"
→ news_front_pages(source_country="ch")

→ More use cases by audience →


Sentiment Analysis

WorldNewsAPI offers German-language sentiment analysis — rare among news APIs:

Score Label Meaning
> 0.3 positiv 😊 Positive coverage
−0.3 to 0.3 neutral 😐 Neutral / factual coverage
< −0.3 negativ 😟 Critical / negative coverage

⚠️ Sentiment is only available for German (de) and English (en).


Safety, Limits & Responsible Use

Read-Only Operation

12 of the 15 tools carry readOnlyHint: true. All 9 monitoring tools (search, headlines, sentiment, briefing, article, sources, front_pages, trend, geo) are fully read-only and issue GET requests to WorldNewsAPI only. The 3 exceptions are local-only operations: news_alert_create and news_alert_delete (write/ delete ~/.news-monitor-mcp/alerts.json) and news_cache_clear (clears in-memory cache). None of the 15 tools modify any external data source.

API Rate Limits

Constraint WorldNewsAPI Free Tier Paid Plans
Quota 50 points/day 500 – 50,000 points/day
Articles/call Up to 100 Up to 100
Historical depth 30 days Extended
Timeout per call 30 seconds 30 seconds

Quotas checked against worldnewsapi.com/pricing on 2026-08-14. Undated, a quota is indistinguishable from a guess after a year. Two caveats worth knowing before you plan around this table:

  • The API bills in points, not calls. What a request costs depends on the endpoint and its options, so there is no fixed "calls per day" figure — the earlier claim of 1,000 calls/month was both the wrong unit and the wrong magnitude.
  • news_front_pages does not work on the free plan. Measured 2026-08-14: /retrieve-front-page answers HTTP 403 — This endpoint is not available on the free plan. The other 14 tools were reachable on the free key.
  • Articles per call is not plan-dependent. Measured 2026-08-14 on the free plan: number=50 returned 50 articles, number=100 returned 100, both HTTP 200 (available: 13037 in each case). Earlier versions of this README claimed a cap of 10 for the free plan — that was this server's own default (DEFAULT_RESULTS), mistaken for a limit of the source.

The TTL cache (v0.2+) reduces redundant calls by up to 80%.

Data Privacy

  • No personal data stored: The server holds no persistent user data. Cache entries are in-memory and reset on server restart.
  • No profiling: The server retrieves publicly published journalism only. It is not designed for surveillance or personal profiling.
  • Alert data: Alert configurations are stored locally in ~/.news-monitor-mcp/alerts.json — on your machine only, never transmitted.

Responsible Use

  • Query public news only — do not use as a profiling tool for individuals.
  • Sentiment scores reflect algorithmic analysis of journalistic tone, not verified editorial judgements.
  • Results depend on WorldNewsAPI's indexing; Swiss regional media may be less well-covered than national outlets.

Terms of Service

Users must comply with:

This MCP server is an independent open-source project and is not affiliated with WorldNewsAPI.


Synergies with Other MCP Servers

news-monitor-mcp can be combined with other servers in the portfolio:

Combination Use Case
+ fedlex-mcp Law meets discourse: legal framework + media coverage
+ global-education-mcp OECD stats + current media context
+ srgssr-mcp Swiss public media + international news comparison
+ swiss-environment-mcp Environmental data + media reporting
+ swiss-statistics-mcp BFS statistics + current media narrative
+ zurich-opendata-mcp City data + local media coverage

Changelog

See CHANGELOG.md


Contributing

See CONTRIBUTING.md (Deutsch).


Security & Compliance


License

MIT License — see LICENSE


Author

Hayal Oezkan · malkreide


Installation

Run via uv's uvx — no clone or manual install needed. Add to your MCP client config (mcpServers for Claude Desktop, Cursor and Windsurf; use a top-level servers key for VS Code in .vscode/mcp.json):

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

Metadata

Release files for news-monitor-mcp 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for news-monitor-mcp 0.4.0
File Size Uploaded
news_monitor_mcp-0.4.0.tar.gz 255.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for news-monitor-mcp 0.4.0
File Interpreter ABI Platform
news_monitor_mcp-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 308.6 kB

Release files / news_monitor_mcp-0.4.0.tar.gz

Download URL news_monitor_mcp-0.4.0.tar.gz
Size 255.8 kB
Tags Source
SHA-256 checksum
How to use checksums
510948c264c52c0856cac973f72a76c091605fb4431104e7c29073fefd4110b0
BLAKE2b-256 checksum
How to use checksums
2b983b8292f5d2ce2517472ab54e9879e4ce78bf22cef84f78138bd9712bc23b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / news_monitor_mcp-0.4.0-py3-none-any.whl

Download URL news_monitor_mcp-0.4.0-py3-none-any.whl
Size 52.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6fa25cf593a13393c59c809353a05cfb6f02999865421c0336cd906240f7c951
BLAKE2b-256 checksum
How to use checksums
7212e9d772fe5773090490a5a558ef0be59fd663e3510ffc4d502f4b059e8c01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page