Model Context Protocol

A news MCP server, built by the people who run the index

Eighteen tools covering search, story grouping and expansion, headlines, article lookup, related-article discovery, entity resolution and relationships, time series and watches. Same filters and same authentication as the REST API, so behaviour does not diverge between the path your backend takes and the path an agent takes.

What changed on chip export controls this week?

Tool call list_stories q: "chip export controls", mode: "hybrid", from: "2026-09-08"
Result 12 stories · top: 34 outlets, 3 languages · credits_charged: 1

Twelve distinct stories. The largest, on export licence rules for lithography tools, ran in 34 outlets across English, German and Japanese, first seen at 07:31.

An illustration of an agent calling the server, not a recorded session.

Why this is worth having as a first-party thing

Search the web for a news MCP server today and you will find community wrappers around other vendors' REST APIs — GitHub projects, a package on PyPI, a tutorial or two. Several are well made. All of them share the same structural problem: they are a translation layer maintained by someone who does not control either side of it. When the upstream API adds a filter, the wrapper does not have it. When the wrapper's author moves on, you inherit it.

A first-party server does not have that seam. The tool definitions are generated from the same query surface the REST API exposes, so a filter that exists in one exists in the other, and a query you debugged with curl behaves identically when a model issues it. There is no version of this where the tool layer drifts from the API it describes, because there is no separate tool layer to drift.

agent Claude · ChatGPT list_stories 8 events MCP 18 tools same key as REST index grouped
One tool call, one round trip, grouped results back.

The tools

ToolWhat it doesWhen to reach for it
search_news Search articles by keyword, meaning or both, with time-range, language and outlet filters. The default tool. Hybrid mode is usually the right choice when a model wrote the query, because models paraphrase.
list_stories The same search, collapsed so one event returns once with the outlets that covered it. Use this whenever results go into a context window. It is the single biggest token saving available here.
get_story Expand one story: every article, every outlet, first and last seen, in one call. For following up on a story from list_stories without re-running a search.
top_headlines The most recent articles, optionally narrowed to a language or an outlet. For "what is happening right now" questions, where recency matters more than relevance.
get_article Fetch one article record by id. For following up on a specific result the model has already seen, without re-running a search.
find_related Find articles similar to a given one. For "what else has been written about this" without the model having to invent a new query.
resolve_entity Find the spellings the index holds for an organization, person, place or topic, grouped with counts. Filters are exact matches; resolve the name first so a spelling the index does not hold is not mistaken for no coverage.
aggregate_news Counts per hour, day, week or month: articles, and on request distinct stories, outlets and signal percentiles. For attention over time, in one call instead of one search per day.
related_entities The organizations, people, places and topics most often reported alongside one entity, strongest first, with co-mention counts and share, and for the strongest the newest shared articles, distinct sources and stories. For "who shows up alongside X". Co-mention is association in reporting, not partnership.
connection_path How two entities connect in the reporting: the articles naming both, and up to five paths through one shared neighbour. For "how are X and Y linked". Bounded to two hops.
entity_network One entity's neighbours grouped by type; with a comparison window, which neighbours are new or gone and which ties strengthened or weakened. For "what changed around X since last month".
shared_exposures What up to five entities have in common: shared articles, stories, sources, topics, countries and signal intensities, with the newest shared articles as evidence. For a watchlist: the reporting that names all of them at once.
create_watch Save a story query the index runs on an interval, recording new and growing stories. For monitoring without a polling loop. Each run costs its query's credits.
list_watches List the account's watches, their status and the plan's cap. To find a watch paused for credits or a revoked key.
get_watch Read one watch: its query, schedule, status (active, paused_key, paused_credits or failing) and when it last ran. To see why one watch went quiet without listing them all.
watch_events Read a watch's new and growing stories, newest first. When no webhook is set, or to catch up after a gap. Events are kept seven days.
delete_watch Delete a watch, its state and its events. Each run costs credits, so remove the ones nobody reads.
account_status Report the current plan, credits and usage. Free. Lets an agent check its own remaining budget instead of discovering the limit by hitting it.

Connecting it

The server is hosted, and every MCP client connects to it the same way: a URL and your API key. Nothing to install, and the same key works for the REST API.

JSON
{
  "mcpServers": {
    "unzoi": {
      "url": "https://api.unzoi.com/mcp",
      "headers": { "x-api-key": "your-key" }
    }
  }
}

The transport is Streamable HTTP, and the protocol version is negotiated — offer yours and the server answers with the one it picked. The endpoint is stateless, so there is no session to keep alive and no reconnect logic to write.

