{
  "markdown": "> 🇨🇭 **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**\n\n# 📰 news-monitor-mcp\n\n![Version](https://img.shields.io/badge/version-0.3.6-blue)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-purple)](https://modelcontextprotocol.io/)\n[![Data Source](https://img.shields.io/badge/Data-WorldNewsAPI-orange)](https://worldnewsapi.com/)\n![CI](https://github.com/malkreide/news-monitor-mcp/actions/workflows/ci.yml/badge.svg)\n\n> 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.\n\n[🇩🇪 Deutsche Version](README.de.md)\n\n---\n\n## Overview\n\n**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.\n\n**Source:** WorldNewsAPI (worldnewsapi.com) — the only freely available news API with German-language sentiment analysis.\n\n**API key required.** Get a free key at [worldnewsapi.com/console](https://worldnewsapi.com/console/) — free plan: 50 points/day, no credit card, backlink to worldnewsapi.com required (checked 2026-08-14).\n\n**Anchor demo query:**\n*\"How has the Schulamt Zürich been portrayed in the media over the last 30 days, and what is the overall sentiment?\"*\n\n---\n\n## Features\n\n- 🔍 **Full-text search** – 150+ countries, 50+ languages, Boolean queries and exact phrase matching\n- 📊 **Sentiment analysis** – German and English only (WorldNewsAPI unique feature); scores from −1 (negative) to +1 (positive)\n- 📰 **Top headlines** – clustered by country and language, ranked by number of sources reporting\n- 📋 **Media briefing** – multi-topic weekly report with sentiment overview for GL / leadership updates\n- 🗞️ **Newspaper front pages** – digital covers from 6,000+ publications in 125 countries\n- 📡 **Trend radar** – category-based trend detection (politics, technology, education, …) per country\n- 📍 **Geo-search** – location-specific news (Zürich, Bern, Basel, Kanton Zürich, …)\n- ☁️ **Dual transport** – stdio for Claude Desktop, Streamable HTTP for cloud deployment\n\n| # | Tool | Description |\n|---|---|---|\n| 1 | `news_search` | Full-text news search in 150+ countries |\n| 2 | `news_top_headlines` | Top headlines by country and language |\n| 3 | `news_sentiment_monitor` | Sentiment analysis for entity or topic |\n| 4 | `news_media_briefing` | Multi-topic weekly briefing report |\n| 5 | `news_retrieve_article` | Fetch full article by ID |\n| 6 | `news_search_sources` | Find available news sources by name/country |\n| 7 | `news_front_pages` | Digital newspaper front pages — **paid plan required**, see below |\n| 8 | `news_trend_radar` | Category-based trend detection per country |\n| 9 | `news_geo_search` | Location-specific news search |\n| 10 | `news_alert_create` | Create a persistent alert (sentiment / volume / keyword) |\n| 11 | `news_alert_list` | List configured alerts with status |\n| 12 | `news_alert_check` | Evaluate alerts against current data |\n| 13 | `news_alert_delete` | Permanently remove an alert |\n| 14 | `news_cache_stats` | Cache hit-rate and entries by type |\n| 15 | `news_cache_clear` | Clear cache (entirely or per tool type) |\n\n---\n\n## Demo\n\n![Media Briefing Demo](assets/demo-media-briefing.png)\n\n> *\"Create a media briefing for: AI in education, Volksschule Zürich, school digitalisation\"*\n\n---\n\n## Data Sources\n\n| Source | API Type | Content |\n|---|---|---|\n| **WorldNewsAPI** | REST JSON | 150+ countries, 50+ languages, full text, sentiment |\n\n---\n\n## Prerequisites\n\n- Python 3.11+\n- `uv` or `pip`\n- API key from [worldnewsapi.com/console](https://worldnewsapi.com/console/) (free tier available)\n\n---\n\n## Installation\n\n```bash\n# Recommended: uvx (no install step needed)\nuvx news-monitor-mcp\n\n# Alternative: pip\npip install news-monitor-mcp\n```\n\n---\n\n## Quickstart\n\n```bash\n# Start the server (stdio mode for Claude Desktop)\nWORLD_NEWS_API_KEY=your-key uvx news-monitor-mcp\n```\n\nTry it immediately in Claude Desktop:\n> *\"Show me the top news from Switzerland today\"*\n> *\"How is the Schulamt Zürich covered in German-language media this month?\"*\n> *\"Create a media briefing on: Volksschule Zürich, AI in education, school digitalisation\"*\n\n---\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `WORLD_NEWS_API_KEY` | – | **Required.** API key from worldnewsapi.com |\n| `MCP_TRANSPORT` | `stdio` | Transport: `stdio` or `streamable_http` |\n| `MCP_HOST` | `127.0.0.1` | HTTP bind host. Use `0.0.0.0` only inside a container. |\n| `MCP_PORT` | `8000` | Port for HTTP transport |\n| `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))\"`. |\n| `MCP_ALLOWED_ORIGINS` | – | Optional CSV allowlist for the `Origin` header (DNS-rebinding protection). Example: `https://claude.ai`. |\n| `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. |\n| `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`. |\n| `NEWS_MONITOR_ALERTS_FILE` | – | *(Back-compat)* explicit path to the alerts file. Same symlink check applies. Prefer `NEWS_MONITOR_ALERTS_DIR`. |\n| `MCP_ALERT_RETENTION_DAYS` | `90` | Alerts older than this many days are deleted on server start (Privacy default per [`docs/privacy-dsg.md`](docs/privacy-dsg.md)). Set to `0` to disable retention. |\n| `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). |\n| `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`). |\n\n### Claude Desktop Configuration\n\n```json\n{\n  \"mcpServers\": {\n    \"news-monitor\": {\n      \"command\": \"uvx\",\n      \"args\": [\"news-monitor-mcp\"],\n      \"env\": {\n        \"WORLD_NEWS_API_KEY\": \"your-api-key-here\"\n      }\n    }\n  }\n}\n```\n\n**Config file locations:**\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\nAfter restarting Claude Desktop, all tools are available. Example queries:\n- \"Show me the top Swiss news today\"\n- \"What is the media sentiment on AI in education this month?\"\n- \"Create a weekly briefing for: Schulamt Zürich, Volksschule, KI Bildung\"\n- \"Find all German-language articles about school digitalisation in the last 14 days\"\n- \"Show me the front pages of Swiss newspapers today\"\n\n### Cloud Deployment (Streamable HTTP)\n\nFor use via **claude.ai in the browser** (e.g. on managed workstations without local software):\n\n**Authentication is mandatory.** The HTTP transport refuses any request without a valid `Authorization: Bearer <token>` header. Generate a token once and keep it secret:\n\n```bash\npython -c \"import secrets; print(secrets.token_urlsafe(32))\"\n```\n\n**Render.com (recommended):**\n1. Push/fork the repository to GitHub\n2. On [render.com](https://render.com): New Web Service → connect GitHub repo\n3. Set the following environment variables in the Render dashboard:\n   - `WORLD_NEWS_API_KEY` — your WorldNewsAPI key\n   - `MCP_BEARER_TOKEN` — the token generated above\n   - `MCP_HOST=0.0.0.0` — bind on all interfaces inside the container\n   - `MCP_ALLOWED_ORIGINS=https://claude.ai` *(optional, recommended)*\n4. 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.\n\n```bash\n# Local HTTP mode (binds 127.0.0.1 by default)\nWORLD_NEWS_API_KEY=your-key \\\n  MCP_BEARER_TOKEN=$(python -c \"import secrets; print(secrets.token_urlsafe(32))\") \\\n  news-monitor-mcp --http --port 8000\n\n# Verify auth is enforced\ncurl -i http://127.0.0.1:8000/mcp                                  # → 401\ncurl -i -H \"Authorization: Bearer $MCP_BEARER_TOKEN\" http://127.0.0.1:8000/mcp\n```\n\n### Scaling notes\n\nThis server is currently **single-process / single-replica**:\n\n- 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.\n- 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`](audits/2026-05-13-news-monitor-mcp/findings/SCALE-stateful-singletons.md).\n- 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.\n\nThe `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.\n\n### Container image\n\nA 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.\n\n```bash\ndocker build -t news-monitor-mcp .\n\ndocker run --rm -p 8000:8000 \\\n  -e WORLD_NEWS_API_KEY=your-key \\\n  -e MCP_BEARER_TOKEN=$(python -c \"import secrets; print(secrets.token_urlsafe(32))\") \\\n  -e MCP_ALLOWED_ORIGINS=https://claude.ai \\\n  -v news-monitor-data:/data \\\n  news-monitor-mcp\n```\n\n---\n\n## Architecture\n\n```\n┌─────────────────┐    ┌──────────────────────────┐    ┌──────────────────────────┐\n│  Claude / AI    │────▶│   News Monitor MCP        │────▶│   WorldNewsAPI           │\n│  (MCP Host)     │◀────│   (MCP Server)            │◀────│   REST JSON API          │\n└─────────────────┘    │                            │    │   150+ countries         │\n                       │  9 Tools                   │    │   50+ languages          │\n                       │  Stdio | Streamable HTTP   │    │   Sentiment DE/EN        │\n                       └──────────────────────────┘    └──────────────────────────┘\n```\n\n---\n\n## Project Structure\n\n```\nnews-monitor-mcp/\n├── src/\n│   └── news_monitor_mcp/\n│       ├── __init__.py\n│       └── server.py          # All 9 tools\n├── tests/\n│   ├── __init__.py\n│   └── test_server.py         # 20 tests (unit + live)\n├── pyproject.toml\n├── CHANGELOG.md\n├── CONTRIBUTING.md\n├── LICENSE\n├── README.md                  # This file (English)\n└── README.de.md               # German version\n```\n\n---\n\n## MCP Protocol Version\n\nThis server speaks **two protocol eras** over the same endpoint. The client's\nfirst request on a connection decides which one applies; a later claim from the\nother era is refused.\n\n| Era | Revision | Who reaches it |\n|---|---|---|\n| `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. |\n| Per-request envelope | **`2026-07-28`** | A request carrying the `2026-07-28` `_meta` envelope opens a modern connection. |\n\nBoth revisions are pinned in\n[`tests/test_protocol_version.py`](tests/test_protocol_version.py) and asserted\nagainst the installed SDK, so a Dependabot bump of `mcp` cannot move either one\nsilently. The handshake ceiling is measured against a live `initialize` through\nthe assembled ASGI stack, not read off a constant name.\n\nNote that the SDK's `LATEST_PROTOCOL_VERSION` is an alias for the **modern**\nera, not for the handshake era — pinning against it alone would leave the era\nthat current clients actually negotiate free to drift.\n\n**Update policy.** When the gate fails, do not edit the constant blindly: read\nthe spec changelog between the two revisions, verify the server still behaves,\nthen move the constant, this section, `README.de.md` and\n[`CHANGELOG.md`](CHANGELOG.md) together.\n\n---\n\n## Testing\n\n```bash\n# Unit + contract tests (no network) — this is what CI runs\nPYTHONPATH=src pytest tests/ -m \"not live\"\n\n# Live tests. The route check needs no key; the data tests skip without one.\nPYTHONPATH=src pytest tests/ -m \"live\"\n\n# Re-record the route inventory (writes tests/fixtures/ + PROVENANCE.md)\nPYTHONPATH=src python scripts/record_fixtures.py\n```\n\n**181 tests** — 174 offline, 7 live (2 of which need no API key).\n\nThe live tests run daily at 06:17 UTC via\n[`.github/workflows/live-tests.yml`](.github/workflows/live-tests.yml), not on\npush: they measure the source, which changes independently of this repo.\n\n### Three live tests never ran\n\nUntil 2026-08-08 the repo's three live tests carried `@pytest.mark.live` but no\n`@pytest.mark.asyncio`. Under pytest-asyncio's strict default that does not mean\n\"skipped\" — it means `async def functions are not natively supported`. They\nnever executed, and anyone running `-m live` got three errors that said nothing\nabout the source. CI excludes `-m live`, so nothing reported it.\n\n`asyncio_mode = \"auto\"` now makes a forgotten marker unable to cause this.\n\nAnd running would not have shown much either:\n\n```python\nassert \"Volksschule\" in result or \"Ergebnisse\" in result\n```\n\nThe second branch matches the tool's own results heading, so the disjunction\ncould not fail. `assert \"Top-Schlagzeilen\" in result` and `assert \"Sentiment\" in\nresult` likewise matched only the template. All three now assert something that\ncan fail, and they **skip** rather than fail when no key is set — \"red\" should\nmean something is wrong, not that you have no key.\n\n### What is verified without a key, and what is not\n\n`tests/fixtures/api_routen.json` records, for each of the five paths the tools\nbuild, the status code and content type measured without a key. The gateway\nroutes **before** authenticating:\n\n| Path | Response |\n|---|---|\n| the five paths the server builds | 401, `application/json` |\n| a freely invented path (control) | 404, `text/html` |\n\nSo a 401 means \"this route exists\". Without the control it would only mean \"I\ngot a 401\" — and that is not a given: `epl.bag.admin.ch` elsewhere in this\nportfolio answers 401 for invented paths too. The recorder therefore re-measures\nthe control on every run and aborts if it stops discriminating.\n\n**Still open, and marked as such in `PROVENANCE.md`:** whether the query\nparameter names the server sends are correct. The API answers 401 regardless of\nparameters, so no key means no verification. In `global-education-mcp` in this\nsame portfolio exactly that went wrong — two filters were silently inert\nbecause unknown parameters were answered with HTTP 200 and dropped. That check\nis outstanding, not done.\n\n### Empty result vs. changed response shape\n\n`data.get(\"news\", [])` answers two entirely different cases the same way: \"the\nsource found nothing\" and \"the source answers differently than we assume\". The\nsecond becomes \"0 results\" — complete, plausible, formatted and wrong. That is\nnot hypothetical: in `global-education-mcp` the envelope had been renamed, so\n**every** answer came back empty while 128 tests stayed green.\n\n`articles_of()` now reads the envelope. An empty `news` stays an empty list — a\nstatement by the source. A *missing* `news` is not a statement about the news\nbut about the response, and is reported as such.\n\n---\n\n## Example Use Cases\n\n### Schulamt / Institutional Communication\n```\n\"How has the Schulamt Zürich been portrayed in media over the last 30 days?\"\n→ news_sentiment_monitor(entity=\"Schulamt Zürich\", language=\"de\", days_back=30)\n\n\"Create a weekly media briefing for leadership\"\n→ news_media_briefing(topics=[\"Volksschule Zürich\", \"KI Bildung\", \"Schuldigitalisierung\"])\n\n\"What are Swiss media reporting on school digitalisation?\"\n→ news_search(query=\"Schuldigitalisierung\", language=\"de\", source_country=\"ch\")\n```\n\n### KI-Fachgruppe / AI Working Group\n```\n\"What are the current tech trends in Swiss press this week?\"\n→ news_trend_radar(category=\"technology\", source_country=\"ch\", language=\"de\")\n\n\"How are AI developments in education covered internationally?\"\n→ news_search(query=\"AI education classroom\", language=\"en\", number=20)\n\n\"Compare Swiss and German media coverage of AI regulation\"\n→ news_search(query=\"KI Regulierung\", source_country=\"ch\", language=\"de\")\n→ news_search(query=\"KI Regulierung\", source_country=\"de\", language=\"de\")\n```\n\n### City Administration / Location Research\n```\n\"What is being reported about Zürich school infrastructure?\"\n→ news_geo_search(location=\"Zürich\", query=\"Schule\")\n\n\"Show today's front pages of Swiss newspapers\"\n→ news_front_pages(source_country=\"ch\")\n```\n\n→ [More use cases by audience](EXAMPLES.md) →\n\n---\n\n## Sentiment Analysis\n\nWorldNewsAPI offers German-language sentiment analysis — rare among news APIs:\n\n| Score | Label | Meaning |\n|---|---|---|\n| > 0.3 | positiv 😊 | Positive coverage |\n| −0.3 to 0.3 | neutral 😐 | Neutral / factual coverage |\n| < −0.3 | negativ 😟 | Critical / negative coverage |\n\n⚠️ **Sentiment is only available for German (`de`) and English (`en`).**\n\n---\n\n## Safety, Limits & Responsible Use\n\n### Read-Only Operation\n12 of the 15 tools carry `readOnlyHint: true`. All 9 monitoring tools (search,\nheadlines, sentiment, briefing, article, sources, front_pages, trend, geo) are\nfully read-only and issue GET requests to WorldNewsAPI only. The 3 exceptions\nare local-only operations: `news_alert_create` and `news_alert_delete` (write/\ndelete `~/.news-monitor-mcp/alerts.json`) and `news_cache_clear` (clears\nin-memory cache). None of the 15 tools modify any external data source.\n\n### API Rate Limits\n\n| Constraint | WorldNewsAPI Free Tier | Paid Plans |\n|---|---|---|\n| Quota | 50 points/day | 500 – 50,000 points/day |\n| Articles/call | Up to 100 | Up to 100 |\n| Historical depth | 30 days | Extended |\n| Timeout per call | 30 seconds | 30 seconds |\n\nQuotas checked against [worldnewsapi.com/pricing](https://worldnewsapi.com/pricing/)\non **2026-08-14**. Undated, a quota is indistinguishable from a guess after a\nyear. Two caveats worth knowing before you plan around this table:\n\n- The API bills in **points, not calls.** What a request costs depends on the\n  endpoint and its options, so there is no fixed \"calls per day\" figure — the\n  earlier claim of 1,000 calls/month was both the wrong unit and the wrong\n  magnitude.\n- `news_front_pages` does **not work on the free plan.** Measured 2026-08-14:\n  `/retrieve-front-page` answers `HTTP 403 — This endpoint is not available on\n  the free plan.` The other 14 tools were reachable on the free key.\n- Articles per call is **not plan-dependent**. Measured 2026-08-14 on the free\n  plan: `number=50` returned 50 articles, `number=100` returned 100, both\n  HTTP 200 (`available: 13037` in each case). Earlier versions of this README\n  claimed a cap of 10 for the free plan — that was this server's own default\n  (`DEFAULT_RESULTS`), mistaken for a limit of the source.\n\nThe TTL cache (v0.2+) reduces redundant calls by up to 80%.\n\n### Data Privacy\n\n- **No personal data stored:** The server holds no persistent user data. Cache entries are in-memory and reset on server restart.\n- **No profiling:** The server retrieves publicly published journalism only. It is not designed for surveillance or personal profiling.\n- **Alert data:** Alert configurations are stored locally in `~/.news-monitor-mcp/alerts.json` — on your machine only, never transmitted.\n\n### Responsible Use\n\n- Query public news only — do not use as a profiling tool for individuals.\n- Sentiment scores reflect algorithmic analysis of journalistic tone, not verified editorial judgements.\n- Results depend on WorldNewsAPI's indexing; Swiss regional media may be less well-covered than national outlets.\n\n### Terms of Service\n\nUsers must comply with:\n- [WorldNewsAPI Terms of Service](https://worldnewsapi.com/terms-of-service/)\n- [WorldNewsAPI Privacy Policy](https://worldnewsapi.com/privacy-policy/)\n\nThis MCP server is an independent open-source project and is not affiliated with WorldNewsAPI.\n\n---\n\n## Synergies with Other MCP Servers\n\n`news-monitor-mcp` can be combined with other servers in the portfolio:\n\n| Combination | Use Case |\n|---|---|\n| `+ fedlex-mcp` | Law meets discourse: legal framework + media coverage |\n| `+ global-education-mcp` | OECD stats + current media context |\n| `+ srgssr-mcp` | Swiss public media + international news comparison |\n| `+ swiss-environment-mcp` | Environmental data + media reporting |\n| `+ swiss-statistics-mcp` | BFS statistics + current media narrative |\n| `+ zurich-opendata-mcp` | City data + local media coverage |\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) ([Deutsch](CONTRIBUTING.de.md)).\n\n---\n\n## Security & Compliance\n\n- Report vulnerabilities privately: see [SECURITY.md](SECURITY.md)\n- Swiss public-sector deployment: see [`docs/isds-klassifikation.md`](docs/isds-klassifikation.md) for the ISDS / Schutzbedarfsfeststellung\n- Swiss data protection (revDSG) — duties, profiling, retention, drittlandtransfer: [`docs/privacy-dsg.md`](docs/privacy-dsg.md)\n- Audit history: [`audits/`](audits/)\n\n---\n\n## License\n\nMIT License — see [LICENSE](LICENSE)\n\n---\n\n## Author\n\nHayal Oezkan · [malkreide](https://github.com/malkreide)\n\n---\n\n## Credits & Related Projects\n\n- **Data:** [WorldNewsAPI](https://worldnewsapi.com/) – global news data with sentiment analysis\n- **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) – Anthropic / Linux Foundation\n- **Related:** [swiss-culture-mcp](https://github.com/malkreide/swiss-culture-mcp) – MCP server for Swiss cultural heritage data\n- **Related:** [srgssr-mcp](https://github.com/malkreide/srgssr-mcp) – MCP server for SRG SSR Swiss public media\n- **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide)\n\n<!-- mcp-name: io.github.malkreide/news-monitor-mcp -->\n\n<!-- BEGIN GENERATED: install -->\n## Installation\n\nRun via [`uv`](https://docs.astral.sh/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`):\n\n```json\n{\n  \"mcpServers\": {\n    \"news-monitor-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"news-monitor-mcp\"\n      ]\n    }\n  }\n}\n```\n<!-- END GENERATED: install -->\n",
  "bytes": 23293,
  "sha": "a5da45c9f6d5a891a8976294a5988157d261078be989e8084451ba9ccc8e25f5",
  "repo_slug": "malkreide/news-monitor-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_malkreide_news_monitor_mcp_b7a7d714/readme"
}