Class: WorldMonitor::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/worldmonitor.rb

Overview

Thin client for the World Monitor MCP server and REST API.

All keyword arguments are optional; unset values fall back to the WORLDMONITOR_API_KEY (or WM_API_KEY), WORLDMONITOR_BASE_URL, and WORLDMONITOR_MCP_URL environment variables, then to the public production endpoints. transport is injectable for offline tests: a callable (request_hash, timeout) -> [status, content_type, body].

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, base_url: nil, mcp_url: nil, timeout: DEFAULT_TIMEOUT, transport: nil, env: ENV) ⇒ Client

Returns a new instance of Client.



83
84
85
86
87
88
89
90
# File 'lib/worldmonitor.rb', line 83

def initialize(api_key: nil, base_url: nil, mcp_url: nil,
               timeout: DEFAULT_TIMEOUT, transport: nil, env: ENV)
  @api_key = api_key || env["WORLDMONITOR_API_KEY"] || env["WM_API_KEY"]
  @base_url = (base_url || env["WORLDMONITOR_BASE_URL"] || DEFAULT_BASE_URL).sub(%r{/+\z}, "")
  @mcp_url = mcp_url || env["WORLDMONITOR_MCP_URL"] || DEFAULT_MCP_URL
  @timeout = timeout
  @transport = transport || method(:http_transport)
end

Instance Attribute Details

#api_keyObject (readonly)

Returns the value of attribute api_key.



81
82
83
# File 'lib/worldmonitor.rb', line 81

def api_key
  @api_key
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



81
82
83
# File 'lib/worldmonitor.rb', line 81

def base_url
  @base_url
end

#mcp_urlObject (readonly)

Returns the value of attribute mcp_url.



81
82
83
# File 'lib/worldmonitor.rb', line 81

def mcp_url
  @mcp_url
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



81
82
83
# File 'lib/worldmonitor.rb', line 81

def timeout
  @timeout
end

Class Method Details

.parse_body(text, content_type = "") ⇒ Object

Decode an MCP/REST response body. MCP responses may arrive as Server-Sent Events (Streamable HTTP): pull the last data: payload. Otherwise parse the whole body as JSON, falling back to the raw text.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/worldmonitor.rb', line 202

def self.parse_body(text, content_type = "")
  payload = text
  if (content_type || "").include?("text/event-stream") || text =~ /^(event|data):/
    data_lines = text.split(/\r?\n/).select { |l| l.start_with?("data:") }
    payload = data_lines.empty? ? "" : data_lines.last[5..-1].strip
  end
  return text if payload.nil? || payload.empty?

  begin
    JSON.parse(payload)
  rescue JSON::ParserError
    text
  end
end

.truncate(value, limit = 300) ⇒ Object



217
218
219
220
# File 'lib/worldmonitor.rb', line 217

def self.truncate(value, limit = 300)
  text = value.is_a?(String) ? value : JSON.generate(value)
  text.length <= limit ? text : "#{text[0, limit - 1]}"
end

Instance Method Details

#call_tool(name, arguments = {}) ⇒ Object

Call an MCP tool by name and return the unwrapped JSON-RPC result. Keyword arguments become the tool's arguments:

call_tool("get_country_risk", country_code: "IR")


97
98
99
# File 'lib/worldmonitor.rb', line 97

def call_tool(name, arguments = {})
  rpc("tools/call", { "name" => name, "arguments" => stringify_keys(arguments) })
end

#conflict_events(args = {}) ⇒ Object

Recent conflict events (country:, min_fatalities:, limit: ...).



163
164
165
# File 'lib/worldmonitor.rb', line 163

def conflict_events(args = {})
  call_tool("get_conflict_events", args)
end

#country_brief(country_code, args = {}) ⇒ Object

AI strategic brief for a country (ISO 3166-1 alpha-2 code).



148
149
150
# File 'lib/worldmonitor.rb', line 148

