Files
worldmonitor/docs/mcp.mdx
Elie Habib e4c95ad9be docs(mintlify): cover MCP, OAuth, non-RPC endpoints, and usage (#3209)
* docs(mintlify): cover MCP, OAuth, non-RPC endpoints, and usage

Audit against api/ + proto/ revealed 9 OpenAPI specs missing from nav,
the scenario/v1 service undocumented, and MCP (32 tools + OAuth 2.1 flow)
with no user-facing docs. The stale Docs_To_Review/API_REFERENCE.md still
pointed at pre-migration endpoints that no longer exist.

- Wire 9 orphaned specs into docs.json: ConsumerPrices, Forecast, Health,
  Imagery, Radiation, Resilience, Sanctions, Thermal, Webcam
- Hand-write ScenarioService.openapi.yaml (3 RPCs) until it's proto-backed
  (tracked in issue #3207)
- New MCP page with tool catalog + client setup (Claude Desktop/web, Cursor)
- New MDX for OAuth, Platform, Brief, Commerce, Notifications, Shipping v2,
  Proxies
- New Usage group: quickstart, auth matrix, rate limits, errors
- Remove docs/Docs_To_Review/API_REFERENCE.md and EXTERNAL_APIS.md
  (referenced dead endpoints); add README flagging dir as archival

* docs(mintlify): move scenario docs out of generated docs/api/ tree

The pre-push hook enforces that docs/api/ is proto-generated only.
Replace the hand-written ScenarioService.openapi.yaml with a plain
MDX page (docs/api-scenarios.mdx) until the proto migration lands
(tracked in issue #3207).

* docs(mintlify): fix factual errors flagged in PR review

Reviewer caught 5 endpoints where I speculated on shape/method/limits
instead of reading the code. All fixes cross-checked against the
source:

- api-shipping-v2: route-intelligence is GET with query params
  (fromIso2, toIso2, cargoType, hs2), not POST with a JSON body.
  Response shape is {primaryRouteId, chokepointExposures[],
  bypassOptions[], warRiskTier, disruptionScore, ...}.
- api-commerce: /api/product-catalog returns {tiers, fetchedAt,
  cachedUntil, priceSource} with tier groups free|pro|api_starter|
  enterprise, not the invented {currency, plans}. Document the
  DELETE purge path too.
- api-notifications: Slack/Discord /oauth/start are POST + Clerk
  JWT + PRO (returning {oauthUrl}), not GET redirects. Callbacks
  remain GET.
- api-platform: /api/version returns the latest GitHub Release
  ({version, tag, url, prerelease}), not deployed commit/build
  metadata.
- api-oauth + mcp: /api/oauth/register limit is 5/60s/IP (match
  code), not 10/hour.

Also caught while double-checking: /api/register-interest and
/api/contact are 5/60min and 3/60min respectively (1-hour window,
not 1-minute). Both require Turnstile. Removed the fabricated
limits for share-url, notification-channels, create-checkout
(they fall back to the default per-IP limit).

* docs(mintlify): second-round fixes — verify every claim against source

Reviewer caught 7 more cases where I described API behavior I hadn't
read. Each fix below cross-checked against the handler.

- api-commerce (product-catalog): tiers are flat objects with
  monthlyPrice/annualPrice/monthlyProductId/annualProductId on paid
  tiers, price+period for free, price:null for enterprise. There is
  no nested plans[] array.
- api-commerce (referral/me): returns {code, shareUrl}, not counts.
  Code is a deterministic 8-char HMAC of the Clerk userId; binding
  into Convex is fire-and-forget via ctx.waitUntil.
- api-notifications (notification-channels): actual action set is
  create-pairing-token, set-channel, set-web-push, delete-channel,
  set-alert-rules, set-quiet-hours, set-digest-settings. Replaced
  the made-up list.
- api-shipping-v2 (webhooks): alertThreshold is numeric 0-100
  (default 50), not a severity string. Subscriber IDs are wh_+24hex;
  secret is raw 64-char hex (no whsec_ prefix). POST registration
  returns 201. Added the management routes: GET /{id},
  POST /{id}/rotate-secret, POST /{id}/reactivate.
- api-platform (cache-purge): auth is Authorization: Bearer
  RELAY_SHARED_SECRET, not an admin-key header. Body takes keys[]
  and/or patterns[] (not {key} or {tag}), with explicit per-request
  caps and prefix-blocklist behavior.
- api-platform (download): platform+variant query params, not
  file=<id>. Response is a 302 to a GitHub release asset; documented
  the full platform/variant tables.
- mcp: server also accepts direct X-WorldMonitor-Key in addition to
  OAuth bearer. Fixed the curl example which was incorrectly sending
  a wm_live_ API key as a bearer token.
- api-notifications (youtube/live): handler reads channel or videoId,
  not channelId.
- usage-auth: corrected the auth-matrix row for /api/mcp to reflect
  that OAuth is one of two accepted modes.

* docs(mintlify): fix Greptile review findings

- mcp.mdx: 'Five' slow tools → 'Six' (list contains 6 tools)
- api-scenarios.mdx: replace invalid JSON numeric separator
  (8_400_000_000) with plain integer (8400000000)

Greptile's third finding — /api/oauth/register rate-limit contradiction
across api-oauth.mdx / mcp.mdx / usage-rate-limits.mdx — was already
resolved in commit 4f2600b2a (reviewed commit was eb5654647).
2026-04-19 15:03:16 +04:00

245 lines
9.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "MCP Server"
description: "Connect Claude, Cursor, and other MCP-compatible clients to WorldMonitor's live global-intelligence data via the Model Context Protocol."
---
WorldMonitor exposes its intelligence stack as a [Model Context Protocol](https://modelcontextprotocol.io) server so any MCP-compatible client (Claude Desktop, Claude web, Cursor, MCP Inspector, custom agents) can pull live conflict, market, aviation, maritime, economic, and forecasting data directly into a model's context.
<Info>
The MCP server is gated behind **PRO**. Free-tier users see a 401 at the OAuth step.
</Info>
## Endpoints
| Endpoint | Purpose |
|----------|---------|
| `https://api.worldmonitor.app/api/mcp` | JSON-RPC server (Streamable HTTP transport, protocol `2025-03-26`) |
| `https://api.worldmonitor.app/api/oauth/register` | Dynamic Client Registration (RFC 7591) |
| `https://api.worldmonitor.app/api/oauth/authorize` | OAuth 2.1 authorization endpoint (PKCE required) |
| `https://api.worldmonitor.app/api/oauth/token` | Token endpoint (authorization_code + refresh_token) |
| `https://api.worldmonitor.app/.well-known/oauth-authorization-server` | AS metadata (RFC 8414) |
| `https://api.worldmonitor.app/.well-known/oauth-protected-resource` | Resource server metadata (RFC 9728) |
Protocol version: `2025-03-26`. Server identifier: `worldmonitor` v1.0.
## Authentication
The MCP handler accepts two auth modes, in priority order:
1. **OAuth 2.1 bearer** — `Authorization: Bearer <token>` where `<token>` was issued by `/api/oauth/token`. This is what Claude Desktop, claude.ai, Cursor, and MCP Inspector use automatically. Required for any client that hits MCP from a browser origin.
2. **Direct API key** — `X-WorldMonitor-Key: wm_live_...`. Intended for server-side scripts, `curl`, and custom integrations. Do **not** send a `wm_live_...` key as a `Bearer` token — it will fail OAuth resolution and return `401 invalid_token`.
Both modes check the same PRO entitlement before every tool call, so a subscription downgrade revokes access on the next request.
### Redirect URI allowlist
Dynamic Client Registration is **not** open to arbitrary HTTPS redirects. Only these prefixes are accepted:
- `https://claude.ai/api/mcp/auth_callback`
- `https://claude.com/api/mcp/auth_callback`
- `http://localhost:<port>` / `http://127.0.0.1:<port>` (any port) — for Claude Code, MCP Inspector, local development
Other clients must proxy via one of these redirects or run locally.
### Token lifetimes
| Artifact | TTL |
|----------|-----|
| Authorization code | 10 min |
| Access token | 1 hour |
| Refresh token | 7 days |
| Registered client record | 90 days (sliding) |
## Rate limits
- **MCP calls**: 60 requests / minute / API key (sliding window)
- **OAuth authorize**: 10 requests / minute / IP
- **OAuth token**: 10 requests / minute / IP
- **Dynamic registration**: 5 registrations / minute / IP
Exceeding returns `429` with a `Retry-After` header.
## Client setup
### Claude Desktop
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) — use the remote MCP entry:
```json
{
"mcpServers": {
"worldmonitor": {
"url": "https://api.worldmonitor.app/api/mcp"
}
}
}
```
Claude Desktop handles the OAuth flow automatically on first connection.
### Claude web (claude.ai)
Add via **Settings → Connectors → Add custom connector**:
- Name: `WorldMonitor`
- URL: `https://api.worldmonitor.app/api/mcp`
### Cursor
`~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"worldmonitor": {
"url": "https://api.worldmonitor.app/api/mcp"
}
}
}
```
### MCP Inspector (debugging)
```bash
npx @modelcontextprotocol/inspector https://api.worldmonitor.app/api/mcp
```
## Tool catalog
The server exposes 32 tools. Most are cache-reads over pre-seeded Redis keys (sub-second). Six (`get_world_brief`, `get_country_brief`, `analyze_situation`, `generate_forecasts`, `search_flights`, `search_flight_prices_by_date`) call live LLMs or external APIs and are slower.
### Markets & economy
| Tool | Description |
|------|-------------|
| `get_market_data` | Equity quotes, commodity prices (incl. gold GC=F), crypto, FX, sector performance, ETF flows, Gulf markets. |
| `get_economic_data` | Fed Funds, economic calendar, fuel prices, ECB FX, EU yield curve, earnings, COT, energy storage, BIS DSR + property prices. |
| `get_country_macro` | IMF WEO bundle per country: inflation, current account, GDP/capita, unemployment, savings-investment gap (~210 countries). |
| `get_eu_housing_cycle` | Eurostat annual house price index (prc_hpi_a), 10-yr sparkline per EU member + EA20/EU27. |
| `get_eu_quarterly_gov_debt` | Eurostat quarterly gross government debt (%GDP), 8-quarter sparkline. |
| `get_eu_industrial_production` | Eurostat monthly industrial production index, 12-month sparkline. |
| `get_prediction_markets` | Active Polymarket event contracts with live probabilities. |
| `get_supply_chain_data` | Dry bulk shipping stress index, customs flows, COMTRADE bilateral trade. |
| `get_commodity_geo` | 71 major mining sites worldwide (gold, silver, copper, lithium, uranium, coal). |
### Geopolitical & security
| Tool | Description |
|------|-------------|
| `get_conflict_events` | Active UCDP/Iran conflicts, unrest w/ geo-coords, country risk scores. |
| `get_country_risk` | CII score 0-100, component breakdown, travel advisory, OFAC exposure per country. Fast, no LLM. |
| `get_military_posture` | Theater posture + military risk scores. |
| `get_cyber_threats` | URLhaus/Feodotracker malware IOCs, CISA KEV catalog, active C2 infra. |
| `get_sanctions_data` | OFAC SDN entities + sanctions pressure scores by country. |
| `get_news_intelligence` | AI-classified threat news, GDELT signals, cross-source intel. |
| `get_positive_events` | Diplomatic agreements, humanitarian aid, peace initiatives. |
| `get_social_velocity` | Reddit r/worldnews + r/geopolitics top posts, engagement scores. |
### Movement & infrastructure
| Tool | Description |
|------|-------------|
| `get_airspace` | Live ADS-B over a country. Params: `iso2` (2-letter code), `filter` (`all`/`civilian`/`military`). |
| `get_maritime_activity` | AIS density zones, dark-ship events, chokepoint congestion per country. Params: `iso2`. |
| `get_aviation_status` | FAA airport delays, NOTAM closures, tracked military aircraft. |
| `get_infrastructure_status` | Cloudflare Radar outages, major cloud/internet service status. |
| `search_flights` | Google Flights real-time search between IATA airport codes on a specific date. |
| `search_flight_prices_by_date` | Date-grid cheapest-day pricing across a range. |
### Environment & science
| Tool | Description |
|------|-------------|
| `get_natural_disasters` | USGS earthquakes, NASA FIRMS wildfires, hazard events. |
| `get_climate_data` | Temp/precip anomalies vs WMO normals, GDACS/FIRMS alerts, Mauna Loa CO2, OpenAQ PM2.5, sea ice, ocean heat. |
| `get_radiation_data` | Global radiation monitoring station readings + anomaly flags. |
| `get_research_signals` | Emerging technology events from curated research feeds. |
### AI intelligence (live LLM)
| Tool | Description | Cost |
|------|-------------|------|
| `get_world_brief` | AI-summarized world intel brief. Optional `geo_context` param. | LLM |
| `get_country_brief` | Per-country geopolitical + economic assessment. Supports analytical frameworks. | LLM |
| `analyze_situation` | Ad-hoc geopolitical deduction from a query + context. Returns confidence + supporting signals. | LLM |
| `generate_forecasts` | Fresh probability estimates (bypasses cache). | LLM |
| `get_forecast_predictions` | Pre-computed cached forecasts. Fast. | Cache |
## JSON-RPC example
Server-side with a direct API key — send it as `X-WorldMonitor-Key`, **not** as a bearer token.
```bash
WM_KEY="wm_live_..."
# 1. List tools
curl -s https://api.worldmonitor.app/api/mcp \
-H "X-WorldMonitor-Key: $WM_KEY" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 2. Call a cache tool
curl -s https://api.worldmonitor.app/api/mcp \
-H "X-WorldMonitor-Key: $WM_KEY" \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0","id":2,
"method":"tools/call",
"params":{"name":"get_country_risk","arguments":{"iso2":"IR"}}
}'
```
If instead you've completed the OAuth flow and hold an access token from `/api/oauth/token`, pass it as `Authorization: Bearer $TOKEN`.
## Response shape
Tool responses use the standard MCP content block format:
```json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{ "type": "text", "text": "{...json payload...}" }
],
"isError": false
}
}
```
For cache tools, the JSON payload includes `_meta` with `fetchedAt` and `staleness` so the model can reason about freshness.
## Data freshness
All cache tools read from Redis keys written by Railway cron seeders. Typical freshness:
| Domain | Typical freshness |
|--------|-------------------|
| Markets (intraday) | 15 min |
| Flights (ADS-B) | 13 min |
| Maritime (AIS) | 515 min |
| Conflicts / unrest | 1560 min |
| Macro / BIS / Eurostat | dailyweekly |
| IMF WEO | monthly |
Seed-level health per key: [status.worldmonitor.app](https://status.worldmonitor.app/).
## Errors
| Code | Meaning |
|------|---------|
| 401 | Invalid / missing token |
| 403 | Token valid but account not PRO |
| 404 | Unknown tool name |
| 429 | Rate limited (60 req/min/key) |
| 503 | Upstream cache unavailable |
Tool-level errors return `isError: true` with a text explanation in `content`.
## Related
- [Authentication overview](/authentication) — browser vs bearer vs OAuth
- [API Reference](/api/ConflictService.openapi.yaml) — the same data via REST
- [Pro features](https://www.worldmonitor.app/pro)