The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the News Monitor MCP listing page.
🇨🇭 Part of the Swiss Public Data MCP Portfolio
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.
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?"
| # | 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) |

"Create a media briefing for: AI in education, Volksschule Zürich, school digitalisation"
| Source | API Type | Content |
|---|---|---|
| WorldNewsAPI | REST JSON | 150+ countries, 50+ languages, full text, sentiment |
uv or pipTry 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"
| 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). |
Config file locations:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonAfter restarting Claude Desktop, all tools are available. Example queries:
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:
Render.com (recommended):
WORLD_NEWS_API_KEY — your WorldNewsAPI keyMCP_BEARER_TOKEN — the token generated aboveMCP_HOST=0.0.0.0 — bind on all interfaces inside the containerMCP_ALLOWED_ORIGINS=https://claude.ai (optional, recommended)https://your-app.onrender.com/mcp and configure the Bearer token as the auth header.This server is currently single-process / single-replica:
NewsCache). If you run multiple Render or Kubernetes replicas, each replica has its own cache — hit-rates drop linearly with the replica count.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./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.
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.
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.
181 tests — 174 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.
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:
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.
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.
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.
→ More use cases by audience →
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).
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.
| 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:
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.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%.
~/.news-monitor-mcp/alerts.json — on your machine only, never transmitted.Users must comply with:
This MCP server is an independent open-source project and is not affiliated with WorldNewsAPI.
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 |
See CHANGELOG.md
See CONTRIBUTING.md (Deutsch).
docs/isds-klassifikation.md for the ISDS / Schutzbedarfsfeststellungdocs/privacy-dsg.mdaudits/MIT License — see LICENSE
Hayal Oezkan · malkreide
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):