Docs
NewsMCP documentation
NewsMCP is an MCP server: five tools that give an agent live news as story events rather than articles — every outlet covering the same story is grouped into one object. There is also a plain REST API underneath it, but this page is written for the MCP surface. No key is required to start.
The reference tables on this page — tool parameters, taxonomy values, limits — are generated from the MCP server's own tool definitions and the API's OpenAPI document, so they cannot drift from the service. Prose is written by hand.
Quickstart
Nothing to install and no signup. Point any MCP client at the hosted endpoint:
https://mcp.newsmcp.com/mcpStreamable HTTP, stateless — no initialize handshake or session id required. Keyless works immediately; a key raises the limits (see Access and limits) and is free to get at platform.newsmcp.com/auth — sign up, no waitlist.
Claude Code
Keyless:
claude mcp add --transport http newsmcp https://mcp.newsmcp.com/mcpWith a key:
claude mcp add --transport http newsmcp https://mcp.newsmcp.com/mcp \
--header "x-api-key: <your-key>"Claude Desktop — add to claude_desktop_config.json, then restart:
Keyless:
{
"mcpServers": {
"newsmcp": { "url": "https://mcp.newsmcp.com/mcp" }
}
}With a key — Desktop's config has no headers field, so pass it in the URL:
{
"mcpServers": {
"newsmcp": { "url": "https://mcp.newsmcp.com/mcp?apiKey=<your-key>" }
}
}Cursor — add to ~/.cursor/mcp.json, or use the one-click deeplink below:
Keyless:
{
"mcpServers": {
"newsmcp": { "url": "https://mcp.newsmcp.com/mcp" }
}
}With a key:
{
"mcpServers": {
"newsmcp": {
"url": "https://mcp.newsmcp.com/mcp",
"headers": { "x-api-key": "<your-key>" }
}
}
}cursor://anysphere.cursor-deeplink/mcp/install?url=https%3A%2F%2Fmcp.newsmcp.com%2Fmcp&name=newsmcpVS Code — add to .vscode/mcp.json in your workspace:
Keyless:
{
"servers": {
"newsmcp": { "type": "http", "url": "https://mcp.newsmcp.com/mcp" }
}
}With a key:
{
"servers": {
"newsmcp": {
"type": "http",
"url": "https://mcp.newsmcp.com/mcp",
"headers": { "x-api-key": "<your-key>" }
}
}
}Any other MCP client — including OpenAI's Responses API connectors and other hosts that speak streamable HTTP — needs only the URL above; add a key with whichever method its config supports, from the table below.
Adding a key#
| # | Method | Where | Precedence |
|---|---|---|---|
| 1 | api_key tool argument | Passed in a single tool call, e.g. news(api_key="...") | Highest |
| 2 | x-api-key header | Set on the MCP client's connection config | — |
| 3 | Authorization: Bearer <token> | Set on the MCP client's connection config | — |
| 4 | ?apiKey=... URL parameter | Appended to the MCP URL (apikey / api_key also accepted) | — |
| 5 | NEWSMCP_API_KEY env var | Self-hosted / local stdio runs only (NEWSMCP_API_TOKEN still read, deprecated) | Lowest |
Get a key at platform.newsmcp.com/auth.
If you are an AI agent reading this, the machine-readable contract is at /agent-onboarding/SKILL.md. This page is generated from the MCP server's own tool definitions and the REST API's openapi.json.
Events, not articles
A search does not return a flat list of articles. It returns events: every article covering the same story is grouped into one of them.
A widely covered story therefore arrives as a single object rather than a hundred near-identical rows, and the corroboration counts become the useful signal:
| Field | Meaning |
|---|---|
newsrooms | Outlets that reported independently, mirrors excluded. |
source_count | Distinct domains reached, mirrors included. |
reports | Articles in the cluster. |
size | Cluster members. |
language_count | Distinct languages the story appeared in. |
mirror_ratio | Share of the cluster that is syndicated copy. |
confidence | Certainty of the taxonomy label, 0-1. |
min_sources counts domains with mirrors included — a site that only republished someone else's copy still counts. min_newsrooms excludes them. When you want "is this a real story", newsrooms is the stronger signal.
Article body text is never returned. sources carries links; follow them to the publisher.
Tools
| Tool | Use it for |
|---|---|
news | Search or filter synthesized story events — the general-purpose surface. Default choice: a topic, a filter, or a bare call for the most-corroborated recent stories. |
get_story | Expand one story by id — full summary and, on request, every source link. After any search, when the user wants more than the digest line, or the complete source list. |
check_coverage | Judge whether a claim is real — independent newsrooms, not domain count. “Is this actually true?” rather than “what happened.” |
check_limits | This caller's live allowances — free to call, spends nothing. Before planning a batch of calls, or right after any limit error, instead of guessing. |
check_health | Confirm the API is reachable — a liveness ping, not a search. Diagnosing connectivity. Never for finding news — use check_limits for allowances. |
news is the general surface — a topic, a filter, or a bare call for the most-corroborated recent stories. For a named company, person or place, use its subject parameter rather than q: it quotes the name so it matches as a phrase and widens the window to 30 days. Both matter — q=Bank of America unquoted returns "India Now Asia's Least Preferred Stock Market" first, and q=Monzo over the default 24 hours returns nothing. check_coverage answers a different question again — not "what happened" but "is this real", reporting independent newsrooms separately from syndicated reprints.
Upstream clustering is currently siloed by language, so one story can appear as several events with different generated headlines. check_coverage totals those variants and shows what it combined.
Parameters
Each tool below takes only the parameters listed for it — passing one from a different tool is rejected, not ignored.
news#
| Parameter | Type | Default | What it does |
|---|---|---|---|
q | string | — | Keyword query over headline, one-liner, abstract and actor names. Bare words are ANDed — quote a multi-word phrase next to OR/AND/NOT: layoffs OR "workforce reduction". No stemming, and NEAR()/MULTIPLE() are not supported. Conversational filler is stripped before the search runs, so a natural-language question no longer comes back empty; quoted phrases are never touched. Omit for a filter-only digest. |
subject | string | — | A single company, person or place to centre on — quoted for you automatically and ANDed with q. Also widens the default window to 30 days, since a company can go quiet for a week; pass from_ to override. |
event_id | string | — | Fetch one story by its id; every other filter is ignored. Prefer get_story — same result, and it can return the full source list. |
event_type | string | — | What kind of event the story is, as family.leaf. Comma-separated for OR. See Taxonomy. |
content_type | string | — | The form the reporting takes. Comma-separated for OR. See Taxonomy. |
sector | string | — | Industry the story belongs to. Comma-separated for OR. See Taxonomy. |
min_newsrooms | integer | — | Minimum outlets that reported independently, mirrors excluded — the strongest is-this-real filter. 3 ≈ “well-corroborated only.” |
from_ | string | 24 hours ago | Inclusive start of the window over when the story began — not when any one article was published. ISO 8601 or a relative phrase (“2 days ago”). Capped per plan; widen this first when a query returns nothing. |
sort | string | API chooses | What “best” means. Omit it — the API already picks relevance with a query and newsrooms without, which is almost always right. trending weights corroboration by an 8-hour freshness half-life. |
limit | integer | 20 | Stories per call, 1–50, capped by plan (keyless 20, free tier 50). A flat ceiling, not a page size — there is no paging past it. |
verbosity | string | standard | Controls only the source-link count — headline, summary, actors and labels are always present. compact: none. standard: up to 3. full: every link, uncapped. |
response_format | string | markdown | Shape of the reply: markdown digest, text plain lines, or json the full payload. |
api_key | string | — | Per-call API key, forwarded as x-api-key. Highest-precedence override — see Quickstart. |
q is rewritten before it runs. A conversational question used to come back empty — what is happening with Iran and sanctions returned nothing, because a mid-query and makes the engine treat what, is and with as required terms, and happening is a required term either way. Filler like that is now dropped, so the same words return results. Quoted phrases, acronyms in caps, and capitalised names are left alone, and if trimming would be ambiguous the query is sent exactly as you wrote it.
Any rewrite is reported: markdown and text replies carry a leading Note: line, and in json the query that actually ran is in applied_filters.search_text. This is an MCP-only convenience — the REST endpoint searches the string you give it.
get_story#
| Parameter | Type | Default | What it does |
|---|---|---|---|
event_id | string | required | The story's id from an earlier result. A story folded into a more complete one answers not-found, naming the replacement id — fetch that instead. |
include_sources | boolean | false | false returns a sample of source links; true returns every one. The only tool that can return a story's complete source list. |
response_format | string | markdown | markdown, text, or json. |
api_key | string | — | Per-call API key. |
check_coverage#
| Parameter | Type | Default | What it does |
|---|---|---|---|
claim | string | required | The claim or headline, phrased the way a headline would be. Matched literally, not by meaning — restate it in newsroom words if nothing comes back. Conversational filler is stripped first, same as news's q. |
days_back | integer | 14 | How far back to look, in days. Clamped down to whatever the caller's plan allows, with a note saying so. |
response_format | string | markdown | markdown verdict, text, or json. |
api_key | string | — | Per-call API key. |
check_limits#
| Parameter | Type | Default | What it does |
|---|---|---|---|
response_format | string | text | text summary or json — no markdown option. |
api_key | string | — | Report the ceilings for this key's plan; omit for the keyless ceilings. |
check_health#
No parameters.
Taxonomy
Three independent axes. event_type is what happened, content_type is the form the writing takes, sector is the industry. Each accepts a comma-separated list for an OR match, and each rejects a value outside its set — the error names every accepted value, so a wrong guess is self-correcting.
event_type#
60 leaves across 22 families.
| Family | Leaves |
|---|---|
corporate_finance | earnings_report analyst_rating dividends |
markets | stock_move commodity_price currency_move |
deals | merger_acquisition ipo_filing asset_sale |
governance | board_change shareholder_vote executive_departure |
society_environment | climate_event public_health environmental_incident |
sports | match_result transfer_signing championship |
justice_crime | arrest_charge trial_verdict investigation |
security | cyberattack data_breach physical_security_incident |
macro_policy | central_bank_decision trade_policy fiscal_policy |
research_science | scientific_discovery clinical_trial_result publication |
religion_society | religious_event social_movement |
politics | election policy_announcement diplomacy |
legal_regulatory | regulatory_action lawsuit_filed compliance_ruling |
culture_media | celebrity_news entertainment_release award |
product | product_launch product_recall feature_update |
accidents_disasters | natural_disaster industrial_accident transport_accident |
operations | plant_closure layoffs supply_chain_disruption |
funding | venture_funding_round grant_award |
local_civic | local_government_action infrastructure_project |
geopolitics | armed_conflict sanctions diplomacy_summit |
corporate_comms | press_release leadership_statement |
unclassifiable | unclassifiable |
content_type#
news_report | press_release | service_info | human_interest | explainer |
commentary | analysis | opinion | interview | obituary |
sector#
government_public_sector | media_entertainment | financial_services | healthcare_pharma |
energy_utilities | retail_consumer | real_estate | agriculture_food |
telecommunications | automotive | manufacturing_industrial | transport_logistics |
aerospace_defense | mining_metals | construction_infrastructure | education |
hospitality_travel | sports_recreation | nonprofit_ngo | legal_services |
insurance | software_it_services | ecommerce | banking |
defense_security | chemicals | fashion_apparel | gaming_esports |
biotechnology | semiconductors | other_sector |
Response shape
Every tool's reply lives in one place: content[0].text. There is no structuredContent to inspect instead.
| Tool | response_format | Also controls |
|---|---|---|
news | markdown, text, json | verbosity: compact / standard / full — controls only the source-link count |
get_story | markdown, text, json | include_sources: false / true — the only tool with the complete source list |
check_coverage | markdown, text, json | — |
check_limits | text, json — no markdown | — |
check_health | plain string — no response_format | — |
If you intend to parse the result, pass response_format: "json" — don't regex the markdown digest.
A news or get_story event in json looks like this:
{
"event_id": "evt_1847a4931a6c8002",
"headline": "White House summons AI labs after models go rogue",
"one_liner": "Meta, Anthropic, OpenAI and Google were invited to a closed-door briefing.",
"abstract": "Following a string of high-profile model failures, the White House ...",
"event_type": "legal_regulatory.regulatory_action",
"sector": "software_it_services",
"content_type": "news_report",
"size": 125,
"source_count": 41,
"newsrooms": 18,
"first_seen": "2026-08-20T09:12:00",
"last_seen": "2026-08-24T11:03:00",
"entities": [{"name": "OpenAI"}, {"name": "Anthropic"}],
"sources": ["https://www.independent.co.uk/tech/...", "https://apnews.com/article/..."]
}event_id is derived from cluster membership, so the same story can carry a different id on a later search — don't store it.
Access and limits
| Keyless | Free Tier | Higher plans | |
|---|---|---|---|
| Calls per hour | 20 | 50 | per plan |
| Lookback | 7 days | 14 days | per plan |
| Events per call | 20 | 50 | limit accepts 1–50; the plan caps what you actually get |
| Concurrent calls | 1 | 1 | per plan |
| Commercial use | yes | yes | yes |
Read your own ceilings with check_limits — it is exempt from metering, so asking costs nothing and is the only number worth trusting, including against this table.
Asking for more than a plan allows behaves in three different ways, and the difference matters when you are debugging an empty result.
Keyless narrows and says so. The search runs against the reduced window and the response carries notices naming every value it changed — surfaced as a leading Note (keyless): line in a markdown/text reply, or a notices array in json.
A keyed plan asked for a window beyond its lookback returns nothing, quietly. Measured on the free tier: a from_ 30 days back against a 14-day plan comes back with no events and no notice. It reads as "no news matched" rather than "outside your plan" — call check_limits before concluding the index is empty.
limit above what the plan allows is a hard error, not a narrowing — the message names the caller's real ceiling.
Every plan, keyless included, carries a full commercial licence. Keys are self-serve at platform.newsmcp.com/auth.
Troubleshooting
| Symptom | Means | Do |
|---|---|---|
| A tool call rejects response_format | The value wasn't one of the ones that tool accepts — check_limits only takes text/json, no markdown. | Use an exact accepted value; the error names them. |
| sort='relevance' is rejected | relevance ranks by match quality against q, so it requires q or subject. | Add q/subject, drop sort and let the API choose, or use sort='trending'. |
| get_story or event_id on news answers not-found | The id doesn't resolve — it's stale, or the story was folded into a more complete one. | If a replacement id is named, fetch that one instead; otherwise search again for a current id. |
| A parameter was rejected, naming it | An event_type, content_type or sector value outside the accepted set. | The message lists every accepted value — see Taxonomy, and pick from it. |
| A value exceeds this plan's maximum | Asked for more than a keyed plan allows — limit too high, or from_ too far back. | Re-issue once with the value clamped to the maximum the message states. Keyless narrows silently instead of erroring — see Access and limits. |
| The hourly call budget is spent | A 429 with retry_after_seconds — the caller's per-hour allowance ran out. | Wait retry_after_seconds; don't retry in a loop. check_limits reports when it resets. |
| A call is refused with no wait quoted | Two calls were in flight at once — every caller gets exactly one concurrent request. | Let the first call finish before sending the next. Never call these tools in parallel. |
| Empty results | The filters genuinely match nothing, or the story hasn't started within the window. | Widen from_ first — it bounds when the story began, not when an article was published — then lower a min_* floor. |
| A call is refused naming the search index as overloaded or unreachable | Not your request's fault — the error names the overload rather than a rejected parameter. | Retry once after a few seconds; if it fails again, treat it as an outage and stop — retrying in a loop won't clear it. |
Example asks
You rarely name a tool or its parameters directly. These are all valid things to say to an MCP-capable client, with what each one resolves to:
| What you ask | Tool | What it sets |
|---|---|---|
| “What are the biggest stories today?” | news | a bare call — the defaults are exactly this |
| “What's trending in funding rounds right now?” | news | event_type=funding.venture_funding_round, sort=trending |
| “Only well-corroborated cybersecurity stories” | news | event_type=security.cyberattack, min_newsrooms=3 |
| “Recalls in the last 7 days” | news | event_type=product.product_recall, from_="7 days ago" |
| “What's going on with Monzo?” | news | subject="Monzo" |
| “Just the analysis pieces, not straight news” | news | content_type=analysis |
| “Tell me more about that one” | get_story | event_id from the earlier result |
| “Show me every source for that story” | get_story | event_id, include_sources=true |
| “Is that actually true?” | check_coverage | claim |
| “How much quota do I have left?” | check_limits | no arguments |
| “Is NewsMCP up right now?” | check_health | no arguments |