@globalfishingwatch/mcp 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -2,10 +2,15 @@
2
2
 
3
3
  Access [Global Fishing Watch](https://globalfishingwatch.org) data from any MCP-compatible AI assistant or directly from the terminal. Search vessels, retrieve fishing and port-visit events, look up Marine Protected Areas, Exclusive Economic Zones and RFMOs, calculate fishing activity hours within any region, and compute aggregate event statistics.
4
4
 
5
+ This package can be used in two modes:
6
+
7
+ - **MCP server** — connect any MCP-compatible AI assistant (Claude, Cursor, Windsurf, VS Code…) to GFW data
8
+ - **CLI** — query GFW data directly from the terminal
9
+
5
10
  ## Requirements
6
11
 
7
12
  - Node.js 18+
8
- - A [GFW API key](https://globalfishingwatch.org/our-apis/)
13
+ - A [GFW API key](https://globalfishingwatch.org/our-apis/) — if not yet available, request one at https://globalfishingwatch.org/our-apis/tokens
9
14
 
10
15
  ---
11
16
 
@@ -142,6 +147,18 @@ npm install && npm run build
142
147
 
143
148
  Then replace `npx -y gfw-mcp-js` with `node /absolute/path/to/gfw-mcp/dist/bin.js` in any config above.
144
149
 
150
+ ### Available MCP tools
151
+
152
+ | Tool | Description |
153
+ |------|-------------|
154
+ | `vessel-search` | Search vessels by name, MMSI, IMO, callsign, flag, or gear type |
155
+ | `vessel-by-id` | Fetch full vessel profile(s) by GFW vessel ID(s); returns metadata and a map URL |
156
+ | `vessel-events` | Retrieve fishing, encounter, port visit, or loitering events; filter by vessel, region, date, confidence, and encounter type |
157
+ | `events-stats` | Compute aggregate statistics (total events, unique vessels, flag breakdown) over a date range, optionally filtered by region and grouped by flag or gear type |
158
+ | `region-id-lookup` | Resolve MPA, EEZ, or RFMO names to canonical region IDs |
159
+ | `region-geometry` | Get the URL to fetch the GeoJSON geometry of a specific MPA, EEZ, or RFMO |
160
+ | `vessel-report` | Calculate fishing or presence hours in a region (MPA, EEZ, RFMO) with optional flag, gear type, vessel type, and speed filters; supports groupBy flag/geartype |
161
+
145
162
  ---
146
163
 
147
164
  ## CLI
@@ -188,6 +205,22 @@ GFW_TOKEN=your_key npx gfw-mcp-js vessel-search --name "Maria"
188
205
 
189
206
  Search vessels by name, MMSI, IMO, callsign, flag, or activity date range.
190
207
 
208
+ ```bash
209
+ npx gfw-mcp-js vessel-search [--name <name>] [--mmsi <mmsi>] [--imo <imo>]
210
+ [--callsign <cs>] [--flag <ISO3>] [--active-from <YYYY-MM-DD>]
211
+ [--active-to <YYYY-MM-DD>] [--limit <n>]
212
+ ```
213
+
214
+ At least one filter must be provided.
215
+
216
+ | Parameter | Format / values |
217
+ |-----------|----------------|
218
+ | `--mmsi` | 9-digit string |
219
+ | `--imo` | 7-digit string |
220
+ | `--flag` | ISO 3166-1 alpha-3 code (e.g. `ESP`, `CHN`, `USA`) |
221
+ | `--active-from` / `--active-to` | `YYYY-MM-DD` |
222
+ | `--limit` | 1–50 (default 10) |
223
+
191
224
  ```bash
192
225
  npx gfw-mcp-js vessel-search --name "Maria" --flag CHN
193
226
  npx gfw-mcp-js vessel-search --mmsi 123456789
@@ -198,6 +231,10 @@ npx gfw-mcp-js vessel-search --flag ESP --active-from 2024-01-01 --active-to 202
198
231
 
199
232
  Fetch full vessel profile(s) by GFW vessel ID.
200
233
 
234
+ ```bash
235
+ npx gfw-mcp-js vessel-by-id --ids <id> [<id2> ...]
236
+ ```
237
+
201
238
  ```bash
202
239
  npx gfw-mcp-js vessel-by-id --ids abc123
203
240
  npx gfw-mcp-js vessel-by-id --ids abc123 def456 ghi789
@@ -207,6 +244,24 @@ npx gfw-mcp-js vessel-by-id --ids abc123 def456 ghi789
207
244
 
208
245
  Retrieve fishing, encounter, port visit, or loitering events.
209
246
 
247
+ ```bash
248
+ npx gfw-mcp-js vessel-events --event-type <type>
249
+ --start-date <YYYY-MM-DD> --end-date <YYYY-MM-DD>
250
+ [--vessel-id <id>] [--limit <n>] [--offset <n>]
251
+ [--confidence <2|3|4> ...] # port_visit only
252
+ [--encounter-types <type> ...] # encounter only
253
+ [--region-type <MPA|EEZ|RFMO>] [--region-id <id>]
254
+ ```
255
+
256
+ | Parameter | Format / values |
257
+ |-----------|----------------|
258
+ | `--event-type` | `fishing` \| `encounter` \| `port_visit` \| `loitering` |
259
+ | `--start-date` / `--end-date` | `YYYY-MM-DD` |
260
+ | `--limit` | 1–100 (default 20) |
261
+ | `--confidence` | `2`, `3`, `4` (one or more; port_visit only; default `4`) |
262
+ | `--encounter-types` | `CARRIER-FISHING` \| `CARRIER-BUNKER` \| `FISHING-BUNKER` \| `FISHING-FISHING` \| `SUPPORT-FISHING` (encounter only; default `CARRIER-FISHING SUPPORT-FISHING`) |
263
+ | `--region-type` | `MPA` \| `EEZ` \| `RFMO` |
264
+
210
265
  ```bash
211
266
  npx gfw-mcp-js vessel-events --event-type fishing --start-date 2024-01-01 --end-date 2024-06-01
212
267
  npx gfw-mcp-js vessel-events --event-type port_visit --vessel-id abc123 --start-date 2024-01-01 --end-date 2024-12-31
@@ -218,6 +273,23 @@ npx gfw-mcp-js vessel-events --event-type fishing --region-type EEZ --region-id
218
273
 
219
274
  Compute aggregate event statistics over a date range.
220
275
 
276
+ ```bash
277
+ npx gfw-mcp-js events-stats --event-type <type>
278
+ --start-date <YYYY-MM-DD> --end-date <YYYY-MM-DD>
279
+ [--group-by <FLAG|GEARTYPE>]
280
+ [--region-type <MPA|EEZ|RFMO>] [--region-id <id>]
281
+ [--confidence <levels> ...] [--encounter-types <types> ...]
282
+ ```
283
+
284
+ | Parameter | Format / values |
285
+ |-----------|----------------|
286
+ | `--event-type` | `fishing` \| `encounter` \| `port_visit` \| `loitering` |
287
+ | `--start-date` / `--end-date` | `YYYY-MM-DD` |
288
+ | `--group-by` | `FLAG` \| `GEARTYPE` (default `FLAG`) |
289
+ | `--region-type` | `MPA` \| `EEZ` \| `RFMO` |
290
+ | `--confidence` | `2`, `3`, `4` (one or more; port_visit only; default `4`) |
291
+ | `--encounter-types` | `CARRIER-FISHING` \| `CARRIER-BUNKER` \| `FISHING-BUNKER` \| `FISHING-FISHING` \| `SUPPORT-FISHING` (encounter only; default `CARRIER-FISHING SUPPORT-FISHING`) |
292
+
221
293
  ```bash
222
294
  npx gfw-mcp-js events-stats --event-type fishing --start-date 2024-01-01 --end-date 2024-12-31
223
295
  npx gfw-mcp-js events-stats --event-type fishing --start-date 2024-01-01 --end-date 2024-12-31 --group-by GEARTYPE
@@ -228,6 +300,17 @@ npx gfw-mcp-js events-stats --event-type encounter --start-date 2024-01-01 --end
228
300
 
229
301
  Resolve an MPA, EEZ, or RFMO name to its canonical ID.
230
302
 
303
+ ```bash
304
+ npx gfw-mcp-js region-id-lookup --region-type <MPA|EEZ|RFMO> --query <name> [--limit <n>]
305
+ ```
306
+
307
+ Use this before `vessel-report` or `vessel-events` when you only know the human-readable name of a region.
308
+
309
+ | Parameter | Format / values |
310
+ |-----------|----------------|
311
+ | `--region-type` | `MPA` \| `EEZ` \| `RFMO` |
312
+ | `--limit` | 1–20 (default 5) |
313
+
231
314
  ```bash
232
315
  npx gfw-mcp-js region-id-lookup --region-type MPA --query "Galapagos"
233
316
  npx gfw-mcp-js region-id-lookup --region-type EEZ --query "Patagonia" --limit 10
@@ -236,7 +319,15 @@ npx gfw-mcp-js region-id-lookup --region-type RFMO --query "WCPFC"
236
319
 
237
320
  #### `region-geometry`
238
321
 
239
- Get the GeoJSON URL for a specific region.
322
+ Get the GeoJSON URL for a specific region (no API token required).
323
+
324
+ ```bash
325
+ npx gfw-mcp-js region-geometry --region-type <MPA|EEZ|RFMO> --id <id>
326
+ ```
327
+
328
+ | Parameter | Format / values |
329
+ |-----------|----------------|
330
+ | `--region-type` | `MPA` \| `EEZ` \| `RFMO` |
240
331
 
241
332
  ```bash
242
333
  npx gfw-mcp-js region-geometry --region-type EEZ --id 8386
@@ -245,7 +336,31 @@ npx gfw-mcp-js region-geometry --region-type MPA --id 12345
245
336
 
246
337
  #### `vessel-report`
247
338
 
248
- Calculate fishing or presence hours inside a region.
339
+ Calculate fishing or presence hours inside a region. Date range must not exceed 1 year.
340
+
341
+ > **Important:** This command must never be run in parallel. If multiple reports are needed, run them sequentially — one at a time, waiting for each to complete before starting the next.
342
+
343
+ ```bash
344
+ npx gfw-mcp-js vessel-report --region-type <MPA|EEZ|RFMO> --region-id <id>
345
+ --start-date <YYYY-MM-DD> --end-date <YYYY-MM-DD>
346
+ [--type <FISHING|PRESENCE>]
347
+ [--flags <ISO3> ...]
348
+ [--geartypes <type> ...] # FISHING only
349
+ [--vessel-types <type> ...] # PRESENCE only
350
+ [--speeds <range> ...] # PRESENCE only
351
+ [--group-by <VESSEL_ID|FLAG|GEARTYPE|FLAGANDGEARTYPE>]
352
+ ```
353
+
354
+ | Parameter | Format / values |
355
+ |-----------|----------------|
356
+ | `--region-type` | `MPA` \| `EEZ` \| `RFMO` |
357
+ | `--start-date` / `--end-date` | `YYYY-MM-DD` (max range: 1 year) |
358
+ | `--type` | `FISHING` (default) \| `PRESENCE` |
359
+ | `--flags` | ISO 3166-1 alpha-3 codes (e.g. `ESP`, `CHN`); up to 10 |
360
+ | `--geartypes` | `tuna_purse_seines` \| `driftnets` \| `trollers` \| `set_longlines` \| `purse_seines` \| `pots_and_traps` \| `other_fishing` \| `dredge_fishing` \| `set_gillnets` \| `fixed_gear` \| `trawlers` \| `fishing` \| `seiners` \| `other_purse_seines` \| `other_seines` \| `squid_jigger` \| `pole_and_line` \| `drifting_longlines` (FISHING only) |
361
+ | `--vessel-types` | `carrier` \| `seismic_vessel` \| `passenger` \| `other` \| `support` \| `bunker` \| `gear` \| `cargo` \| `fishing` \| `discrepancy` (PRESENCE only) |
362
+ | `--speeds` | `2-4` \| `4-6` \| `6-10` \| `10-15` \| `15-25` \| `>25` (PRESENCE only) |
363
+ | `--group-by` | `VESSEL_ID` (default) \| `FLAG` \| `GEARTYPE` \| `FLAGANDGEARTYPE` (`GEARTYPE`/`FLAGANDGEARTYPE` only valid with `--type FISHING`) |
249
364
 
250
365
  ```bash
251
366
  npx gfw-mcp-js vessel-report --region-type EEZ --region-id 8386 --start-date 2024-01-01 --end-date 2024-12-31
@@ -265,20 +380,6 @@ npx gfw-mcp-js vessel-report --region-type EEZ --region-id 8386 --start-date 202
265
380
 
266
381
  ---
267
382
 
268
- ## Available tools
269
-
270
- | Tool | Description |
271
- |------|-------------|
272
- | `vessel-search` | Search vessels by name, MMSI, IMO, callsign, flag, or gear type |
273
- | `vessel-by-id` | Fetch full vessel profile(s) by GFW vessel ID(s); returns metadata and a map URL |
274
- | `vessel-events` | Retrieve fishing, encounter, port visit, or loitering events; filter by vessel, region, date, confidence, and encounter type |
275
- | `events-stats` | Compute aggregate statistics (total events, unique vessels, flag breakdown) over a date range, optionally filtered by region and grouped by flag or gear type |
276
- | `region-id-lookup` | Resolve MPA, EEZ, or RFMO names to canonical region IDs |
277
- | `region-geometry` | Get the URL to fetch the GeoJSON geometry of a specific MPA, EEZ, or RFMO |
278
- | `vessel-report` | Calculate fishing or presence hours in a region (MPA, EEZ, RFMO) with optional flag, gear type, vessel type, and speed filters; supports groupBy flag/geartype |
279
-
280
- ---
281
-
282
383
  ## Environment variables
283
384
 
284
385
  | Variable | Default | Description |
package/dist/cli/auth.js CHANGED
@@ -43,7 +43,7 @@ function resolveToken() {
43
43
  async function authLogin() {
44
44
  const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
45
45
  const token = await new Promise((resolve) => {
46
- rl.question('Enter your GFW API token: ', (answer) => {
46
+ rl.question('Get your token at: https://globalfishingwatch.org/our-apis/tokens\nEnter your GFW API token: ', (answer) => {
47
47
  rl.close();
48
48
  resolve(answer.trim());
49
49
  });
package/dist/cli/index.js CHANGED
@@ -13,34 +13,49 @@ const vessel_report_js_1 = require("../tools/vessel-report.js");
13
13
  function print(data) {
14
14
  console.log(JSON.stringify(data, null, 2));
15
15
  }
16
- function fail(message) {
17
- console.error(`Error: ${message}`);
16
+ function fail(message, stdout = 'error') {
17
+ if (stdout === 'error') {
18
+ console.error(`Error: ${message}`);
19
+ }
20
+ else {
21
+ console.log(`Error: ${message}`);
22
+ }
18
23
  process.exit(1);
19
24
  }
25
+ const TOKEN_URL = 'https://globalfishingwatch.org/our-apis/tokens';
26
+ const TOKEN_HINT = `\n You need a GFW API token. Generate one at: ${TOKEN_URL}\n Then run: gfw auth login`;
27
+ function isAuthError(message) {
28
+ return /401|403|unauthorized|forbidden|no gfw api token/i.test(message);
29
+ }
20
30
  async function run(fn) {
21
31
  try {
22
32
  // Inject token into env so gfwFetch picks it up
23
33
  process.env.API_KEY = (0, auth_js_1.resolveToken)();
24
34
  const result = await fn();
25
- if (result && typeof result === 'object' && 'isError' in result && result.isError) {
26
- fail(result.content?.[0]?.text ?? 'Unknown error');
35
+ if (result &&
36
+ typeof result === 'object' &&
37
+ 'isError' in result &&
38
+ result.isError) {
39
+ const message = result.content?.[0]?.text ?? 'Unknown error';
40
+ fail(isAuthError(message) ? message + TOKEN_HINT : message);
27
41
  }
28
42
  print(result);
29
43
  }
30
44
  catch (err) {
31
- fail(err instanceof Error ? err.message : String(err));
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ fail(isAuthError(message) ? message + TOKEN_HINT : message, isAuthError(message) ? 'log' : 'error');
32
47
  }
33
48
  }
34
49
  const program = new commander_1.Command();
35
- program
36
- .name('gfw')
37
- .description('Global Fishing Watch CLI')
38
- .version('1.0.0');
50
+ program.name('gfw').description('Global Fishing Watch CLI').version('1.0.0');
39
51
  // ── auth ──────────────────────────────────────────────────────────────────────
40
52
  const auth = program.command('auth').description('Manage GFW API credentials');
41
53
  auth.command('login').description('Save a GFW API token').action(auth_js_1.authLogin);
42
54
  auth.command('logout').description('Remove stored token').action(auth_js_1.authLogout);
43
- auth.command('status').description('Show current token source').action(auth_js_1.authStatus);
55
+ auth
56
+ .command('status')
57
+ .description('Show current token source')
58
+ .action(auth_js_1.authStatus);
44
59
  // ── vessel-search ─────────────────────────────────────────────────────────────
45
60
  program
46
61
  .command('vessel-search')
@@ -91,7 +106,9 @@ program
91
106
  limit: opts.limit,
92
107
  offset: opts.offset,
93
108
  confidence: opts.confidence?.length ? opts.confidence : undefined,
94
- encounterTypes: opts.encounterTypes?.length ? opts.encounterTypes : undefined,
109
+ encounterTypes: opts.encounterTypes?.length
110
+ ? opts.encounterTypes
111
+ : undefined,
95
112
  regionType: opts.regionType,
96
113
  regionId: opts.regionId,
97
114
  })));
@@ -111,8 +128,12 @@ program
111
128
  eventType: opts.eventType,
112
129
  startDate: opts.startDate,
113
130
  endDate: opts.endDate,
114
- confidence: opts.confidence?.length ? opts.confidence.map(Number) : undefined,
115
- encounterTypes: opts.encounterTypes?.length ? opts.encounterTypes : undefined,
131
+ confidence: opts.confidence?.length
132
+ ? opts.confidence.map(Number)
133
+ : undefined,
134
+ encounterTypes: opts.encounterTypes?.length
135
+ ? opts.encounterTypes
136
+ : undefined,
116
137
  regionType: opts.regionType,
117
138
  regionId: opts.regionId,
118
139
  groupBy: opts.groupBy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalfishingwatch/mcp",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "MCP server for Global Fishing Watch data — vessel search, events, fishing hours, and region lookups",
5
5
  "author": "Global Fishing Watch",
6
6
  "license": "Apache-2.0",