mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* 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 commit4f2600b2a(reviewed commit waseb5654647).
245 lines
9.6 KiB
Plaintext
245 lines
9.6 KiB
Plaintext
---
|
||
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) | 1–5 min |
|
||
| Flights (ADS-B) | 1–3 min |
|
||
| Maritime (AIS) | 5–15 min |
|
||
| Conflicts / unrest | 15–60 min |
|
||
| Macro / BIS / Eurostat | daily–weekly |
|
||
| 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)
|