Client-specific walkthroughs, each covering what that client does well and the mistake people actually hit with it:

The technical docs carry one for every client — including the bridge for clients that only speak stdio.

MCP access is on every plan, including the free one.

What kind of MCP server this is

MCP servers differ in ways that decide how much work integration is, so here is the shape of this one before you wire anything up. It is remote and hosted, so there is no package to install and no process to supervise. The transport is Streamable HTTP — a single endpoint answering POST — rather than the retired two-endpoint SSE arrangement that older servers and older client versions expect.

The protocol version is negotiated per connection rather than pinned, so a client offering an older revision gets that revision back instead of a refusal. The endpoint is stateless: no session identifier is issued, nothing expires, and every request carries its own authorisation. For a client author that removes reconnect logic, keepalives and session recovery from the list of things to build. It also means a request that fails is just a request that failed, rather than a session that has to be re-established before anything else can proceed.

Credits are shared with the REST API rather than metered separately, so a tool call costs exactly what the equivalent request costs, and every result reports it as credits_charged. The handshake, tools/list and account_status are free, and estimate prices a call without running it. Refusals that concern your account — credits spent on a key that stops there, a lapsed subscription — arrive as tool errors on a working connection rather than as transport failures, which is the difference between an agent that can tell you what is wrong and one that reports the server as unreachable.

Designing tool calls that do not waste context

The most common mistake when wiring news into an agent is reaching for article search when you wanted story grouping. A busy news day means one event has been covered by dozens of outlets, and article search will faithfully return all of them. The model then reads the same fact many times, and — because repetition reads as importance — weights it accordingly.

Prefer list_stories whenever the output is going into a context window and search_news when a human will scan the results. Bound every call with a time range, because "recent" means something different to a model than it does to you, and an unbounded query over a deep archive is both slower and less useful than a bounded one.

If the agent is doing research rather than answering a single question, let it call find_related on the best result instead of issuing another search, and get_story on a story it has already seen instead of searching again with the identifier. Models reformulate queries badly under uncertainty, and each reformulation costs a request; a similarity lookup from a result it already trusts is cheaper and usually better.

For questions about attention over time, aggregate_news returns counts per hour, day, week or month in one call, where a loop would issue one search per day.

For a place, pass near or bbox rather than a list of city names; for a kind of event, event_type takes one of seventeen classes.

For "who is connected to X", call related_entities on a resolved id and have the model present the answer as a reporting pattern: two companies named together in forty articles were reported together, and the evidence says whether that was a contract or a lawsuit.

Watches

create_watch saves a story query that the index runs for you on an interval. It records new_story when a story first reaches the number of outlets you set, and story_growth as it keeps gaining them. An agent reads those events with watch_events; a server can receive them at an https webhook instead, each signed with HMAC-SHA256.

Each run costs its query's credits, and each webhook delivery attempt 1, charged to the key that created the watch. Plans cap how many watches an account keeps and the shortest interval: Free 2 at 60 minutes, Build 25 at 15, Scale 200 at 5, Archive 1,000 at 1.

What the tools return

Metadata, a snippet and a link: headline, outlet, timestamp, language, and a story identifier where one applies. Article bodies are not redistributed, which keeps the publishers who did the reporting in the loop and keeps you clear of the licensing questions that arrive the moment a pipeline starts storing full text it does not own.

Every result arrives twice: as structuredContent, typed by the tool's outputSchema, and as a text block carrying the same JSON for clients that predate structured results. A schema-aware host validates the first; an older one reads the second. Nothing is lost either way.

Pass view: "compact" when the result feeds a model. Each hit keeps its id, title, url, source, publication time, language, story identifier and score, and the full record is one get_article call away. A model deciding what to read next needs to compare candidates, not consume all of them, and a compact result set lets it consider far more options within the same budget. When it does need the full text, the link is right there and the publisher gets the visit.

Every response says whether it is complete. partial is true when part of the index did not answer, coverage says why, and from_clamped says the time range was shortened to the plan's archive depth. An agent that reads those three fields can tell "nothing was written" from "part of the index did not answer", and every search-shaped tool tells it to retry, narrow the range or check account_status rather than conclude absence.

A misspelled argument is rejected with a suggestion rather than ignored. organiztion comes back as an invalid-arguments error naming organization; it does not quietly search everything. Every tool declares additionalProperties: false, so a client that validates arguments before sending them catches the same mistake locally.