@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 +118 -17
- package/dist/cli/auth.js +1 -1
- package/dist/cli/index.js +34 -13
- package/package.json +1 -1
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('
|
|
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
|
-
|
|
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 &&
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
115
|
-
|
|
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