def country_brief(country_code, args = {})
  call_tool("get_country_brief", args.merge(country_code: country_code))
end

#country_risk(country_code, args = {}) ⇒ Object

Country risk / resilience scores (ISO 3166-1 alpha-2 code).



153
154
155
# File 'lib/worldmonitor.rb', line 153

def country_risk(country_code, args = {})
  call_tool("get_country_risk", args.merge(country_code: country_code))
end

#cyber_threats(args = {}) ⇒ Object

Cyber-threat indicators (min_severity:, threat_type:, country: ...).



168
169
170
# File 'lib/worldmonitor.rb', line 168

def cyber_threats(args = {})
  call_tool("get_cyber_threats", args)
end

#forecast_predictions(args = {}) ⇒ Object

Scenario forecasts (domain:, region: ...).



188
189
190
# File 'lib/worldmonitor.rb', line 188

def forecast_predictions(args = {})
  call_tool("get_forecast_predictions", args)
end

#get(path, params = {}) ⇒ Object

GET a raw REST path (host-relative, e.g. "/api/health").

Raises:

  • (ArgumentError)


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/worldmonitor.rb', line 117

def get(path, params = {})
  raise ArgumentError, "get() needs a host-relative API path starting with '/'" unless path.start_with?("/")

  url = base_url + path
  query = stringify_keys(params)
  url += "?#{URI.encode_www_form(query.map { |k, v| [k, stringify_value(v)] })}" unless query.empty?
  status, content_type, body = @transport.call(
    { url: url, method: "GET", headers: headers(accept: "application/json") },
    timeout
  )
  value = self.class.parse_body(body, content_type)
  raise APIError.new(status, value) unless (200..299).cover?(status)

  value
end

#healthObject

API status / health check.



134
135
136
# File 'lib/worldmonitor.rb', line 134

def health
  get("/api/health")
end

#list_promptsObject

List MCP prompt templates (public).



107
108
109
# File 'lib/worldmonitor.rb', line 107

def list_prompts
  rpc("prompts/list")
end

#list_resourcesObject

List MCP resources (public).



112
113
114
# File 'lib/worldmonitor.rb', line 112

def list_resources
  rpc("resources/list")
end

#list_toolsObject

List every MCP tool (public - no key needed).



102
103
104
# File 'lib/worldmonitor.rb', line 102

def list_tools
  rpc("tools/list")
end

#maritime_activity(country_code, args = {}) ⇒ Object

Maritime / port activity for a country (ISO 3166-1 alpha-2 code).



193
194
195
# File 'lib/worldmonitor.rb', line 193

def maritime_activity(country_code, args = {})
  call_tool("get_maritime_activity", args.merge(country_code: country_code))
end

#market_data(args = {}) ⇒ Object

Equities, commodities, crypto and FX quotes.



158
159
160
# File 'lib/worldmonitor.rb', line 158

def market_data(args = {})
  call_tool("get_market_data", args)
end

#natural_disasters(args = {}) ⇒ Object

Earthquakes, fires and storms (dataset:, active_only:, min_magnitude: ...).



178
179
180
# File 'lib/worldmonitor.rb', line 178

def natural_disasters(args = {})
  call_tool("get_natural_disasters", args)
end

#news_intelligence(args = {}) ⇒ Object

Classified news intelligence (topic:, country:, alerts_only: ...).



173
174
175
# File 'lib/worldmonitor.rb', line 173

def news_intelligence(args = {})
  call_tool("get_news_intelligence", args)
end

#sanctions_data(args = {}) ⇒ Object

Sanctions designations (country:, entity_type:, query: ...).



183
184
185
# File 'lib/worldmonitor.rb', line 183

def sanctions_data(args = {})
  call_tool("get_sanctions_data", args)
end

#world_brief(args = {}) ⇒ Object

Live global situation brief.



143
144
145
# File 'lib/worldmonitor.rb', line 143

def world_brief(args = {})
  call_tool("get_world_brief", args)
end