io.github.cyanheads/gdelt-mcp-server
Search and analyze global news coverage and US TV transcripts via the GDELT Project APIs.
ماذا يمكنك أن تفعل مع io.github.cyanheads/gdelt-mcp-server؟
- Shared request pacer across the DOC and TV tools: one request in flight at 1 req/5s, with a backoff (5s, doubling to 60s) after any rate-limit response
- Structured
stationsarrays instead of raw TV query syntax - GKG theme lookup held in memory after first download
- Query echo on every response: the original query and applied timespan, so calls chain without re-deriving parameters
- Typed series
labelfields ("Volume Intensity","Average Tone", language/country names) instead of positional arrays - Station metadata with an
isActiveboolean and ISO 8601 dates - Zero matches return the normal output shape with empty arrays and a
noticeon how to broaden the query
التثبيت
أضف هذا إلى إعدادات MCP في عميلك (Claude Desktop: claude_desktop_config.json، وCursor: .cursor/mcp.json، ويستخدم VS Code وغيره الصيغة نفسها «mcpServers»).
claude mcp add --transport http gdelt-mcp-server 'https://gdelt.caseyjhand.com/mcp'
{
"mcpServers": {
"gdelt-mcp-server": {
"type": "http",
"url": "https://gdelt.caseyjhand.com/mcp"
}
}
}
@cyanheads/gdelt-mcp-serverclaude mcp add gdelt-mcp-server -e 'GDELT_CLAMP_LIMITS=true' -e 'GDELT_BASE_URL=https://api.gdeltproject.org/api/v2' -e 'GDELT_REQUEST_DELAY_MS=5300' -e 'GDELT_REQUEST_TIMEOUT_MS=60000' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/gdelt-mcp-server@0.8.0
{
"mcpServers": {
"gdelt-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/gdelt-mcp-server@0.8.0"
],
"env": {
"GDELT_CLAMP_LIMITS": "true",
"GDELT_BASE_URL": "https://api.gdeltproject.org/api/v2",
"GDELT_REQUEST_DELAY_MS": "5300",
"GDELT_REQUEST_TIMEOUT_MS": "60000",
"MCP_LOG_LEVEL": "info"
}
}
}
}
متغيرات البيئة
GDELT_CLAMP_LIMITSClamp counts above each per-call cap; false rejects them.GDELT_BASE_URLOverride the base URL for the GDELT DOC and TV APIs.GDELT_REQUEST_DELAY_MSMinimum milliseconds between GDELT requests, enforcing the 1 req/5s rate limit.GDELT_REQUEST_TIMEOUT_MSDeadline in milliseconds for a single GDELT request; the whole call, retries included, is bounded at twice this value.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
@cyanheads/gdelt-mcp-serverclaude mcp add gdelt-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'GDELT_CLAMP_LIMITS=true' -e 'GDELT_BASE_URL=https://api.gdeltproject.org/api/v2' -e 'GDELT_REQUEST_DELAY_MS=5300' -e 'GDELT_REQUEST_TIMEOUT_MS=60000' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/gdelt-mcp-server@0.8.0
{
"mcpServers": {
"gdelt-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/gdelt-mcp-server@0.8.0"
],
"env": {
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"GDELT_CLAMP_LIMITS": "true",
"GDELT_BASE_URL": "https://api.gdeltproject.org/api/v2",
"GDELT_REQUEST_DELAY_MS": "5300",
"GDELT_REQUEST_TIMEOUT_MS": "60000",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_ENDPOINT_PATH": "/mcp",
"MCP_AUTH_MODE": "none",
"MCP_LOG_LEVEL": "info"
}
}
}
}
متغيرات البيئة
MCP_TRANSPORT_TYPESelects the HTTP transport.GDELT_CLAMP_LIMITSClamp counts above each per-call cap; false rejects them.GDELT_BASE_URLOverride the base URL for the GDELT DOC and TV APIs.GDELT_REQUEST_DELAY_MSMinimum milliseconds between GDELT requests, enforcing the 1 req/5s rate limit.GDELT_REQUEST_TIMEOUT_MSDeadline in milliseconds for a single GDELT request; the whole call, retries included, is bounded at twice this value.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODEAuthentication mode to use: 'none', 'jwt', or 'oauth'.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
التوثيق
مأخوذ من ملف README للمشروع على GitHub ويُحدَّث أسبوعيًا. تفتح الروابط على GitHub.
المحتويات
@cyanheads/gdelt-mcp-server
Search and analyze global news coverage and US television transcripts from the GDELT Project's real-time APIs via MCP. STDIO or Streamable HTTP.
9 Tools
Public Hosted Server: https://gdelt.caseyjhand.com/mcp
Overview
News and TV coverage analysis from the GDELT Project: the last 3 months of global news in 65+ languages, and US TV transcripts from 2009 through October 2024 across 150+ stations. Search articles and clips, track coverage spikes, analyze tone, and trace how a story spread across languages and countries.
Tools
| Tool | Description |
|---|---|
gdelt_search_articles | Search the last 3 months of global news coverage (65+ languages) with full-text and filter operators. Fetches up to 250 articles, returns as many as fit a 48,000-byte response, and hands back the date windows to re-query for the rest. |
gdelt_get_coverage_timeline | Retrieve a time series of coverage volume or average tone for a query. volume_with_articles mode includes top articles per spike timestep, with points to get named timesteps' full article lists. Pages by offset within a 48,000-byte response. |
gdelt_get_tone_distribution | Get a tone histogram (bins ~−30 to +30) showing whether coverage is uniformly negative, bimodal, or clustered near neutral. Pages by offset within a 48,000-byte response. |
gdelt_get_coverage_breakdown | Break down coverage volume by source language or source country — a multi-series time series showing geographic propagation. Values are normalized shares of media output, not article counts. Pages by offset within a 48,000-byte response. |
gdelt_search_themes | Find GKG theme identifiers for the theme: operator the DOC tools accept — drought finds NATURAL_DISASTER_DROUGHT, cyberattack finds CYBER_ATTACK — ranked by the count GDELT's theme lookup lists. |
gdelt_search_tv | Search US television news closed captions (2009–Oct 2024) and return per-station airtime time series. |
gdelt_get_tv_clips | Retrieve matching TV clips with transcript excerpts and Internet Archive viewing links — up to 3,000 fetched, as many as fit a 48,000-byte response returned — and the date windows to re-query for the rest. |
gdelt_get_tv_context | Get the most frequent co-occurring words and phrases from GDELT's relevance-ranked sample of TV clips matching a query. |
gdelt_list_tv_stations | List TV stations with market, network, and monitoring date ranges — all of them, or filtered by station ID, network, or market — to verify station availability before querying. |
Capability reference
gdelt_search_articles tool
- Full GDELT query syntax: phrases, boolean OR, exclusion, filter operators (
sourcecountry:,sourcelang:,domain:,theme:,tone</tone>), proximity (near20:) and repetition (repeat3:) —gdelt_search_themesfinds the identifierstheme:takes - Configurable sort (
relevance,dateDesc,dateAsc,toneDesc,toneAsc,hybridRel) and fetch count, up to 250 per call — amaxRecordsup to 2,500 runs at 250 and reports the clamp inlimitClamp; above 2,500 the call is rejected - Returns URL, title, publication date, domain, language, source country, and social image URL
- Responses carry as many articles as fit a 48,000-byte budget; the rest are counted in
withheldCount, andcontinuationWindowsgives date windows to re-query for them (de-duplicate byurl). 250 is a hard ceiling, not a page size, so narrowing the date window is the only way past it - Articles outside an explicit
startDatetime/endDatetimewindow are dropped, and the notice counts them
gdelt_get_coverage_timeline tool
- Three modes:
volume(normalized % per timestep),volume_with_articles(volume plus top articles per spike — signal detection in one call),tone(average sentiment per timestep) - Each timestep carries its first 3 article links beside
referenceCount, the true count;points: ["<date>"]returns only the named timesteps, each with every link - Responses hold as many whole timesteps as fit a 48,000-byte budget; a cut page returns
nextOffsetto pass back asoffset. Each series'pointCountandpeakcover the whole series on every page - Configurable smoothing (0–5 timesteps) and time range (
timespan, or explicitstartDatetime/endDatetime); date resolution (15min/hour/day) is inferred from the returned intervals - A
pointsdate matching no timestep, or anoffsetpast the last timestep, is rejected with the valid values
gdelt_get_tone_distribution tool
- Histogram bins from approximately −30 to +30; each bin carries its first 3 article links beside
referenceCount, andbins: [<bin>]returns only the named bins, each with every link. Abinsvalue matching no bin is rejected with the available bins - Summary fields over the whole histogram:
peakNegativeBin,peakPositiveBin,neutralPct(% of articles in the −2 to +2 range) - Responses hold as many whole bins as fit a 48,000-byte budget; a cut page returns
nextOffsetto pass back asoffset. A snapshot across all matching articles, unlike the tone timeline (gdelt_get_coverage_timelinemodetone)
gdelt_get_coverage_breakdown tool
- Breaks down by
languageorcountryinto a multi-series time series: the top 10 series by summed value, with every other series named inotherSeriesLabels series: ["<label>"]returns only those series, complete, underselectedSeries; a label matching no series is rejected with the available labels- Responses hold as many whole series as fit a 48,000-byte budget; a cut page returns
nextOffsetto pass back asoffset - Values are normalized shares of each source's media output, not article counts, so don't sum them across sources
gdelt_search_themes tool
- Searches the identifiers in GDELT's GKG theme lookup (59,315 themes); each match is the identifier, its listed count, and a paste-ready
operatorsuch astheme:TAX_DISEASE_OUTBREAK - Case-insensitive, with no stemming or synonyms: words match parts of an identifier (
cyberattack→CYBER_ATTACK,plant disease→TAX_PLANTDISEASE). If nothing matches, it retries once with a trailingsdropped (protests→PROTEST) and the notice says so - Ranked by exact match, then listed count (a static prevalence figure, not a live article total), then identifier
- Pages with
offset/limit(1–100, default 25; alimitup to 1,000 runs at 100 and reports the clamp inlimitClamp, and above 1,000 the call is rejected);nextOffsetappears only when more matches remain, and an offset past a non-empty result failsoffset_out_of_range - Does not queue behind the DOC and TV rate limit
gdelt_search_tv tool
- Up to 10 structured
stations(e.g.["CNN", "FOXNEWS"]), or astation:selector embedded in the query — the TV API requires at least one, either way normalizetoggles relative airtime % (default) vs. raw matching 15-second clip counts; optionaldateresaggregation (hour/day/week/month/year). TV operators:station:,network:,market:,show:,context:- Each series carries
station(the ID thestationsinput takes,FOXNEWS) andstationName(GDELT's label,FOX News). If an ID can't be resolved, the series keepsstationNamewithoutstationand the notice says so - Pages at most 500 points per call in date-then-station-name order; pass
nextOffsetwith the same inputs for the next page. Alimitup to 5,000 runs at 500 and reports the clamp inlimitClamp; above 5,000 the call is rejected. The text surface's per-station count, sum, and peak cover only the returned page - Check station date ranges with
gdelt_list_tv_stationsbefore querying recent events
gdelt_get_tv_clips tool
- Up to 3,000 clips fetched per call, sorted by relevance, date descending, or date ascending — a
maxRecordsup to 30,000 runs at 3,000 and reports the clamp inlimitClamp; above 30,000 the call is rejected - Each clip: show name,
stationandstationName(resolved as ingdelt_search_tv, so a series'stationpasses straight intostations), air timestamp, 15-second transcript excerpt, direct Archive.org link, and optional thumbnail - The server requests whole clock hours, so any window width works; clips outside an explicit
startDatetime/endDatetimewindow are dropped and counted in the notice (atimespancall drops nothing) - Responses carry as many clips as fit a 48,000-byte budget; the rest are counted in
withheldCount, andcontinuationWindowsgives date windows to re-query for them (de-duplicate byarchiveUrl). 3,000 is a hard ceiling, not a page size, so narrowing the date window is the only way past it
gdelt_get_tv_context tool
- Returns the most frequent non-stopword terms co-occurring with the query
- Terms and scores come from GDELT's sample of the 200 most relevant matching clips, not all coverage; every non-empty response says so in
notice. Compare windows or stations with a separate, narrower call for each - Relative frequency scores 0–100, where the query term itself scores 100
gdelt_list_tv_stations tool
- Returns every station with market, network, monitoring start date, and end date — or only those matching the optional
stations,network, andmarketfilters - Filters match whole values case-insensitively and combine with AND (
network: "FOX"does not matchFOXNEWS);activeCountandtotalCountcount the returned stations - A filter or station ID that matches nothing is named in the notice;
isActiveis true when the end date is within the last 24 hours - Use to verify a station was active during a target period, or to find valid station IDs for the
stationsparameter on other TV tools
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
GDELT-specific:
- Shared request pacer across the DOC and TV tools: one request in flight at 1 req/5s, with a backoff (5s, doubling to 60s) after any rate-limit response
- Structured
stationsarrays instead of raw TV query syntax - GKG theme lookup held in memory after first download
Agent-friendly output:
- Query echo on every response: the original query and applied timespan, so calls chain without re-deriving parameters
- Typed series
labelfields ("Volume Intensity","Average Tone", language/country names) instead of positional arrays - Station metadata with an
isActiveboolean and ISO 8601 dates - Zero matches return the normal output shape with empty arrays and a
noticeon how to broaden the query
Getting started
Public Hosted Instance
A public instance is available at https://gdelt.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"gdelt-mcp-server": {
"type": "streamable-http",
"url": "https://gdelt.caseyjhand.com/mcp"
}
}
}
Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"gdelt-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/gdelt-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"gdelt-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/gdelt-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"gdelt-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/gdelt-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No API key required — GDELT is a free public API.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/gdelt-mcp-server.git
- Navigate into the directory:
cd gdelt-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env if you need to override defaults
Configuration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
GDELT_BASE_URL | Override the GDELT API base URL for both DOC and TV APIs. | https://api.gdeltproject.org/api/v2 |
GDELT_REQUEST_DELAY_MS | Minimum milliseconds between GDELT requests (enforces 1 req/5s limit). | 5300 |
GDELT_REQUEST_TIMEOUT_MS | Deadline for a single GDELT request (a call with retries is bounded at twice this). | 60000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for the HTTP server. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path where the MCP server is mounted. | /mcp |
MCP_PUBLIC_URL | Public origin override for TLS-terminating reverse-proxy deployments. | none |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_SESSION_MODE | HTTP session mode: auto, stateful, or stateless. | stateless |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
MCP_GC_PRESSURE_INTERVAL_MS | Opt-in Bun-only forced-GC loop interval in ms. | 0 (disabled) |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
- Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http - Run checks and tests:
bun run devcheck # Lint, format, typecheck, security audit bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t gdelt-mcp-server .
docker run --rm -p 3010:3010 gdelt-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/gdelt-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Nine tools across the DOC and TV APIs and the GKG theme lookup. |
src/services/gdelt | GdeltDocService and GdeltTvService wrapping the DOC and TV APIs, the shared outbound pacer every DOC and TV call queues behind, and GdeltThemeService for the GKG theme lookup. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools via the barrels in
src/mcp-server/tools/definitions/index.ts - Wrap GDELT API calls: validate raw JSON → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
الأسئلة الشائعة
ما هو io.github.cyanheads/gdelt-mcp-server؟
Search and analyze global news coverage and US TV transcripts via the GDELT Project APIs.
كيف أثبّت io.github.cyanheads/gdelt-mcp-server؟
يعمل كخادم بعيد. في Claude Code: claude mcp add --transport http gdelt-mcp-server 'https://gdelt.caseyjhand.com/mcp'. وفي العملاء الآخرين، الصق إعداد JSON من هذه الصفحة في إعدادات MCP الخاصة بهم.
هل يحتاج io.github.cyanheads/gdelt-mcp-server إلى مفتاح API؟
لا يذكر إدراجه في السجل أي مفتاح أو سر إلزامي. قد تتطلب بعض الميزات حسابًا لدى الخدمة التي يتصل بها.
هل io.github.cyanheads/gdelt-mcp-server مفتوح المصدر؟
نعم. شيفرته متاحة على GitHub بترخيص Apache-2.0.
من يصون io.github.cyanheads/gdelt-mcp-server؟
ينشره cyanheads. يدرجه AI.tn لكنه لا يصونه ولا يوصي به.
من سجل MCP الرسمي. الإدراج ليس توصية: راجع شيفرة الخادم وصلاحياته قبل منحه الوصول إلى بياناتك.