feat(intelligence): GetCountryRisk RPC + MCP tool for per-country risk scores (#2502)

* feat(intelligence): GetCountryRisk RPC for per-country risk intelligence

Adds a new fast Redis-read RPC that consolidates CII score, travel advisory
level, and OFAC sanctions exposure into a single per-country response.

Replaces the need to call GetRiskScores (all-countries) and filter client-side.
Wired to MCP as get_country_risk tool (no LLM, ~200ms, good for agent screening).

- proto/intelligence/v1/get_country_risk.proto (new)
- server/intelligence/v1/get-country-risk.ts (reads 3 pre-seeded Redis keys)
- gateway.ts: slow cache tier
- api/mcp.ts: RpcToolDef with 8s timeout
- tests/mcp.test.mjs: update tool count 27→28

* fix(intelligence): upstream-unavailable signal, fetchedAt from CII, drop redundant catch

P1: return upstreamUnavailable:true when all Redis reads are null — prevents
CDN from caching false-negative sanctions/risk responses during Redis outages.

P2: fetchedAt now uses cii.computedAt (actual data age) instead of request time.

P2: removed redundant .catch(() => null) — getCachedJson already swallows errors.

* fix(intelligence): accurate OFAC counts and country names for GetCountryRisk

P1: sanctions:pressure:v1.countries is a top-12 slice — switch to a new
sanctions:country-counts:v1 key (ISO2→count across ALL 40K+ OFAC entries).
Written by seed-sanctions-pressure.mjs in afterPublish alongside entity index.

P1: trigger upstreamUnavailable:true when sanctions key alone is missing,
preventing false-negative sanctionsActive:false from being cached by CDN.

P2: advisory seeder now writes byCountryName (ISO2→display name) derived
from country-names.json reverse map. Handler uses it as fallback so countries
outside TIER1_COUNTRIES (TH, CO, BD, IT...) get proper names.
This commit is contained in:
Elie Habib
2026-03-29 17:07:03 +04:00
committed by GitHub
parent ebd778fe19
commit 8aee4d340e
13 changed files with 367 additions and 5 deletions

View File

@@ -120,6 +120,39 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/intelligence/v1/get-country-risk:
get:
tags:
- IntelligenceService
summary: GetCountryRisk
description: GetCountryRisk retrieves composite risk intelligence for a specific country.
operationId: GetCountryRisk
parameters:
- name: country_code
in: query
description: ISO 3166-1 alpha-2 country code.
required: false
schema:
type: string
responses:
"200":
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/GetCountryRiskResponse'
"400":
description: Validation error
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
default:
description: Error response
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/intelligence/v1/get-country-intel-brief:
get:
tags:
@@ -1014,6 +1047,45 @@ components:
type: string
description: Related entities identified.
description: EventClassification represents an AI-generated classification of a real-world event.
GetCountryRiskRequest:
type: object
properties:
countryCode:
type: string
pattern: ^[A-Z]{2}$
description: ISO 3166-1 alpha-2 country code.
required:
- countryCode
description: GetCountryRiskRequest specifies which country to retrieve risk intelligence for.
GetCountryRiskResponse:
type: object
properties:
countryCode:
type: string
description: ISO 3166-1 alpha-2 country code.
countryName:
type: string
description: Country name.
cii:
$ref: '#/components/schemas/CiiScore'
advisoryLevel:
type: string
description: Travel advisory level from government sources (e.g. "do-not-travel", "reconsider", "caution"). Empty if none.
sanctionsActive:
type: boolean
description: Whether this country has active OFAC sanctions designations.
sanctionsCount:
type: integer
format: int32
description: Count of sanctioned entities associated with this country.
fetchedAt:
type: integer
format: int64
description: 'Data freshness timestamp derived from CII computedAt, as Unix epoch milliseconds.. Warning: Values > 2^53 may lose precision in JavaScript'
upstreamUnavailable:
type: boolean
description: True when all upstream Redis keys were unavailable. Signals CDN cache bypass.
description: GetCountryRiskResponse contains composite risk intelligence for a specific country.
GetCountryIntelBriefRequest:
type: object
properties: