Connected to live AIS data through an MCP server, Claude found 29 to 32 ships at anchor off Durban and dated the longest wait at about 18 days. A week after Storm Austen it counted 6 container ships still at anchor off Rotterdam, most of them there for one or two days. In the US Gulf it found no VLCC or Suezmax at Corpus Christi or Galveston. It read most of the five large tankers due there as arriving in ballast and left one undecided.
Data from live runs on 1 November 2026.
In all 17 runs Claude Code was the AI agent, and vesselapi-mcp, the MCP server for the VesselAPI AIS API, supplied its only tools. The runs took place between about 19:00 and 19:25 UTC on 1 November, from 7 prompts: 13 on Sonnet 5.5 and 4 on Haiku 5.5. No prompt named a tool.
Three questions and what the agent found
Part 1: Are US Gulf crude tankers arriving laden or in ballast? None of the seven runs found a VLCC or Suezmax at or due at Corpus Christi or Galveston. Every run found the same five Aframax-size tankers declaring Corpus Christi for the coming week. The three judgement runs read most of them as in ballast, an inference from crew-entered draughts and not trading advice.
Part 2: How long are ships waiting at Durban anchorage? Three runs found 29 to 32 ships at anchor; a fourth found 7 because its search box stopped short of the anchorage. The longest dated wait, about 18 days, came from the shortest prompt. The two Sonnet runs that tried to date every ship gave only rough bounds.
Part 3: Has the Rotterdam port backlog cleared after Storm Austen? Kuehne+Nagel reported that the port closed from 23 to 25 October because of high winds. A week later, two Sonnet runs with overlapping search boxes found the same 6 container ships, one of them drifting in one run, and a third with a wider box found 8 waiting for Rotterdam.
Connecting Claude to the AIS API
The server runs through npx and needs one VesselAPI key, created in the dashboard. It needs Node 20 or later. The first line below reads the key without echoing it, which keeps it out of your shell history:
read -rs VESSELAPI_API_KEY && export VESSELAPI_API_KEY
claude mcp add vesselapi --scope user -e VESSELAPI_API_KEY="$VESSELAPI_API_KEY" -- npx -y vesselapi-mcp
With --scope user the server is available in every project; without it, only in the directory where you ran the command. Both store the key in plain text in ~/.claude.json.
For a project that shares its configuration through a committed .mcp.json, keep the key out of the file. Single quotes stop the shell from expanding the variable. Claude Code then stores a reference and fills it from each user's environment at start-up:
claude mcp add vesselapi --scope project -e 'VESSELAPI_API_KEY=${VESSELAPI_API_KEY}' -- npx -y vesselapi-mcp
Each person approves a project server the first time they open the project in Claude Code. If the variable is unset, claude mcp list warns about it, and the server receives the literal ${VESSELAPI_API_KEY} text as its key, so every call fails to authenticate. Other clients take the configuration from the vesselapi-mcp README:
{
"mcpServers": {
"vesselapi": {
"command": "npx",
"args": ["-y", "vesselapi-mcp"],
"env": {
"VESSELAPI_API_KEY": "your-api-key"
}
}
}
}
| Client | Where the configuration goes |
|---|---|
| Claude Desktop |
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows). If Node was installed through a version manager such as nvm, give the absolute path to npx and add Node's directory to PATH in env
|
| Cursor |
~/.cursor/mcp.json, or .cursor/mcp.json in the project. A project file can reference the key as ${env:VESSELAPI_API_KEY} instead of holding it |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Files that hold the key in plain text belong outside version control.
How we ran the tests
Each run was one non-interactive Claude Code call (claude -p, version 2.1.316) in an empty directory outside any repository. User settings, hooks and instruction files were not loaded (--setting-sources project), and Claude Code's built-in tools were switched off (--tools ""). The agent had exactly the 19 VesselAPI tools, which each run's start-up record confirms. Claude Code's own system prompt, the date and the working-directory path were still in its context. Size bands and draught rules of thumb came from the model's own knowledge.
The server was vesselapi-mcp 2.2.1, the current release, run locally with a one-line patch to its bundled SDK that pointed it at a recording proxy in front of the live API. The proxy refused requests beyond 80 per run with HTTP 429. Only the two Rotterdam runs told to use as many calls as they needed reached that cap. Each prompt ran one to four times, and every Haiku figure comes from a single run, so the figures describe these runs and are not a benchmark.
The 19 tools, in four groups
| Group | Tools | What they return |
|---|---|---|
| Where ships are now |
get_vessels_in_area (bounding box), get_vessels_in_radius (point and radius) |
Position reports with speed, heading and AIS navigational status |
| What a ship is |
get_vessel, get_vessel_position, search_vessels, get_vessel_eta, get_vessel_positions_batch
|
The vessel record: type, length, beam, deadweight, gross tonnage, average and deepest draught over the last 31 days. get_vessel_position adds the latest position, declared destination and ETA in one call; the batch tool returns many ships' positions over a time range |
| Ports and port calls |
search_ports, get_port, get_port_inbound, and six port-event tools |
Port records by name or UN/LOCODE; ships declaring the port as destination; arrivals and departures by port, by vessel, by name, across all ports, and a ship's last call |
| Records |
get_vessel_emissions, list_emissions, get_vessel_casualties
|
Emissions and marine casualty records |
Navigational status arrives as the AIS code (1 for "at anchor", 5 for "moored"), and the agent decoded it without help.
Query limits the agent works within
List tools return 20 rows by default and up to 50 per page, with a nextToken for the next page. A row in a position search is one report, not one ship, so a page of 50 off Corpus Christi held 28 to 33 distinct ships. Position lists come newest first, so in a busy area a full page covers only a few minutes. Each page is about 24 KB of tool output. No run in the series read every page of every search.
Area and radius searches cover the last 2 hours by default and at most 4 hours per call. A radius is given in metres, up to 100 km. A bounding box's height and width in degrees may total at most 4. To page through a search, pass an explicit timeFrom and timeTo and reuse them, with the same box, alongside the nextToken. A short window that one or two pages cover is easiest.
The port-event tools cover only the last 2 hours unless given timeFrom, and keep events for 31 days; get_vessel_last_port_event has no window. get_port_events_by_vessel takes a sortOrder, which position searches lack. get_port_inbound is ordered by ETA, starts at the current time unless given etaFrom, looks 72 hours ahead unless given etaTo, and by default counts only ships that reported in the last 24 hours.
Five prompt details that changed the answers
Rotterdam went from 5 calls to 80 once the prompt allowed them
Given the plain backlog question, both Sonnet runs made 5 or 6 calls. Each explained that ship type sits in the vessel record, one lookup per hull, and asked before going on. Haiku went ahead without asking. The prompt that added "look up the type of each ship … and use as many calls as you need" produced counts, and both of its runs reached the 80-request cap. One of them made 11 more lookups after the server's first refusal told it to stop and tell the user, because the prompt said to keep going. A hard budget is better enforced in the system prompt or in the client.
The 18-day Durban wait came from sampling earlier dates
The prompt that asked how long each Durban ship had waited got rough bounds from both Sonnet runs. Neither sampled the whole anchorage on earlier dates, and one run's newest-first history reached back only about an hour. The shorter prompt, which asked only for the longest wait, did sample 20-minute windows on earlier dates and dated a wait of about 18 days. In these runs, naming the method in the prompt ("sample each ship's position in short windows on earlier days") would have closed that gap.
Durban's anchorage lay 16 to 37.5 km from the port's reference point
A port record is a reference point in the harbour, so the agent sets its own search area around the anchorage. At Durban one run's box stopped short and found 7 ships at anchor where the others found about 30. At Rotterdam a wider box held 55 ships at anchor against 31, which nearly doubled the type lookups. Naming the anchorage and its extent in the prompt would avoid both.
Draught, ETA and status are crew-entered evidence
Destinations, ETAs, draughts and navigational status are all entered by the crew. The deepest draught on a vessel record is the deepest a ship reported in the last 31 days. The agent treated these fields as evidence and gave a confidence for every laden or ballast reading. Declared ETAs also decide what the inbound list shows. Off Corpus Christi, two moored Aframax-size tankers were not on it, since the list starts at the current time and both ships' declared ETAs had passed. A position search near the port found them where the inbound list could not.
Give the agent the time
Claude Code gives the agent the date but not the time of day. One Haiku run at Durban reasoned as if it were about 12:05 UTC, when its requests ran at about 19:10, so every wait it gave was about seven hours short. Both Haiku runs in the US Gulf had requests rejected, for a radius over 100 km and for a window over 4 hours. The radius was corrected on the next call and the window on the second retry. Putting the current UTC time and an explicit window in the prompt avoids both.
Calls, time and cost per run for Sonnet 5.5 and Haiku 5.5
| Model | Runs | Tool calls per run | API requests served per run | Time per run | Cost per run |
|---|---|---|---|---|---|
| Sonnet 5.5 | 13 | 5 to 92 | 5 to 80 | 26 s to 3 min 53 s | \$0.10 to \$0.67 |
| Haiku 5.5 | 4 | 31 to 44 | 31 to 44 | 53 s to 3 min 31 s | \$0.016 to \$0.22 |
Cost is Claude Code's own estimate per run (total_cost_usd) at API list prices. API requests count against the key's monthly quota separately. Tool calls exceed requests only in the two runs that reached the cap, where 2 and 12 calls were refused.
Each Haiku result is one run on one prompt, so the comparison is anecdotal. Haiku went about as deep as Sonnet. It dated 29 of its 32 ships at Durban by sampling small groups so that no page filled, and found the same five inbound Aframaxes in the US Gulf. At Rotterdam it went ahead where Sonnet asked, but its box missed most of the container ships and it counted only records labelled "Container Ship", so it found one.
Claude, Claude Code, Sonnet, Haiku and Anthropic are trademarks of Anthropic PBC. Other names are trademarks of their respective owners.
Top comments (0)