offnadir-delta-mcp 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/catalog.js CHANGED
@@ -75,10 +75,10 @@ export const TOOLS = [
75
75
  minSeverity: { type: 'number', minimum: 0, maximum: 10, description: 'Keep only signals with severity_score >= this (0-10).' },
76
76
  escalating: { type: 'boolean', description: 'Keep only signals whose escalation_trend is "escalating".' },
77
77
  sort: { type: 'string', enum: ['severity', 'recent', 'sources'], description: "Result ordering. Omit for the feed's default ranking." },
78
- updatedSince: { type: 'string', description: 'Differential fetch: only signals (re)enriched at/after this ISO 8601 timestamp. Ignores the date window.' },
78
+ updatedSince: { type: 'string', description: 'Differential fetch: only signals (re)enriched at/after this ISO 8601 timestamp. Ignores the date window. Response signals carry last_updated_at.' },
79
79
  createdSince: { type: 'string', description: 'Differential fetch: only signals first enriched at/after this ISO 8601 timestamp.' },
80
- observability: { type: 'string', enum: ['observable', 'not-observable'], description: 'Keep only signals with this satellite observability (intelligence.satellite_observability).' },
81
- openData: { type: 'string', enum: ['sufficient', 'commercial-recommended', 'not-applicable'], description: 'Keep only signals with this open-data sufficiency (intelligence.open_data_sufficiency).' },
80
+ observability: { type: 'string', enum: ['observable', 'not-observable'], description: 'Keep only signals with this satellite observability — whether a physical mark is imageable at all (intelligence.satellite_observability).' },
81
+ openData: { type: 'string', enum: ['sufficient', 'commercial-recommended', 'not-applicable'], description: 'Keep only signals with this open-data sufficiency — free imagery is enough vs commercial tasking recommended (intelligence.open_data_sufficiency).' },
82
82
  minInformationGain: { type: 'number', minimum: 0, maximum: 1, description: 'Keep only signals whose intelligence.expected_information_gain >= this (0-1).' },
83
83
  responseFormat: { type: 'string', enum: ['concise', 'detailed'], description: 'Per-signal field detail. "concise" (default) returns key decision + GEOINT fields; "detailed" returns the full Signal object.' },
84
84
  },
@@ -115,7 +115,7 @@ export const TOOLS = [
115
115
  categories: { type: 'array', items: { type: 'string', enum: [...DELTA_CATEGORIES] }, description: 'Restrict to these categories. Omit for all.' },
116
116
  precision: { type: 'number', minimum: 0.1, maximum: 5, description: 'Grid cell size in decimal degrees. Defaults to 1.' },
117
117
  minSeverity: { type: 'number', minimum: 0, maximum: 10, description: 'Keep only points with severity_score >= this.' },
118
- limit: { type: 'integer', minimum: 1, maximum: SIGNALS_MAX_LIMIT, description: 'Max source points to bin. Defaults to 500.' },
118
+ limit: { type: 'integer', minimum: 1, maximum: SIGNALS_MAX_LIMIT, description: 'Max source points sampled before grid-binning — NOT the number of cells returned. Defaults to 500 (the max). Lower values sample fewer events and fragment clusters (each cell trends toward count 1), so leave at the default for a representative density map.' },
119
119
  },
120
120
  },
121
121
  },
@@ -137,7 +137,8 @@ export const TOOLS = [
137
137
  annotations: { readOnlyHint: true },
138
138
  description: "Check the calling key's remaining token balance and plan capabilities — monthly allocation, " +
139
139
  'tokens used this period, tokens remaining, and whether the plan includes AI tools over the API ' +
140
- '(assess_signal / ask_analyst). Use this to pre-flight a metered call. Free of token charges.',
140
+ '(assess_signal / ask_analyst). Use this to pre-flight a metered call: decide whether enough ' +
141
+ 'balance is left before spending. Free of token charges.',
141
142
  inputSchema: { type: 'object', properties: {} },
142
143
  },
143
144
  {
@@ -167,7 +168,10 @@ export const TOOLS = [
167
168
  description: 'Run an AI RS (remote-sensing) deep-dive assessment for a specific signal: what to observe, ' +
168
169
  'recommended sensors, and a collection window. `eventId` is the `id` from query_signals. ' +
169
170
  "Costs 5 (quick) or 15 (deep) tokens, charged to the key owner's balance. A prior assessment for " +
170
- 'the same signal is cached (no re-charge). The exact charge and remaining balance are in meta.tokens.',
171
+ 'the same signal is cached (no re-charge). The exact charge and remaining balance are in meta.tokens. ' +
172
+ 'Signals that are not satellite-observable (observability:"not-observable" — e.g. political statements ' +
173
+ 'or broad-area events with no imageable physical mark) are rejected BEFORE any charge, so pre-filter ' +
174
+ 'with query_signals observability:"observable" to spend only where imagery helps.',
171
175
  inputSchema: {
172
176
  type: 'object',
173
177
  properties: {
@@ -182,14 +186,17 @@ export const TOOLS = [
182
186
  annotations: { readOnlyHint: true },
183
187
  description: 'Ask the Delta Analyst an OSINT/GEOINT question. Runs an agentic multi-step analysis over the ' +
184
188
  'signal corpus and returns a structured brief (summary, findings with collection recommendations, ' +
185
- 'assessment, citations). Costs 5–45 tokens (usage-based; the exact charge and remaining balance are ' +
186
- 'in meta.tokens). Synchronous — may take up to ~120s. Not cached — each call is charged.',
189
+ 'assessment, citations). Costs 5–45 tokens (usage-based, metered by the compute the question actually ' +
190
+ 'uses; charged ONCE, when the run completes; the exact charge and remaining balance are in meta.tokens). ' +
191
+ 'Hybrid async: waits up to ~95s and returns the brief; a longer run returns {status:"processing", job_id} ' +
192
+ 'while finishing in the background — call again with the SAME idempotencyKey to fetch the finished brief ' +
193
+ 'without a second charge.',
187
194
  inputSchema: {
188
195
  type: 'object',
189
196
  properties: {
190
197
  question: { type: 'string', description: 'The analytic question (≤ 500 chars).' },
191
198
  bbox: { ...BBOX, description: 'Optional focus bounding box [minLon, minLat, maxLon, maxLat] (WGS84).' },
192
- idempotencyKey: { type: 'string', description: 'Optional at-most-once key. Retry with the SAME key after a timeout to avoid a second charge; use a fresh key to ask again.' },
199
+ idempotencyKey: { type: 'string', description: 'Optional at-most-once key. Re-sending the SAME key resolves to the SAME run: if it finished you get the brief with NO second charge; if it is still running you get its processing status. Strongly recommended — it makes a timeout recoverable. Use a fresh key to ask again.' },
193
200
  },
194
201
  required: ['question'],
195
202
  },
@@ -197,9 +204,10 @@ export const TOOLS = [
197
204
  ];
198
205
  export const RESOURCES = [
199
206
  { uri: 'brief://latest', name: 'Daily World Brief (latest)', description: 'The most recent AI-synthesized Daily World Brief (JSON). Free.', mimeType: 'application/json' },
200
- { uri: 'signals://schema', name: 'Signal object schema', description: 'JSON Schema of the public Signal shape returned by query_signals.', mimeType: 'application/json' },
207
+ { uri: 'signals://schema', name: 'Signal object schema', description: 'JSON Schema of the public Signal shape returned by query_signals / /api/v1/signals.', mimeType: 'application/json' },
201
208
  { uri: 'usage://current', name: 'API usage & quota', description: 'Remaining token balance and plan capabilities for the calling key. Free.', mimeType: 'application/json' },
202
209
  { uri: 'imagery://collections', name: 'Imagery collections', description: 'The satellite catalog collections searchable via search_imagery. Free.', mimeType: 'application/json' },
210
+ { uri: 'status://current', name: 'Data freshness & pipeline status', description: 'How current the data is (ingestion/enrichment frontier), the Daily World Brief status, and an Operational/Delayed/Degraded roll-up. Free.', mimeType: 'application/json' },
203
211
  ];
204
212
  export const RESOURCE_TEMPLATES = [
205
213
  { uriTemplate: 'brief://{date}', name: 'Daily World Brief by date', description: 'The Daily World Brief for a specific UTC date (YYYY-MM-DD). Free.', mimeType: 'application/json' },
@@ -212,7 +220,7 @@ export const PROMPTS = [
212
220
  },
213
221
  {
214
222
  name: 'assess-top-signal',
215
- description: 'Find the highest-severity recent signal in an area/category and run an RS assessment (Pro).',
223
+ description: 'Find the highest-severity recent signal in an area/category and run an RS assessment.',
216
224
  arguments: [
217
225
  { name: 'bbox', description: 'Bounding box "minLon,minLat,maxLon,maxLat" (optional).', required: false },
218
226
  { name: 'category', description: 'Category filter (optional).', required: false },
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
21
21
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
22
22
  import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
23
23
  import { PROMPTS, RESOURCES, RESOURCE_TEMPLATES, TOOLS } from './catalog.js';
24
- const VERSION = '1.3.1';
24
+ const VERSION = '1.3.3';
25
25
  const REMOTE_URL = new URL(process.env.OFFNADIR_DELTA_MCP_URL ?? 'https://offnadir-delta.com/api/v1/mcp');
26
26
  /** Lazily-connected client to the hosted remote MCP server. */
27
27
  let remote = null;
@@ -54,7 +54,7 @@ async function getRemote() {
54
54
  throw err;
55
55
  }
56
56
  }
57
- const server = new Server({ name: 'off-nadir-delta', version: VERSION }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
57
+ const server = new Server({ name: 'off-nadir-delta', title: 'Off-Nadir Delta', version: VERSION }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
58
58
  // --- Introspection: answered locally, no network, no credentials. ---
59
59
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
60
60
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: RESOURCES }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offnadir-delta-mcp",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Local (stdio) MCP server for Off-Nadir Delta — live, geolocated world-event intelligence, a Daily World Brief, satellite-imagery search, and an OSINT/GEOINT analyst. Forwards to the hosted server with your API key.",
5
5
  "type": "module",
6
6
  "bin": {