From fc105872e2425c0ba4744c8c1438be7b42bcbbbe Mon Sep 17 00:00:00 2001 From: Marc Shade Date: Tue, 24 Feb 2026 11:36:26 -0500 Subject: [PATCH] fix: update UCDP GED API to v25.1 + add token auth support UCDP now requires x-ucdp-access-token header (previously open access). Updated from v24.1 to v25.1 (latest stable). Token passed via UCDP_ACCESS_TOKEN env var; gracefully degrades to 0 events without it (conflict zone fallback uses static INTEL_HOTSPOTS). Co-Authored-By: Claude Opus 4.6 --- src/world_intel_mcp/sources/conflict.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/world_intel_mcp/sources/conflict.py b/src/world_intel_mcp/sources/conflict.py index f711afe..cf496d2 100644 --- a/src/world_intel_mcp/sources/conflict.py +++ b/src/world_intel_mcp/sources/conflict.py @@ -203,7 +203,7 @@ async def fetch_acled_events( # UCDP: Uppsala Conflict Data Program (GED API) # --------------------------------------------------------------------------- -_UCDP_GED_URL = "https://ucdpapi.pcr.uu.se/api/gedevents/24.1" +_UCDP_GED_URL = "https://ucdpapi.pcr.uu.se/api/gedevents/25.1" _VIOLENCE_TYPES = { 1: "state-based", @@ -219,8 +219,9 @@ async def fetch_ucdp_events( ) -> dict: """Fetch recent conflict events from the UCDP GED API. - No API key required. Fetches multiple pages in parallel when the - dataset spans more than one page. + Optionally uses ``UCDP_ACCESS_TOKEN`` if set (required since 2026). + Fetches multiple pages in parallel when the dataset spans more than + one page. Args: fetcher: Shared HTTP fetcher with caching and circuit breaking. @@ -235,6 +236,11 @@ async def fetch_ucdp_events( now = datetime.now(timezone.utc) cutoff = now - timedelta(days=days) + ucdp_headers: dict[str, str] = {} + ucdp_token = os.environ.get("UCDP_ACCESS_TOKEN") + if ucdp_token: + ucdp_headers["x-ucdp-access-token"] = ucdp_token + # Fetch page 1 to discover total pages (UCDP is slow — 30s timeout) page1_data = await fetcher.get_json( _UCDP_GED_URL, @@ -242,6 +248,7 @@ async def fetch_ucdp_events( cache_key=f"conflict:ucdp:{days}:page1", cache_ttl=21600, params={"pagesize": min(limit, 1000), "page": 0}, + headers=ucdp_headers or None, timeout=30.0, ) @@ -268,6 +275,7 @@ async def fetch_ucdp_events( cache_key=f"conflict:ucdp:{days}:page{p}", cache_ttl=21600, params={"pagesize": min(limit, 1000), "page": p}, + headers=ucdp_headers or None, timeout=30.0, ) for p in range(1, total_pages)