vesselapi-mcp 1.2.0 → 2.0.0
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/CHANGELOG.md +65 -0
- package/README.md +32 -16
- package/dist/index.js +197 -112
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this server are documented here.
|
|
4
|
+
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [2.0.0] - 2026-08-13
|
|
7
|
+
|
|
8
|
+
Vessel ownership, classification and inspection data, and NAVTEX messages were
|
|
9
|
+
retired from the VesselAPI service on **10 August 2026**. There is no
|
|
10
|
+
replacement for them.
|
|
11
|
+
|
|
12
|
+
### Removed (breaking)
|
|
13
|
+
|
|
14
|
+
- `get_vessel_classification`
|
|
15
|
+
- `get_vessel_ownership`
|
|
16
|
+
- `get_vessel_inspections`
|
|
17
|
+
- `get_vessel_inspection_detail`
|
|
18
|
+
- `get_navtex_messages`
|
|
19
|
+
|
|
20
|
+
`search_vessels` no longer accepts `classSociety` or `owner`. The API rejects
|
|
21
|
+
both with HTTP 400.
|
|
22
|
+
|
|
23
|
+
**19 tools remain**, down from 24.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`q` on `search_vessels`.** Free-text search across name, IMO, MMSI, ENI and
|
|
28
|
+
callsign. The response reports which fields matched.
|
|
29
|
+
- **`eni` on `search_vessels`**, for inland waterway vessels.
|
|
30
|
+
- **`sat` on `get_vessel_position`.** Falls back to a satellite position when no
|
|
31
|
+
recent terrestrial one exists. Charged per call against a prepaid balance.
|
|
32
|
+
- **`timeFrom` and `timeTo` on `get_port_events`, `search_port_events_by_port`
|
|
33
|
+
and `search_port_events_by_vessel`.** Without them the service returns only
|
|
34
|
+
the last 2 hours.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **`get_vessels_in_radius` asked for the radius in nautical miles.** The API
|
|
39
|
+
takes metres, so a request for 20 nautical miles searched 20 metres and
|
|
40
|
+
returned nothing.
|
|
41
|
+
- **`get_port_inbound` required an ETA window.** It is now optional, and the
|
|
42
|
+
service defaults to the next 72 hours.
|
|
43
|
+
- **`idType` offered `vesselId`, which the API rejects with HTTP 400.** It now
|
|
44
|
+
accepts `imo` and `mmsi` only.
|
|
45
|
+
- **402 and 403 reported as generic API errors.** Out of satellite credits, a
|
|
46
|
+
suspended key and a feature outside the plan are now distinct conditions,
|
|
47
|
+
none worth retrying unchanged.
|
|
48
|
+
- **A missing `VESSELAPI_API_KEY` killed the process with a stack trace during
|
|
49
|
+
startup.** It now prints one line naming the variable.
|
|
50
|
+
- **Two version mismatches.** `server.json` declared 1.0.1 while the package was
|
|
51
|
+
1.3.0, and the server reported 1.0.0 in the MCP handshake whatever the
|
|
52
|
+
installed version.
|
|
53
|
+
- **Tool schemas now match what the API accepts.** `idType`, `eventType` and
|
|
54
|
+
`sortOrder` are enumerations, `limit` is bounded to 1 to 50, and the area
|
|
55
|
+
tools state the 4 hour maximum window.
|
|
56
|
+
|
|
57
|
+
### Requires
|
|
58
|
+
|
|
59
|
+
`vesselapi` 2.0.0 or later.
|
|
60
|
+
|
|
61
|
+
### Attribution
|
|
62
|
+
|
|
63
|
+
Emissions and casualty data: © European Union. Source: European Maritime Safety
|
|
64
|
+
Agency (EMSA), THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European
|
|
65
|
+
Marine Casualty Information Platform (EMCIP). See the README.
|
package/README.md
CHANGED
|
@@ -21,16 +21,19 @@ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that
|
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
24
|
-
- **
|
|
25
|
-
- Vessel search, positions
|
|
26
|
-
- Port search, details,
|
|
24
|
+
- **19 tools** covering vessels, ports, location search, and emissions
|
|
25
|
+
- Vessel search, positions (single and batch), ETA, emissions, and casualties
|
|
26
|
+
- Port search, details, port events (arrivals/departures), and global port event search
|
|
27
27
|
- Geographic vessel search (bounding box and radius)
|
|
28
|
-
- NAVTEX maritime safety messages
|
|
29
28
|
- Manual pagination to control API quota usage
|
|
30
29
|
|
|
30
|
+
## Hosted deployment
|
|
31
|
+
|
|
32
|
+
A hosted deployment is available on [Fronteir AI](https://fronteir.ai/mcp/vessel-api-vesselapi-mcp).
|
|
33
|
+
|
|
31
34
|
## Quick Start
|
|
32
35
|
|
|
33
|
-
No installation required
|
|
36
|
+
No installation required. Configure your AI client with `npx`:
|
|
34
37
|
|
|
35
38
|
```json
|
|
36
39
|
{
|
|
@@ -54,7 +57,7 @@ Add the JSON above to the config file for your client:
|
|
|
54
57
|
|---|---|
|
|
55
58
|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
|
|
56
59
|
| Cursor | `.cursor/mcp.json` or `~/.cursor/mcp.json` |
|
|
57
|
-
| Claude Code | `.
|
|
60
|
+
| Claude Code | `claude mcp add`, which writes `.mcp.json` in the project or `~/.claude.json` for user scope |
|
|
58
61
|
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
59
62
|
|
|
60
63
|
## Tools
|
|
@@ -63,15 +66,13 @@ Add the JSON above to the config file for your client:
|
|
|
63
66
|
|
|
64
67
|
| Tool | Description |
|
|
65
68
|
|---|---|
|
|
66
|
-
| `search_vessels` | Search vessels by name, IMO, MMSI, flag, type, callsign, year built
|
|
69
|
+
| `search_vessels` | Search vessels by name, IMO, MMSI, flag, type, callsign, or year built |
|
|
67
70
|
| `get_vessel` | Get detailed vessel information |
|
|
68
71
|
| `get_vessel_position` | Get current vessel position (lat/lon, speed, heading) |
|
|
69
72
|
| `get_vessel_eta` | Get vessel estimated time of arrival |
|
|
70
|
-
| `get_vessel_classification` | Get classification details (class society, surveys, hull) |
|
|
71
|
-
| `get_vessel_ownership` | Get ownership details (owner, manager, operator) |
|
|
72
73
|
| `get_vessel_emissions` | Get emissions data (CO2, fuel consumption) |
|
|
73
|
-
| `get_vessel_inspections` | Get port state control inspections |
|
|
74
74
|
| `get_vessel_casualties` | Get marine casualty records |
|
|
75
|
+
| `get_vessel_positions_batch` | Get positions for multiple vessels at once (with optional time range) |
|
|
75
76
|
|
|
76
77
|
### Port Tools
|
|
77
78
|
|
|
@@ -79,25 +80,31 @@ Add the JSON above to the config file for your client:
|
|
|
79
80
|
|---|---|
|
|
80
81
|
| `search_ports` | Search ports by name, country, type, size, region, harbor size, or harbor use |
|
|
81
82
|
| `get_port` | Get port details by UN/LOCODE |
|
|
83
|
+
| `get_port_inbound` | Get vessels inbound to a port within an ETA window |
|
|
82
84
|
| `get_port_events` | Get arrivals/departures for a port |
|
|
83
85
|
| `get_port_events_by_vessel` | Get port events for a vessel |
|
|
86
|
+
| `list_port_events` | List port events globally with filters for time, country, port, vessel, or event type |
|
|
87
|
+
| `search_port_events_by_port` | Search port events by port name |
|
|
88
|
+
| `search_port_events_by_vessel` | Search port events by vessel name |
|
|
89
|
+
| `get_vessel_last_port_event` | Get the most recent port event for a vessel |
|
|
84
90
|
|
|
85
|
-
###
|
|
91
|
+
### Emissions Tools
|
|
86
92
|
|
|
87
93
|
| Tool | Description |
|
|
88
94
|
|---|---|
|
|
89
|
-
| `
|
|
90
|
-
| `get_vessels_in_radius` | Find vessels within a radius of a point (with optional time range) |
|
|
95
|
+
| `list_emissions` | List global vessel emissions data with optional year filter |
|
|
91
96
|
|
|
92
|
-
###
|
|
97
|
+
### Location Tools
|
|
93
98
|
|
|
94
99
|
| Tool | Description |
|
|
95
100
|
|---|---|
|
|
96
|
-
| `
|
|
101
|
+
| `get_vessels_in_area` | Find vessels in a bounding box (with optional time range) |
|
|
102
|
+
| `get_vessels_in_radius` | Find vessels within a radius of a point (with optional time range) |
|
|
103
|
+
|
|
97
104
|
|
|
98
105
|
## Pagination
|
|
99
106
|
|
|
100
|
-
All list endpoints support `limit` and `nextToken` parameters for manual pagination. When more results exist, the response includes a `nextToken
|
|
107
|
+
All list endpoints support `limit` and `nextToken` parameters for manual pagination. When more results exist, the response includes a `nextToken`. Pass it in the next call to get the next page.
|
|
101
108
|
|
|
102
109
|
## Development
|
|
103
110
|
|
|
@@ -120,6 +127,15 @@ npm run clean # Remove build artifacts
|
|
|
120
127
|
VESSELAPI_API_KEY=your-key npx @modelcontextprotocol/inspector node dist/index.js
|
|
121
128
|
```
|
|
122
129
|
|
|
130
|
+
## Data Sources & Attribution
|
|
131
|
+
|
|
132
|
+
Emissions and casualty data: © European Union. Source: European Maritime Safety Agency
|
|
133
|
+
(EMSA): THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European Marine Casualty
|
|
134
|
+
Information Platform (EMCIP). Reused under the European Commission reuse notice
|
|
135
|
+
(Commission Decision 2011/833/EU), which authorises reuse for commercial and
|
|
136
|
+
non-commercial purposes with acknowledgement of the source. Data may be transformed and
|
|
137
|
+
combined; EMSA does not endorse this service.
|
|
138
|
+
|
|
123
139
|
## License
|
|
124
140
|
|
|
125
141
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { VesselClient } from "vesselapi";
|
|
7
7
|
|
|
8
|
+
// src/version.ts
|
|
9
|
+
var VERSION = "2.0.0";
|
|
10
|
+
|
|
8
11
|
// src/tools/location.ts
|
|
9
12
|
import { z } from "zod";
|
|
10
13
|
|
|
@@ -12,7 +15,9 @@ import { z } from "zod";
|
|
|
12
15
|
import {
|
|
13
16
|
VesselAPIError,
|
|
14
17
|
VesselAuthError,
|
|
18
|
+
VesselForbiddenError,
|
|
15
19
|
VesselNotFoundError,
|
|
20
|
+
VesselPaymentRequiredError,
|
|
16
21
|
VesselRateLimitError,
|
|
17
22
|
VesselServerError
|
|
18
23
|
} from "vesselapi";
|
|
@@ -24,15 +29,21 @@ function formatResult(data) {
|
|
|
24
29
|
function handleToolError(error) {
|
|
25
30
|
let message;
|
|
26
31
|
if (error instanceof VesselAuthError) {
|
|
27
|
-
message = "Authentication failed. Check your VESSELAPI_API_KEY.";
|
|
32
|
+
message = "Authentication failed. Check your VESSELAPI_API_KEY. Do not retry.";
|
|
33
|
+
} else if (error instanceof VesselPaymentRequiredError) {
|
|
34
|
+
message = "Out of satellite credits, and no stored position was available to fall back on. Retry without the satellite option to use stored data, or top up the balance at https://dashboard.vesselapi.com. Retrying the same request will not help.";
|
|
35
|
+
} else if (error instanceof VesselForbiddenError) {
|
|
36
|
+
message = error.code === "feature_not_available" ? "This endpoint is not included in the current plan. Do not retry; a different plan is required." : "The API key is suspended, usually for sustained quota abuse. Do not retry; check the account at https://dashboard.vesselapi.com.";
|
|
28
37
|
} else if (error instanceof VesselNotFoundError) {
|
|
29
|
-
message =
|
|
38
|
+
message = `Resource not found. Verify the ID or parameters. ${error.message}`;
|
|
30
39
|
} else if (error instanceof VesselRateLimitError) {
|
|
31
|
-
message = "Rate limit exceeded.
|
|
40
|
+
message = "Rate limit exceeded. Retrying later may succeed; reduce request frequency.";
|
|
32
41
|
} else if (error instanceof VesselServerError) {
|
|
33
|
-
message = `Server error: ${error.message}`;
|
|
42
|
+
message = `Server error, retrying later may succeed: ${error.message}`;
|
|
34
43
|
} else if (error instanceof VesselAPIError) {
|
|
35
|
-
|
|
44
|
+
const code = error.code ? ` (code: ${error.code})` : "";
|
|
45
|
+
const retry = error.statusCode >= 400 && error.statusCode < 500 ? " Do not retry without changing the request." : "";
|
|
46
|
+
message = `Request rejected${code}: ${error.message}.${retry}`;
|
|
36
47
|
} else if (error instanceof Error) {
|
|
37
48
|
message = error.message;
|
|
38
49
|
} else {
|
|
@@ -54,9 +65,9 @@ function registerLocationTools(server2, client2) {
|
|
|
54
65
|
latMax: z.number().describe("Northern boundary latitude"),
|
|
55
66
|
lonMin: z.number().describe("Western boundary longitude"),
|
|
56
67
|
lonMax: z.number().describe("Eastern boundary longitude"),
|
|
57
|
-
timeFrom: z.string().optional().describe("Start time
|
|
58
|
-
timeTo: z.string().optional().describe("End time
|
|
59
|
-
limit: z.number().optional().describe("
|
|
68
|
+
timeFrom: z.string().optional().describe("Start of the time window, RFC3339. Defaults to 2 hours ago. The window may not exceed 4 hours."),
|
|
69
|
+
timeTo: z.string().optional().describe("End of the time window, RFC3339. Defaults to now. The window may not exceed 4 hours."),
|
|
70
|
+
limit: z.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
60
71
|
nextToken: z.string().optional().describe("Pagination token from previous response")
|
|
61
72
|
},
|
|
62
73
|
async (params) => {
|
|
@@ -79,14 +90,14 @@ function registerLocationTools(server2, client2) {
|
|
|
79
90
|
);
|
|
80
91
|
server2.tool(
|
|
81
92
|
"get_vessels_in_radius",
|
|
82
|
-
"Find all vessels within a radius
|
|
93
|
+
"Find all vessels within a radius of a point. The radius is in METRES, not nautical miles or kilometres.",
|
|
83
94
|
{
|
|
84
95
|
latitude: z.number().describe("Center latitude"),
|
|
85
96
|
longitude: z.number().describe("Center longitude"),
|
|
86
|
-
radius: z.number().describe("
|
|
87
|
-
timeFrom: z.string().optional().describe("Start time
|
|
88
|
-
timeTo: z.string().optional().describe("End time
|
|
89
|
-
limit: z.number().optional().describe("
|
|
97
|
+
radius: z.number().describe("Search radius in METRES, maximum 100000 (100 km). One nautical mile is 1852 metres."),
|
|
98
|
+
timeFrom: z.string().optional().describe("Start of the time window, RFC3339. Defaults to 2 hours ago. The window may not exceed 4 hours."),
|
|
99
|
+
timeTo: z.string().optional().describe("End of the time window, RFC3339. Defaults to now. The window may not exceed 4 hours."),
|
|
100
|
+
limit: z.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
90
101
|
nextToken: z.string().optional().describe("Pagination token from previous response")
|
|
91
102
|
},
|
|
92
103
|
async (params) => {
|
|
@@ -122,7 +133,7 @@ function registerPortTools(server2, client2) {
|
|
|
122
133
|
region: z2.string().optional().describe("Geographic region (partial match)"),
|
|
123
134
|
harborSize: z2.string().optional().describe("Harbor size classification"),
|
|
124
135
|
harborUse: z2.string().optional().describe("Primary harbor use"),
|
|
125
|
-
limit: z2.number().optional().describe("
|
|
136
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
126
137
|
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
127
138
|
},
|
|
128
139
|
async (params) => {
|
|
@@ -164,11 +175,11 @@ function registerPortTools(server2, client2) {
|
|
|
164
175
|
"Get vessels heading to a specific port within an ETA arrival window",
|
|
165
176
|
{
|
|
166
177
|
unlocode: z2.string().describe("UN/LOCODE of the destination port (e.g. NLRTM for Rotterdam)"),
|
|
167
|
-
etaFrom: z2.string().describe("Start of ETA arrival window (RFC3339
|
|
168
|
-
etaTo: z2.string().describe("End of ETA arrival window (RFC3339
|
|
178
|
+
etaFrom: z2.string().optional().describe("Start of ETA arrival window (RFC3339). Omit to default to now."),
|
|
179
|
+
etaTo: z2.string().optional().describe("End of ETA arrival window (RFC3339). Omit to default to 72 hours ahead."),
|
|
169
180
|
timeFrom: z2.string().optional().describe("AIS position time range start (RFC3339 format)"),
|
|
170
181
|
timeTo: z2.string().optional().describe("AIS position time range end (RFC3339 format)"),
|
|
171
|
-
limit: z2.number().optional().describe("
|
|
182
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
172
183
|
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
173
184
|
},
|
|
174
185
|
async (params) => {
|
|
@@ -189,15 +200,19 @@ function registerPortTools(server2, client2) {
|
|
|
189
200
|
);
|
|
190
201
|
server2.tool(
|
|
191
202
|
"get_port_events",
|
|
192
|
-
"Get port events (arrivals/departures) for a specific port",
|
|
203
|
+
"Get port events (arrivals/departures) for a specific port. Covers only the last 2 hours unless timeFrom is given.",
|
|
193
204
|
{
|
|
194
205
|
unlocode: z2.string().describe("UN/LOCODE of the port"),
|
|
195
|
-
|
|
206
|
+
timeFrom: z2.string().optional().describe("Start of the time window, RFC3339. Without it the service returns only the last 2 hours."),
|
|
207
|
+
timeTo: z2.string().optional().describe("End of the time window, RFC3339. Defaults to now."),
|
|
208
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
196
209
|
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
197
210
|
},
|
|
198
211
|
async (params) => {
|
|
199
212
|
try {
|
|
200
213
|
const data = await client2.portEvents.byPort(params.unlocode, {
|
|
214
|
+
timeFrom: params.timeFrom,
|
|
215
|
+
timeTo: params.timeTo,
|
|
201
216
|
paginationLimit: params.limit,
|
|
202
217
|
paginationNextToken: params.nextToken
|
|
203
218
|
});
|
|
@@ -212,12 +227,12 @@ function registerPortTools(server2, client2) {
|
|
|
212
227
|
"Get port events (arrivals/departures) for a specific vessel",
|
|
213
228
|
{
|
|
214
229
|
vesselId: z2.string().describe("Vessel identifier (IMO number by default)"),
|
|
215
|
-
idType: z2.
|
|
216
|
-
eventType: z2.
|
|
217
|
-
sortOrder: z2.
|
|
230
|
+
idType: z2.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi"),
|
|
231
|
+
eventType: z2.enum(["arrival", "departure", "all"]).optional().describe("Filter by event type. Omit for both arrivals and departures"),
|
|
232
|
+
sortOrder: z2.enum(["asc", "desc"]).optional().describe("Sort order by timestamp"),
|
|
218
233
|
timeFrom: z2.string().optional().describe("Start time (ISO 8601 format)"),
|
|
219
234
|
timeTo: z2.string().optional().describe("End time (ISO 8601 format)"),
|
|
220
|
-
limit: z2.number().optional().describe("
|
|
235
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
221
236
|
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
222
237
|
},
|
|
223
238
|
async (params) => {
|
|
@@ -237,25 +252,30 @@ function registerPortTools(server2, client2) {
|
|
|
237
252
|
}
|
|
238
253
|
}
|
|
239
254
|
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// src/tools/safety.ts
|
|
243
|
-
import { z as z3 } from "zod";
|
|
244
|
-
function registerSafetyTools(server2, client2) {
|
|
245
255
|
server2.tool(
|
|
246
|
-
"
|
|
247
|
-
"
|
|
256
|
+
"list_port_events",
|
|
257
|
+
"List port events (arrivals/departures) globally with optional filters for time, country, port, vessel, or event type",
|
|
248
258
|
{
|
|
249
|
-
timeFrom:
|
|
250
|
-
timeTo:
|
|
251
|
-
|
|
252
|
-
|
|
259
|
+
timeFrom: z2.string().optional().describe("Start time (RFC3339 format)"),
|
|
260
|
+
timeTo: z2.string().optional().describe("End time (RFC3339 format)"),
|
|
261
|
+
country: z2.string().optional().describe("Filter by port country (case-insensitive)"),
|
|
262
|
+
unlocode: z2.string().optional().describe("Filter by port UN/LOCODE"),
|
|
263
|
+
eventType: z2.enum(["arrival", "departure", "all"]).optional().describe("Filter by event type. Omit for both arrivals and departures"),
|
|
264
|
+
vesselName: z2.string().optional().describe("Filter by vessel name (full-text search)"),
|
|
265
|
+
portName: z2.string().optional().describe("Filter by port name (full-text search)"),
|
|
266
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
267
|
+
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
253
268
|
},
|
|
254
269
|
async (params) => {
|
|
255
270
|
try {
|
|
256
|
-
const data = await client2.
|
|
271
|
+
const data = await client2.portEvents.list({
|
|
257
272
|
timeFrom: params.timeFrom,
|
|
258
273
|
timeTo: params.timeTo,
|
|
274
|
+
filterCountry: params.country,
|
|
275
|
+
filterUnlocode: params.unlocode,
|
|
276
|
+
filterEventType: params.eventType,
|
|
277
|
+
filterVesselName: params.vesselName,
|
|
278
|
+
filterPortName: params.portName,
|
|
259
279
|
paginationLimit: params.limit,
|
|
260
280
|
paginationNextToken: params.nextToken
|
|
261
281
|
});
|
|
@@ -265,41 +285,22 @@ function registerSafetyTools(server2, client2) {
|
|
|
265
285
|
}
|
|
266
286
|
}
|
|
267
287
|
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// src/tools/vessels.ts
|
|
271
|
-
import { z as z4 } from "zod";
|
|
272
|
-
function registerVesselTools(server2, client2) {
|
|
273
288
|
server2.tool(
|
|
274
|
-
"
|
|
275
|
-
"Search
|
|
289
|
+
"search_port_events_by_port",
|
|
290
|
+
"Search port events by port name. Covers only the last 2 hours unless timeFrom is given.",
|
|
276
291
|
{
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
callsign: z4.string().optional().describe("Radio callsign"),
|
|
283
|
-
yearBuiltMin: z4.number().optional().describe("Minimum year built"),
|
|
284
|
-
yearBuiltMax: z4.number().optional().describe("Maximum year built"),
|
|
285
|
-
classSociety: z4.string().optional().describe("Classification society (case-insensitive)"),
|
|
286
|
-
owner: z4.string().optional().describe("Owner name (partial match, case-insensitive)"),
|
|
287
|
-
limit: z4.number().optional().describe("Max results per page"),
|
|
288
|
-
nextToken: z4.string().optional().describe("Pagination token from previous response")
|
|
292
|
+
portName: z2.string().describe("Port name to search for"),
|
|
293
|
+
timeFrom: z2.string().optional().describe("Start of the time window, RFC3339. Without it the service returns only the last 2 hours."),
|
|
294
|
+
timeTo: z2.string().optional().describe("End of the time window, RFC3339. Defaults to now."),
|
|
295
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
296
|
+
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
289
297
|
},
|
|
290
298
|
async (params) => {
|
|
291
299
|
try {
|
|
292
|
-
const data = await client2.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
filterFlag: params.flag,
|
|
297
|
-
filterVesselType: params.vesselType,
|
|
298
|
-
filterCallsign: params.callsign,
|
|
299
|
-
filterYearBuiltMin: params.yearBuiltMin,
|
|
300
|
-
filterYearBuiltMax: params.yearBuiltMax,
|
|
301
|
-
filterClassSociety: params.classSociety,
|
|
302
|
-
filterOwner: params.owner,
|
|
300
|
+
const data = await client2.portEvents.byPorts({
|
|
301
|
+
filterPortName: params.portName,
|
|
302
|
+
timeFrom: params.timeFrom,
|
|
303
|
+
timeTo: params.timeTo,
|
|
303
304
|
paginationLimit: params.limit,
|
|
304
305
|
paginationNextToken: params.nextToken
|
|
305
306
|
});
|
|
@@ -310,16 +311,23 @@ function registerVesselTools(server2, client2) {
|
|
|
310
311
|
}
|
|
311
312
|
);
|
|
312
313
|
server2.tool(
|
|
313
|
-
"
|
|
314
|
-
"
|
|
314
|
+
"search_port_events_by_vessel",
|
|
315
|
+
"Search port events by vessel name. Covers only the last 2 hours unless timeFrom is given.",
|
|
315
316
|
{
|
|
316
|
-
|
|
317
|
-
|
|
317
|
+
vesselName: z2.string().describe("Vessel name to search for"),
|
|
318
|
+
timeFrom: z2.string().optional().describe("Start of the time window, RFC3339. Without it the service returns only the last 2 hours."),
|
|
319
|
+
timeTo: z2.string().optional().describe("End of the time window, RFC3339. Defaults to now."),
|
|
320
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
321
|
+
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
318
322
|
},
|
|
319
323
|
async (params) => {
|
|
320
324
|
try {
|
|
321
|
-
const data = await client2.
|
|
322
|
-
|
|
325
|
+
const data = await client2.portEvents.byVessels({
|
|
326
|
+
filterVesselName: params.vesselName,
|
|
327
|
+
timeFrom: params.timeFrom,
|
|
328
|
+
timeTo: params.timeTo,
|
|
329
|
+
paginationLimit: params.limit,
|
|
330
|
+
paginationNextToken: params.nextToken
|
|
323
331
|
});
|
|
324
332
|
return formatResult(data);
|
|
325
333
|
} catch (error) {
|
|
@@ -328,15 +336,15 @@ function registerVesselTools(server2, client2) {
|
|
|
328
336
|
}
|
|
329
337
|
);
|
|
330
338
|
server2.tool(
|
|
331
|
-
"
|
|
332
|
-
"Get the
|
|
339
|
+
"get_vessel_last_port_event",
|
|
340
|
+
"Get the most recent port event (arrival or departure) for a vessel",
|
|
333
341
|
{
|
|
334
|
-
vesselId:
|
|
335
|
-
idType:
|
|
342
|
+
vesselId: z2.string().describe("Vessel identifier (IMO number by default)"),
|
|
343
|
+
idType: z2.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi")
|
|
336
344
|
},
|
|
337
345
|
async (params) => {
|
|
338
346
|
try {
|
|
339
|
-
const data = await client2.
|
|
347
|
+
const data = await client2.portEvents.lastByVessel(params.vesselId, {
|
|
340
348
|
filterIdType: params.idType
|
|
341
349
|
});
|
|
342
350
|
return formatResult(data);
|
|
@@ -346,15 +354,80 @@ function registerVesselTools(server2, client2) {
|
|
|
346
354
|
}
|
|
347
355
|
);
|
|
348
356
|
server2.tool(
|
|
349
|
-
"
|
|
350
|
-
"
|
|
357
|
+
"list_emissions",
|
|
358
|
+
"List global vessel emissions data with optional year filter",
|
|
351
359
|
{
|
|
352
|
-
|
|
353
|
-
|
|
360
|
+
period: z2.number().optional().describe("Reporting year filter (e.g. 2024)"),
|
|
361
|
+
limit: z2.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
362
|
+
nextToken: z2.string().optional().describe("Pagination token from previous response")
|
|
354
363
|
},
|
|
355
364
|
async (params) => {
|
|
356
365
|
try {
|
|
357
|
-
const data = await client2.
|
|
366
|
+
const data = await client2.emissions.list({
|
|
367
|
+
filterPeriod: params.period,
|
|
368
|
+
paginationLimit: params.limit,
|
|
369
|
+
paginationNextToken: params.nextToken
|
|
370
|
+
});
|
|
371
|
+
return formatResult(data);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
return handleToolError(error);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/tools/vessels.ts
|
|
380
|
+
import { z as z3 } from "zod";
|
|
381
|
+
function registerVesselTools(server2, client2) {
|
|
382
|
+
server2.tool(
|
|
383
|
+
"search_vessels",
|
|
384
|
+
"Search for vessels. Use q when you have an identifier but do not know which kind it is; use the specific filters to narrow a fleet.",
|
|
385
|
+
{
|
|
386
|
+
q: z3.string().optional().describe("Free-text search across name, IMO, MMSI, ENI and callsign. Use this when the type of identifier is unknown."),
|
|
387
|
+
name: z3.string().optional().describe("Vessel name (partial match)"),
|
|
388
|
+
imo: z3.string().optional().describe("IMO number"),
|
|
389
|
+
mmsi: z3.string().optional().describe("MMSI number"),
|
|
390
|
+
eni: z3.string().optional().describe("ENI number, used for inland waterway vessels"),
|
|
391
|
+
flag: z3.string().optional().describe("Flag state (ISO country code)"),
|
|
392
|
+
vesselType: z3.string().optional().describe("Vessel type"),
|
|
393
|
+
callsign: z3.string().optional().describe("Radio callsign"),
|
|
394
|
+
yearBuiltMin: z3.number().optional().describe("Minimum year built"),
|
|
395
|
+
yearBuiltMax: z3.number().optional().describe("Maximum year built"),
|
|
396
|
+
limit: z3.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
397
|
+
nextToken: z3.string().optional().describe("Pagination token from previous response")
|
|
398
|
+
},
|
|
399
|
+
async (params) => {
|
|
400
|
+
try {
|
|
401
|
+
const data = await client2.search.vessels({
|
|
402
|
+
q: params.q,
|
|
403
|
+
filterEni: params.eni,
|
|
404
|
+
filterName: params.name,
|
|
405
|
+
filterImo: params.imo,
|
|
406
|
+
filterMmsi: params.mmsi,
|
|
407
|
+
filterFlag: params.flag,
|
|
408
|
+
filterVesselType: params.vesselType,
|
|
409
|
+
filterCallsign: params.callsign,
|
|
410
|
+
filterYearBuiltMin: params.yearBuiltMin,
|
|
411
|
+
filterYearBuiltMax: params.yearBuiltMax,
|
|
412
|
+
paginationLimit: params.limit,
|
|
413
|
+
paginationNextToken: params.nextToken
|
|
414
|
+
});
|
|
415
|
+
return formatResult(data);
|
|
416
|
+
} catch (error) {
|
|
417
|
+
return handleToolError(error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
server2.tool(
|
|
422
|
+
"get_vessel",
|
|
423
|
+
"Get detailed information about a specific vessel",
|
|
424
|
+
{
|
|
425
|
+
vesselId: z3.string().describe("Vessel identifier (IMO number by default)"),
|
|
426
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi")
|
|
427
|
+
},
|
|
428
|
+
async (params) => {
|
|
429
|
+
try {
|
|
430
|
+
const data = await client2.vessels.get(params.vesselId, {
|
|
358
431
|
filterIdType: params.idType
|
|
359
432
|
});
|
|
360
433
|
return formatResult(data);
|
|
@@ -364,15 +437,17 @@ function registerVesselTools(server2, client2) {
|
|
|
364
437
|
}
|
|
365
438
|
);
|
|
366
439
|
server2.tool(
|
|
367
|
-
"
|
|
368
|
-
"Get the
|
|
440
|
+
"get_vessel_position",
|
|
441
|
+
"Get the current position of a vessel (latitude, longitude, speed, heading)",
|
|
369
442
|
{
|
|
370
|
-
vesselId:
|
|
371
|
-
idType:
|
|
443
|
+
vesselId: z3.string().describe("Vessel identifier (IMO number by default)"),
|
|
444
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi"),
|
|
445
|
+
sat: z3.boolean().optional().describe("Fall back to a satellite position when no recent terrestrial one exists. Charged per call against a prepaid balance, so use it only when a stored position is genuinely insufficient.")
|
|
372
446
|
},
|
|
373
447
|
async (params) => {
|
|
374
448
|
try {
|
|
375
|
-
const data = await client2.vessels.
|
|
449
|
+
const data = await client2.vessels.position(params.vesselId, {
|
|
450
|
+
filterSat: params.sat,
|
|
376
451
|
filterIdType: params.idType
|
|
377
452
|
});
|
|
378
453
|
return formatResult(data);
|
|
@@ -382,15 +457,15 @@ function registerVesselTools(server2, client2) {
|
|
|
382
457
|
}
|
|
383
458
|
);
|
|
384
459
|
server2.tool(
|
|
385
|
-
"
|
|
386
|
-
"Get the
|
|
460
|
+
"get_vessel_eta",
|
|
461
|
+
"Get the estimated time of arrival for a vessel",
|
|
387
462
|
{
|
|
388
|
-
vesselId:
|
|
389
|
-
idType:
|
|
463
|
+
vesselId: z3.string().describe("Vessel identifier (IMO number by default)"),
|
|
464
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi")
|
|
390
465
|
},
|
|
391
466
|
async (params) => {
|
|
392
467
|
try {
|
|
393
|
-
const data = await client2.vessels.
|
|
468
|
+
const data = await client2.vessels.eta(params.vesselId, {
|
|
394
469
|
filterIdType: params.idType
|
|
395
470
|
});
|
|
396
471
|
return formatResult(data);
|
|
@@ -403,10 +478,10 @@ function registerVesselTools(server2, client2) {
|
|
|
403
478
|
"get_vessel_emissions",
|
|
404
479
|
"Get emissions data for a vessel (CO2, fuel consumption)",
|
|
405
480
|
{
|
|
406
|
-
vesselId:
|
|
407
|
-
idType:
|
|
408
|
-
limit:
|
|
409
|
-
nextToken:
|
|
481
|
+
vesselId: z3.string().describe("Vessel identifier (IMO number by default)"),
|
|
482
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi"),
|
|
483
|
+
limit: z3.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
484
|
+
nextToken: z3.string().optional().describe("Pagination token from previous response")
|
|
410
485
|
},
|
|
411
486
|
async (params) => {
|
|
412
487
|
try {
|
|
@@ -422,17 +497,17 @@ function registerVesselTools(server2, client2) {
|
|
|
422
497
|
}
|
|
423
498
|
);
|
|
424
499
|
server2.tool(
|
|
425
|
-
"
|
|
426
|
-
"Get
|
|
500
|
+
"get_vessel_casualties",
|
|
501
|
+
"Get marine casualty records for a vessel",
|
|
427
502
|
{
|
|
428
|
-
vesselId:
|
|
429
|
-
idType:
|
|
430
|
-
limit:
|
|
431
|
-
nextToken:
|
|
503
|
+
vesselId: z3.string().describe("Vessel identifier (IMO number by default)"),
|
|
504
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi"),
|
|
505
|
+
limit: z3.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
506
|
+
nextToken: z3.string().optional().describe("Pagination token from previous response")
|
|
432
507
|
},
|
|
433
508
|
async (params) => {
|
|
434
509
|
try {
|
|
435
|
-
const data = await client2.vessels.
|
|
510
|
+
const data = await client2.vessels.casualties(params.vesselId, {
|
|
436
511
|
filterIdType: params.idType,
|
|
437
512
|
paginationLimit: params.limit,
|
|
438
513
|
paginationNextToken: params.nextToken
|
|
@@ -444,18 +519,23 @@ function registerVesselTools(server2, client2) {
|
|
|
444
519
|
}
|
|
445
520
|
);
|
|
446
521
|
server2.tool(
|
|
447
|
-
"
|
|
448
|
-
"Get
|
|
522
|
+
"get_vessel_positions_batch",
|
|
523
|
+
"Get positions for multiple vessels at once by MMSI or IMO numbers",
|
|
449
524
|
{
|
|
450
|
-
|
|
451
|
-
idType:
|
|
452
|
-
|
|
453
|
-
|
|
525
|
+
ids: z3.string().describe("Comma-separated list of MMSI or IMO numbers"),
|
|
526
|
+
idType: z3.enum(["imo", "mmsi"]).optional().describe("Identifier type: imo (default) or mmsi"),
|
|
527
|
+
timeFrom: z3.string().optional().describe("Start time filter in RFC3339 format (defaults to 2 hours ago)"),
|
|
528
|
+
timeTo: z3.string().optional().describe("End time filter in RFC3339 format (defaults to current time)"),
|
|
529
|
+
limit: z3.number().int().min(1).max(50).optional().describe("Results per page, 1 to 50. Defaults to 20."),
|
|
530
|
+
nextToken: z3.string().optional().describe("Pagination token from previous response")
|
|
454
531
|
},
|
|
455
532
|
async (params) => {
|
|
456
533
|
try {
|
|
457
|
-
const data = await client2.vessels.
|
|
534
|
+
const data = await client2.vessels.positions({
|
|
535
|
+
filterIds: params.ids,
|
|
458
536
|
filterIdType: params.idType,
|
|
537
|
+
timeFrom: params.timeFrom,
|
|
538
|
+
timeTo: params.timeTo,
|
|
459
539
|
paginationLimit: params.limit,
|
|
460
540
|
paginationNextToken: params.nextToken
|
|
461
541
|
});
|
|
@@ -468,15 +548,20 @@ function registerVesselTools(server2, client2) {
|
|
|
468
548
|
}
|
|
469
549
|
|
|
470
550
|
// src/index.ts
|
|
471
|
-
|
|
551
|
+
if (!process.env.VESSELAPI_API_KEY) {
|
|
552
|
+
process.stderr.write(
|
|
553
|
+
"vesselapi-mcp: VESSELAPI_API_KEY is not set. Add it to the env block of your MCP client configuration. Keys are issued at https://dashboard.vesselapi.com\n"
|
|
554
|
+
);
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}
|
|
557
|
+
var client = new VesselClient(void 0, { userAgent: `vesselapi-mcp/${VERSION}` });
|
|
472
558
|
var server = new McpServer({
|
|
473
559
|
name: "vesselapi",
|
|
474
|
-
version:
|
|
560
|
+
version: VERSION
|
|
475
561
|
});
|
|
476
562
|
registerVesselTools(server, client);
|
|
477
563
|
registerPortTools(server, client);
|
|
478
564
|
registerLocationTools(server, client);
|
|
479
|
-
registerSafetyTools(server, client);
|
|
480
565
|
var transport = new StdioServerTransport();
|
|
481
566
|
await server.connect(transport);
|
|
482
567
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/tools/location.ts","../src/errors.ts","../src/tools/ports.ts","../src/tools/safety.ts","../src/tools/vessels.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { VesselClient } from \"vesselapi\";\nimport { registerLocationTools } from \"./tools/location.js\";\nimport { registerPortTools } from \"./tools/ports.js\";\nimport { registerSafetyTools } from \"./tools/safety.js\";\nimport { registerVesselTools } from \"./tools/vessels.js\";\n\nconst client = new VesselClient();\n\nconst server = new McpServer({\n name: \"vesselapi\",\n version: \"1.0.0\",\n});\n\nregisterVesselTools(server, client);\nregisterPortTools(server, client);\nregisterLocationTools(server, client);\nregisterSafetyTools(server, client);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerLocationTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"get_vessels_in_area\",\n \"Find all vessels within a rectangular bounding box (latitude/longitude)\",\n {\n latMin: z.number().describe(\"Southern boundary latitude\"),\n latMax: z.number().describe(\"Northern boundary latitude\"),\n lonMin: z.number().describe(\"Western boundary longitude\"),\n lonMax: z.number().describe(\"Eastern boundary longitude\"),\n timeFrom: z.string().optional().describe(\"Start time filter in RFC3339 format (defaults to 2 hours ago)\"),\n timeTo: z.string().optional().describe(\"End time filter in RFC3339 format (defaults to current time)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.location.vesselsBoundingBox({\n latMin: params.latMin,\n latMax: params.latMax,\n lonMin: params.lonMin,\n lonMax: params.lonMax,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessels_in_radius\",\n \"Find all vessels within a radius (in nautical miles) of a point\",\n {\n latitude: z.number().describe(\"Center latitude\"),\n longitude: z.number().describe(\"Center longitude\"),\n radius: z.number().describe(\"Radius in nautical miles\"),\n timeFrom: z.string().optional().describe(\"Start time filter in RFC3339 format (defaults to 2 hours ago)\"),\n timeTo: z.string().optional().describe(\"End time filter in RFC3339 format (defaults to current time)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.location.vesselsRadius({\n latitude: params.latitude,\n longitude: params.longitude,\n radius: params.radius,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n","import {\n VesselAPIError,\n VesselAuthError,\n VesselNotFoundError,\n VesselRateLimitError,\n VesselServerError,\n} from \"vesselapi\";\n\nexport function formatResult(data: unknown): { content: { type: \"text\"; text: string }[] } {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n}\n\nexport function handleToolError(error: unknown): { isError: true; content: { type: \"text\"; text: string }[] } {\n let message: string;\n\n if (error instanceof VesselAuthError) {\n message = \"Authentication failed. Check your VESSELAPI_API_KEY.\";\n } else if (error instanceof VesselNotFoundError) {\n message = \"Resource not found. Verify the ID or parameters.\";\n } else if (error instanceof VesselRateLimitError) {\n message = \"Rate limit exceeded. Try again later or reduce request frequency.\";\n } else if (error instanceof VesselServerError) {\n message = `Server error: ${error.message}`;\n } else if (error instanceof VesselAPIError) {\n message = `API error: ${error.message}`;\n } else if (error instanceof Error) {\n message = error.message;\n } else {\n message = String(error);\n }\n\n return {\n isError: true,\n content: [{ type: \"text\", text: message }],\n };\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerPortTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"search_ports\",\n \"Search for ports by name, country, type, size, region, harbor size, or harbor use\",\n {\n name: z.string().optional().describe(\"Port name (partial match)\"),\n country: z.string().optional().describe(\"Country (ISO code)\"),\n portType: z.string().optional().describe(\"Port type classification\"),\n size: z.string().optional().describe(\"Port size classification\"),\n region: z.string().optional().describe(\"Geographic region (partial match)\"),\n harborSize: z.string().optional().describe(\"Harbor size classification\"),\n harborUse: z.string().optional().describe(\"Primary harbor use\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.search.ports({\n filterName: params.name,\n filterCountry: params.country,\n filterPortType: params.portType,\n filterSize: params.size,\n filterRegion: params.region,\n filterHarborSize: params.harborSize,\n filterHarborUse: params.harborUse,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port\",\n \"Get detailed information about a specific port by UN/LOCODE\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the port (e.g. NLRTM for Rotterdam)\"),\n },\n async (params) => {\n try {\n const data = await client.ports.get(params.unlocode);\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_inbound\",\n \"Get vessels heading to a specific port within an ETA arrival window\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the destination port (e.g. NLRTM for Rotterdam)\"),\n etaFrom: z.string().describe(\"Start of ETA arrival window (RFC3339 format, e.g. 2026-03-07T00:00:00Z)\"),\n etaTo: z.string().describe(\"End of ETA arrival window (RFC3339 format, e.g. 2026-03-14T00:00:00Z)\"),\n timeFrom: z.string().optional().describe(\"AIS position time range start (RFC3339 format)\"),\n timeTo: z.string().optional().describe(\"AIS position time range end (RFC3339 format)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.ports.inbound(params.unlocode, {\n filterEtaFrom: params.etaFrom,\n filterEtaTo: params.etaTo,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_events\",\n \"Get port events (arrivals/departures) for a specific port\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the port\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byPort(params.unlocode, {\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_events_by_vessel\",\n \"Get port events (arrivals/departures) for a specific vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n eventType: z.string().optional().describe(\"Filter by event type (arrival, departure)\"),\n sortOrder: z.string().optional().describe(\"Sort order by timestamp (asc or desc)\"),\n timeFrom: z.string().optional().describe(\"Start time (ISO 8601 format)\"),\n timeTo: z.string().optional().describe(\"End time (ISO 8601 format)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byVessel(params.vesselId, {\n filterIdType: params.idType,\n filterEventType: params.eventType,\n filterSortOrder: params.sortOrder,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerSafetyTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"get_navtex_messages\",\n \"Get NAVTEX maritime safety messages (navigational warnings, weather forecasts)\",\n {\n timeFrom: z.string().optional().describe(\"Start time (ISO 8601 format)\"),\n timeTo: z.string().optional().describe(\"End time (ISO 8601 format)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.navtex.list({\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerVesselTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"search_vessels\",\n \"Search for vessels by name, IMO, MMSI, flag, type, callsign, year built, class society, or owner\",\n {\n name: z.string().optional().describe(\"Vessel name (partial match)\"),\n imo: z.string().optional().describe(\"IMO number\"),\n mmsi: z.string().optional().describe(\"MMSI number\"),\n flag: z.string().optional().describe(\"Flag state (ISO country code)\"),\n vesselType: z.string().optional().describe(\"Vessel type\"),\n callsign: z.string().optional().describe(\"Radio callsign\"),\n yearBuiltMin: z.number().optional().describe(\"Minimum year built\"),\n yearBuiltMax: z.number().optional().describe(\"Maximum year built\"),\n classSociety: z.string().optional().describe(\"Classification society (case-insensitive)\"),\n owner: z.string().optional().describe(\"Owner name (partial match, case-insensitive)\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.search.vessels({\n filterName: params.name,\n filterImo: params.imo,\n filterMmsi: params.mmsi,\n filterFlag: params.flag,\n filterVesselType: params.vesselType,\n filterCallsign: params.callsign,\n filterYearBuiltMin: params.yearBuiltMin,\n filterYearBuiltMax: params.yearBuiltMax,\n filterClassSociety: params.classSociety,\n filterOwner: params.owner,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel\",\n \"Get detailed information about a specific vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.get(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_position\",\n \"Get the current position of a vessel (latitude, longitude, speed, heading)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.position(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_eta\",\n \"Get the estimated time of arrival for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.eta(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_classification\",\n \"Get the classification details for a vessel (class society, surveys, hull info)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.classification(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_ownership\",\n \"Get the ownership details for a vessel (owner, manager, operator)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.ownership(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_emissions\",\n \"Get emissions data for a vessel (CO2, fuel consumption)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.emissions(params.vesselId, {\n filterIdType: params.idType,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_inspections\",\n \"Get port state control inspections for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.inspections(params.vesselId, {\n filterIdType: params.idType,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_casualties\",\n \"Get marine casualty records for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.string().optional().describe(\"Identifier type: imo (default), mmsi, or vesselId\"),\n limit: z.number().optional().describe(\"Max results per page\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.casualties(params.vesselId, {\n filterIdType: params.idType,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n"],"mappings":"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","names":["server","client","z","server","client","z","z","server","client","z","z","server","client","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/version.ts","../src/tools/location.ts","../src/errors.ts","../src/tools/ports.ts","../src/tools/vessels.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { VesselClient } from \"vesselapi\";\n\nimport { VERSION } from \"./version.js\";\nimport { registerLocationTools } from \"./tools/location.js\";\nimport { registerPortTools } from \"./tools/ports.js\";\nimport { registerVesselTools } from \"./tools/vessels.js\";\n\n// Fail with a readable line rather than a stack trace. Without a key the client\n// throws while this module is still evaluating, so the transport never connects\n// and the calling application sees the process die mid-handshake with no reason.\nif (!process.env.VESSELAPI_API_KEY) {\n process.stderr.write(\n \"vesselapi-mcp: VESSELAPI_API_KEY is not set. Add it to the env block of your \" +\n \"MCP client configuration. Keys are issued at https://dashboard.vesselapi.com\\n\",\n );\n process.exit(1);\n}\n\nconst client = new VesselClient(undefined, { userAgent: `vesselapi-mcp/${VERSION}` });\n\nconst server = new McpServer({\n name: \"vesselapi\",\n version: VERSION,\n});\n\nregisterVesselTools(server, client);\nregisterPortTools(server, client);\nregisterLocationTools(server, client);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","// Version advertised by the McpServer handshake. package.json and server.json\n// carry their own copies and are NOT derived from this — keep all three in step\n// when bumping.\nexport const VERSION = \"2.0.0\";\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerLocationTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"get_vessels_in_area\",\n \"Find all vessels within a rectangular bounding box (latitude/longitude)\",\n {\n latMin: z.number().describe(\"Southern boundary latitude\"),\n latMax: z.number().describe(\"Northern boundary latitude\"),\n lonMin: z.number().describe(\"Western boundary longitude\"),\n lonMax: z.number().describe(\"Eastern boundary longitude\"),\n timeFrom: z.string().optional().describe(\"Start of the time window, RFC3339. Defaults to 2 hours ago. The window may not exceed 4 hours.\"),\n timeTo: z.string().optional().describe(\"End of the time window, RFC3339. Defaults to now. The window may not exceed 4 hours.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.location.vesselsBoundingBox({\n latMin: params.latMin,\n latMax: params.latMax,\n lonMin: params.lonMin,\n lonMax: params.lonMax,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessels_in_radius\",\n \"Find all vessels within a radius of a point. The radius is in METRES, not nautical miles or kilometres.\",\n {\n latitude: z.number().describe(\"Center latitude\"),\n longitude: z.number().describe(\"Center longitude\"),\n radius: z.number().describe(\"Search radius in METRES, maximum 100000 (100 km). One nautical mile is 1852 metres.\"),\n timeFrom: z.string().optional().describe(\"Start of the time window, RFC3339. Defaults to 2 hours ago. The window may not exceed 4 hours.\"),\n timeTo: z.string().optional().describe(\"End of the time window, RFC3339. Defaults to now. The window may not exceed 4 hours.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.location.vesselsRadius({\n latitude: params.latitude,\n longitude: params.longitude,\n radius: params.radius,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n","import {\n VesselAPIError,\n VesselAuthError,\n VesselForbiddenError,\n VesselNotFoundError,\n VesselPaymentRequiredError,\n VesselRateLimitError,\n VesselServerError,\n} from \"vesselapi\";\n\nexport function formatResult(data: unknown): { content: { type: \"text\"; text: string }[] } {\n return {\n content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }],\n };\n}\n\n/**\n * Turn an SDK error into something a model can act on.\n *\n * The distinction that matters to a caller is whether retrying can ever help.\n * A 4xx is the request's fault and will fail identically until the caller\n * changes something, so each one says what to change. Only 429 and 5xx are\n * worth trying again. Where the API supplies a machine-readable `code` it is\n * included, since the prose message is free-form and may be reworded.\n */\nexport function handleToolError(error: unknown): { isError: true; content: { type: \"text\"; text: string }[] } {\n let message: string;\n\n if (error instanceof VesselAuthError) {\n message = \"Authentication failed. Check your VESSELAPI_API_KEY. Do not retry.\";\n } else if (error instanceof VesselPaymentRequiredError) {\n message =\n \"Out of satellite credits, and no stored position was available to fall back on. \" +\n \"Retry without the satellite option to use stored data, or top up the balance at \" +\n \"https://dashboard.vesselapi.com. Retrying the same request will not help.\";\n } else if (error instanceof VesselForbiddenError) {\n message =\n error.code === \"feature_not_available\"\n ? \"This endpoint is not included in the current plan. Do not retry; a different plan is required.\"\n : \"The API key is suspended, usually for sustained quota abuse. Do not retry; \" +\n \"check the account at https://dashboard.vesselapi.com.\";\n } else if (error instanceof VesselNotFoundError) {\n message = `Resource not found. Verify the ID or parameters. ${error.message}`;\n } else if (error instanceof VesselRateLimitError) {\n message = \"Rate limit exceeded. Retrying later may succeed; reduce request frequency.\";\n } else if (error instanceof VesselServerError) {\n message = `Server error, retrying later may succeed: ${error.message}`;\n } else if (error instanceof VesselAPIError) {\n // Remaining 4xx: the request itself is wrong. Name the code so the model can\n // tell an unparseable time range from an out-of-range coordinate.\n const code = error.code ? ` (code: ${error.code})` : \"\";\n const retry = error.statusCode >= 400 && error.statusCode < 500 ? \" Do not retry without changing the request.\" : \"\";\n message = `Request rejected${code}: ${error.message}.${retry}`;\n } else if (error instanceof Error) {\n message = error.message;\n } else {\n message = String(error);\n }\n\n return {\n isError: true,\n content: [{ type: \"text\", text: message }],\n };\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerPortTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"search_ports\",\n \"Search for ports by name, country, type, size, region, harbor size, or harbor use\",\n {\n name: z.string().optional().describe(\"Port name (partial match)\"),\n country: z.string().optional().describe(\"Country (ISO code)\"),\n portType: z.string().optional().describe(\"Port type classification\"),\n size: z.string().optional().describe(\"Port size classification\"),\n region: z.string().optional().describe(\"Geographic region (partial match)\"),\n harborSize: z.string().optional().describe(\"Harbor size classification\"),\n harborUse: z.string().optional().describe(\"Primary harbor use\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.search.ports({\n filterName: params.name,\n filterCountry: params.country,\n filterPortType: params.portType,\n filterSize: params.size,\n filterRegion: params.region,\n filterHarborSize: params.harborSize,\n filterHarborUse: params.harborUse,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port\",\n \"Get detailed information about a specific port by UN/LOCODE\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the port (e.g. NLRTM for Rotterdam)\"),\n },\n async (params) => {\n try {\n const data = await client.ports.get(params.unlocode);\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_inbound\",\n \"Get vessels heading to a specific port within an ETA arrival window\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the destination port (e.g. NLRTM for Rotterdam)\"),\n etaFrom: z.string().optional().describe(\"Start of ETA arrival window (RFC3339). Omit to default to now.\"),\n etaTo: z.string().optional().describe(\"End of ETA arrival window (RFC3339). Omit to default to 72 hours ahead.\"),\n timeFrom: z.string().optional().describe(\"AIS position time range start (RFC3339 format)\"),\n timeTo: z.string().optional().describe(\"AIS position time range end (RFC3339 format)\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.ports.inbound(params.unlocode, {\n filterEtaFrom: params.etaFrom,\n filterEtaTo: params.etaTo,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_events\",\n \"Get port events (arrivals/departures) for a specific port. Covers only the last 2 hours unless timeFrom is given.\",\n {\n unlocode: z.string().describe(\"UN/LOCODE of the port\"),\n timeFrom: z.string().optional().describe(\"Start of the time window, RFC3339. Without it the service returns only the last 2 hours.\"),\n timeTo: z.string().optional().describe(\"End of the time window, RFC3339. Defaults to now.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byPort(params.unlocode, {\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_port_events_by_vessel\",\n \"Get port events (arrivals/departures) for a specific vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n eventType: z.enum([\"arrival\", \"departure\", \"all\"]).optional().describe(\"Filter by event type. Omit for both arrivals and departures\"),\n sortOrder: z.enum([\"asc\", \"desc\"]).optional().describe(\"Sort order by timestamp\"),\n timeFrom: z.string().optional().describe(\"Start time (ISO 8601 format)\"),\n timeTo: z.string().optional().describe(\"End time (ISO 8601 format)\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byVessel(params.vesselId, {\n filterIdType: params.idType,\n filterEventType: params.eventType,\n filterSortOrder: params.sortOrder,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"list_port_events\",\n \"List port events (arrivals/departures) globally with optional filters for time, country, port, vessel, or event type\",\n {\n timeFrom: z.string().optional().describe(\"Start time (RFC3339 format)\"),\n timeTo: z.string().optional().describe(\"End time (RFC3339 format)\"),\n country: z.string().optional().describe(\"Filter by port country (case-insensitive)\"),\n unlocode: z.string().optional().describe(\"Filter by port UN/LOCODE\"),\n eventType: z.enum([\"arrival\", \"departure\", \"all\"]).optional().describe(\"Filter by event type. Omit for both arrivals and departures\"),\n vesselName: z.string().optional().describe(\"Filter by vessel name (full-text search)\"),\n portName: z.string().optional().describe(\"Filter by port name (full-text search)\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.list({\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n filterCountry: params.country,\n filterUnlocode: params.unlocode,\n filterEventType: params.eventType,\n filterVesselName: params.vesselName,\n filterPortName: params.portName,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"search_port_events_by_port\",\n \"Search port events by port name. Covers only the last 2 hours unless timeFrom is given.\",\n {\n portName: z.string().describe(\"Port name to search for\"),\n timeFrom: z.string().optional().describe(\"Start of the time window, RFC3339. Without it the service returns only the last 2 hours.\"),\n timeTo: z.string().optional().describe(\"End of the time window, RFC3339. Defaults to now.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byPorts({\n filterPortName: params.portName,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"search_port_events_by_vessel\",\n \"Search port events by vessel name. Covers only the last 2 hours unless timeFrom is given.\",\n {\n vesselName: z.string().describe(\"Vessel name to search for\"),\n timeFrom: z.string().optional().describe(\"Start of the time window, RFC3339. Without it the service returns only the last 2 hours.\"),\n timeTo: z.string().optional().describe(\"End of the time window, RFC3339. Defaults to now.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.byVessels({\n filterVesselName: params.vesselName,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_last_port_event\",\n \"Get the most recent port event (arrival or departure) for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n },\n async (params) => {\n try {\n const data = await client.portEvents.lastByVessel(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"list_emissions\",\n \"List global vessel emissions data with optional year filter\",\n {\n period: z.number().optional().describe(\"Reporting year filter (e.g. 2024)\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.emissions.list({\n filterPeriod: params.period,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { VesselClient } from \"vesselapi\";\nimport { z } from \"zod\";\nimport { formatResult, handleToolError } from \"../errors.js\";\n\nexport function registerVesselTools(server: McpServer, client: VesselClient): void {\n server.tool(\n \"search_vessels\",\n \"Search for vessels. Use q when you have an identifier but do not know which kind it is; use the specific filters to narrow a fleet.\",\n {\n q: z.string().optional().describe(\"Free-text search across name, IMO, MMSI, ENI and callsign. Use this when the type of identifier is unknown.\"),\n name: z.string().optional().describe(\"Vessel name (partial match)\"),\n imo: z.string().optional().describe(\"IMO number\"),\n mmsi: z.string().optional().describe(\"MMSI number\"),\n eni: z.string().optional().describe(\"ENI number, used for inland waterway vessels\"),\n flag: z.string().optional().describe(\"Flag state (ISO country code)\"),\n vesselType: z.string().optional().describe(\"Vessel type\"),\n callsign: z.string().optional().describe(\"Radio callsign\"),\n yearBuiltMin: z.number().optional().describe(\"Minimum year built\"),\n yearBuiltMax: z.number().optional().describe(\"Maximum year built\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.search.vessels({\n q: params.q,\n filterEni: params.eni,\n filterName: params.name,\n filterImo: params.imo,\n filterMmsi: params.mmsi,\n filterFlag: params.flag,\n filterVesselType: params.vesselType,\n filterCallsign: params.callsign,\n filterYearBuiltMin: params.yearBuiltMin,\n filterYearBuiltMax: params.yearBuiltMax,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel\",\n \"Get detailed information about a specific vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.get(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_position\",\n \"Get the current position of a vessel (latitude, longitude, speed, heading)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n sat: z.boolean().optional().describe(\"Fall back to a satellite position when no recent terrestrial one exists. Charged per call against a prepaid balance, so use it only when a stored position is genuinely insufficient.\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.position(params.vesselId, {\n filterSat: params.sat,\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_eta\",\n \"Get the estimated time of arrival for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.eta(params.vesselId, {\n filterIdType: params.idType,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_emissions\",\n \"Get emissions data for a vessel (CO2, fuel consumption)\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.emissions(params.vesselId, {\n filterIdType: params.idType,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_casualties\",\n \"Get marine casualty records for a vessel\",\n {\n vesselId: z.string().describe(\"Vessel identifier (IMO number by default)\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.casualties(params.vesselId, {\n filterIdType: params.idType,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n\n server.tool(\n \"get_vessel_positions_batch\",\n \"Get positions for multiple vessels at once by MMSI or IMO numbers\",\n {\n ids: z.string().describe(\"Comma-separated list of MMSI or IMO numbers\"),\n idType: z.enum([\"imo\", \"mmsi\"]).optional().describe(\"Identifier type: imo (default) or mmsi\"),\n timeFrom: z.string().optional().describe(\"Start time filter in RFC3339 format (defaults to 2 hours ago)\"),\n timeTo: z.string().optional().describe(\"End time filter in RFC3339 format (defaults to current time)\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Results per page, 1 to 50. Defaults to 20.\"),\n nextToken: z.string().optional().describe(\"Pagination token from previous response\"),\n },\n async (params) => {\n try {\n const data = await client.vessels.positions({\n filterIds: params.ids,\n filterIdType: params.idType,\n timeFrom: params.timeFrom,\n timeTo: params.timeTo,\n paginationLimit: params.limit,\n paginationNextToken: params.nextToken,\n });\n return formatResult(data);\n } catch (error) {\n return handleToolError(error);\n }\n },\n );\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;;;ACCtB,IAAM,UAAU;;;ACDvB,SAAS,SAAS;;;ACFlB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,SAAS,aAAa,MAA8D;AACzF,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,EACjE;AACF;AAWO,SAAS,gBAAgB,OAA8E;AAC5G,MAAI;AAEJ,MAAI,iBAAiB,iBAAiB;AACpC,cAAU;AAAA,EACZ,WAAW,iBAAiB,4BAA4B;AACtD,cACE;AAAA,EAGJ,WAAW,iBAAiB,sBAAsB;AAChD,cACE,MAAM,SAAS,0BACX,mGACA;AAAA,EAER,WAAW,iBAAiB,qBAAqB;AAC/C,cAAU,oDAAoD,MAAM,OAAO;AAAA,EAC7E,WAAW,iBAAiB,sBAAsB;AAChD,cAAU;AAAA,EACZ,WAAW,iBAAiB,mBAAmB;AAC7C,cAAU,6CAA6C,MAAM,OAAO;AAAA,EACtE,WAAW,iBAAiB,gBAAgB;AAG1C,UAAM,OAAO,MAAM,OAAO,WAAW,MAAM,IAAI,MAAM;AACrD,UAAM,QAAQ,MAAM,cAAc,OAAO,MAAM,aAAa,MAAM,gDAAgD;AAClH,cAAU,mBAAmB,IAAI,KAAK,MAAM,OAAO,IAAI,KAAK;AAAA,EAC9D,WAAW,iBAAiB,OAAO;AACjC,cAAU,MAAM;AAAA,EAClB,OAAO;AACL,cAAU,OAAO,KAAK;AAAA,EACxB;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,EAC3C;AACF;;;AD1DO,SAAS,sBAAsBA,SAAmBC,SAA4B;AACnF,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,4BAA4B;AAAA,MACxD,QAAQ,EAAE,OAAO,EAAE,SAAS,4BAA4B;AAAA,MACxD,QAAQ,EAAE,OAAO,EAAE,SAAS,4BAA4B;AAAA,MACxD,QAAQ,EAAE,OAAO,EAAE,SAAS,4BAA4B;AAAA,MACxD,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gGAAgG;AAAA,MACzI,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sFAAsF;AAAA,MAC7H,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMC,QAAO,SAAS,mBAAmB;AAAA,UACpD,QAAQ,OAAO;AAAA,UACf,QAAQ,OAAO;AAAA,UACf,QAAQ,OAAO;AAAA,UACf,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAU,EAAE,OAAO,EAAE,SAAS,iBAAiB;AAAA,MAC/C,WAAW,EAAE,OAAO,EAAE,SAAS,kBAAkB;AAAA,MACjD,QAAQ,EAAE,OAAO,EAAE,SAAS,qFAAqF;AAAA,MACjH,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gGAAgG;AAAA,MACzI,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sFAAsF;AAAA,MAC7H,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMC,QAAO,SAAS,cAAc;AAAA,UAC/C,UAAU,OAAO;AAAA,UACjB,WAAW,OAAO;AAAA,UAClB,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;;;AEjEA,SAAS,KAAAC,UAAS;AAGX,SAAS,kBAAkBC,SAAmBC,SAA4B;AAC/E,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,MAAME,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,MAChE,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MAC5D,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAAA,MACnE,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAAA,MAC/D,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,MAC1E,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,MACvE,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MAC9D,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,OAAO,MAAM;AAAA,UACrC,YAAY,OAAO;AAAA,UACnB,eAAe,OAAO;AAAA,UACtB,gBAAgB,OAAO;AAAA,UACvB,YAAY,OAAO;AAAA,UACnB,cAAc,OAAO;AAAA,UACrB,kBAAkB,OAAO;AAAA,UACzB,iBAAiB,OAAO;AAAA,UACxB,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,kDAAkD;AAAA,IAClF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,MAAM,IAAI,OAAO,QAAQ;AACnD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,8DAA8D;AAAA,MAC5F,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAAgE;AAAA,MACxG,OAAOA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yEAAyE;AAAA,MAC/G,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,MACzF,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,MACrF,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,MAAM,QAAQ,OAAO,UAAU;AAAA,UACvD,eAAe,OAAO;AAAA,UACtB,aAAa,OAAO;AAAA,UACpB,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,MACrD,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0FAA0F;AAAA,MACnI,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAAA,MAC1F,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,OAAO,OAAO,UAAU;AAAA,UAC3D,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MAC5F,WAAWA,GAAE,KAAK,CAAC,WAAW,aAAa,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,6DAA6D;AAAA,MACpI,WAAWA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,MAChF,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,MACvE,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,MACnE,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,SAAS,OAAO,UAAU;AAAA,UAC7D,cAAc,OAAO;AAAA,UACrB,iBAAiB,OAAO;AAAA,UACxB,iBAAiB,OAAO;AAAA,UACxB,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6BAA6B;AAAA,MACtE,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,MAClE,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2CAA2C;AAAA,MACnF,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAAA,MACnE,WAAWA,GAAE,KAAK,CAAC,WAAW,aAAa,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,6DAA6D;AAAA,MACpI,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0CAA0C;AAAA,MACrF,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MACjF,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,KAAK;AAAA,UACxC,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,eAAe,OAAO;AAAA,UACtB,gBAAgB,OAAO;AAAA,UACvB,iBAAiB,OAAO;AAAA,UACxB,kBAAkB,OAAO;AAAA,UACzB,gBAAgB,OAAO;AAAA,UACvB,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,MACvD,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0FAA0F;AAAA,MACnI,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAAA,MAC1F,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,QAAQ;AAAA,UAC3C,gBAAgB,OAAO;AAAA,UACvB,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYE,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,MAC3D,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0FAA0F;AAAA,MACnI,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAAA,MAC1F,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,UAAU;AAAA,UAC7C,kBAAkB,OAAO;AAAA,UACzB,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,IAC9F;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,WAAW,aAAa,OAAO,UAAU;AAAA,UACjE,cAAc,OAAO;AAAA,QACvB,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQE,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,MAC1E,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,UAAU,KAAK;AAAA,UACvC,cAAc,OAAO;AAAA,UACrB,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;;;ACzQA,SAAS,KAAAE,UAAS;AAGX,SAAS,oBAAoBC,SAAmBC,SAA4B;AACjF,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,GAAGE,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6GAA6G;AAAA,MAC/I,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6BAA6B;AAAA,MAClE,KAAKA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,YAAY;AAAA,MAChD,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,MAClD,KAAKA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,MAClF,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+BAA+B;AAAA,MACpE,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,aAAa;AAAA,MACxD,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gBAAgB;AAAA,MACzD,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MACjE,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MACjE,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,OAAO,QAAQ;AAAA,UACvC,GAAG,OAAO;AAAA,UACV,WAAW,OAAO;AAAA,UAClB,YAAY,OAAO;AAAA,UACnB,WAAW,OAAO;AAAA,UAClB,YAAY,OAAO;AAAA,UACnB,YAAY,OAAO;AAAA,UACnB,kBAAkB,OAAO;AAAA,UACzB,gBAAgB,OAAO;AAAA,UACvB,oBAAoB,OAAO;AAAA,UAC3B,oBAAoB,OAAO;AAAA,UAC3B,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,IAC9F;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,IAAI,OAAO,UAAU;AAAA,UACrD,cAAc,OAAO;AAAA,QACvB,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MAC5F,KAAKA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,uLAAuL;AAAA,IAC9N;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,SAAS,OAAO,UAAU;AAAA,UAC1D,WAAW,OAAO;AAAA,UAClB,cAAc,OAAO;AAAA,QACvB,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,IAC9F;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,IAAI,OAAO,UAAU;AAAA,UACrD,cAAc,OAAO;AAAA,QACvB,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MAC5F,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,UAAU,OAAO,UAAU;AAAA,UAC3D,cAAc,OAAO;AAAA,UACrB,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAUE,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACzE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MAC5F,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,WAAW,OAAO,UAAU;AAAA,UAC5D,cAAc,OAAO;AAAA,UACrB,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,KAAKE,GAAE,OAAO,EAAE,SAAS,6CAA6C;AAAA,MACtE,QAAQA,GAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MAC5F,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+DAA+D;AAAA,MACxG,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8DAA8D;AAAA,MACrG,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,MACvG,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACrF;AAAA,IACA,OAAO,WAAW;AAChB,UAAI;AACF,cAAM,OAAO,MAAMD,QAAO,QAAQ,UAAU;AAAA,UAC1C,WAAW,OAAO;AAAA,UAClB,cAAc,OAAO;AAAA,UACrB,UAAU,OAAO;AAAA,UACjB,QAAQ,OAAO;AAAA,UACf,iBAAiB,OAAO;AAAA,UACxB,qBAAqB,OAAO;AAAA,QAC9B,CAAC;AACD,eAAO,aAAa,IAAI;AAAA,MAC1B,SAAS,OAAO;AACd,eAAO,gBAAgB,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;;;ALtKA,IAAI,CAAC,QAAQ,IAAI,mBAAmB;AAClC,UAAQ,OAAO;AAAA,IACb;AAAA,EAEF;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,IAAI,aAAa,QAAW,EAAE,WAAW,iBAAiB,OAAO,GAAG,CAAC;AAEpF,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS;AACX,CAAC;AAED,oBAAoB,QAAQ,MAAM;AAClC,kBAAkB,QAAQ,MAAM;AAChC,sBAAsB,QAAQ,MAAM;AAEpC,IAAM,YAAY,IAAI,qBAAqB;AAC3C,MAAM,OAAO,QAAQ,SAAS;","names":["server","client","z","server","client","z","z","server","client","z"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesselapi-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"mcpName": "io.github.vessel-api/vesselapi-mcp",
|
|
5
|
-
"description": "MCP server for the VesselAPI
|
|
5
|
+
"description": "MCP server for the VesselAPI: maritime vessel tracking, port events and emissions",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "VesselAPI",
|
|
8
8
|
"repository": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"vesselapi-mcp": "./dist/index.js"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
|
-
"dist"
|
|
33
|
+
"dist",
|
|
34
|
+
"CHANGELOG.md"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
37
|
"prepublishOnly": "npm run build",
|
|
@@ -42,9 +43,9 @@
|
|
|
42
43
|
"node": ">=18"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
|
-
"vesselapi": "^1.2.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
47
|
-
"
|
|
47
|
+
"vesselapi": "^2.0.0",
|
|
48
|
+
"zod": "^3.25.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^25.3.0",
|