Charter Boats
Charter Boats API
AI Integrations

MCP Server

Connect AI assistants to Charter Boats via the Model Context Protocol

The Charter Boats MCP server lets Claude, ChatGPT, Cursor, and any MCP-compatible AI assistant search boats, locations, POIs, routes, trips, and content -- with rich HTML widgets for visual boat and destination cards.

Endpoint: https://charter.boats/mcp Install / quickstart page: open https://charter.boats/mcp in a browser. Transport: Streamable HTTP (JSON-RPC 2.0). Auth: None. The server does not pass request headers on to the API, so an API key sent to /mcp has no effect — MCP calls are neither attributed to an account nor affiliate-tagged. See also Rate limits. Spec: MCP Apps 2026-01-26.

Deployment

The server is a Cloudflare Worker routed at charter.boats/mcp. It proxies tool calls to the Charter Boats AI API (https://charter.boats/api/ai/*) and serves two HTML widgets as MCP App resources.

AI assistant → https://charter.boats/mcp (CF Worker) → charter.boats/api/ai/*

A single /mcp URL handles three things:

  • GET with Accept: text/html → install / quickstart page (HTML)
  • GET with Accept: application/json → server info JSON
  • POST → MCP JSON-RPC

Tools

Ten read-only tools. Each returns the underlying API response as structuredContent (which the widgets render) plus a short text summary for the model. A search that finds nothing still returns the API's zero-results blockno_results (searched, why, try_next) and note — in structuredContent.

ToolDescriptionWidget
search_boatsBoats by location, type, dates, capacity, budget. Returns up to 8 with prices, images, booking links.✓ boats
search_locationsMarinas, harbours and anchorages three ways — see below. Up to 10 with boat counts.✓ locations
search_poisRestaurants, fuel, groceries, etc. near a destination. Up to 10.
search_routesSailing passages between destinations. Up to 5.
search_tripsMulti-day curated itineraries. Up to 5.
search_contentArticles, guides, FAQs (semantic search). Up to 5.

search_locations answers three different questions, picked by which inputs you send:

  • By nameq (min 2 characters): "Lefkada", "Dubrovnik".
  • By popularity in an arearegion (a sailing region as guests say it: Dalmatia, the Ionian, the Cyclades…), country, city, optionally type (marina, harbour, anchorage, bay, mooring, spot — the same words the results carry back, spot being an unclassified stop) and month (1-12, default the current month). Ranked by sort: popularity (default — observed vessel traffic for that month), boats (charter fleet size) or rating.
  • By reach from a placefrom (a location id or slug from an earlier result) plus within_nm (10, 20, 35, 55 or 100; default 35): the places boats were recorded sailing to from there, most-travelled first, with estimated distance and hours at 6 knots.

See Search Locations for the response of each mode.

Detail

ToolDescription
get_boat_detailsFull specs, 365-day availability + pricing ranges, bookable check-in/check-out slots, required/optional fees, active discounts, cancellation policy, check-in/out times.
get_poi_detailsDescription, opening hours, address, contact, nearby marinas, special offers. POIs carry no rating or review count.
get_location_detailsDescription, amenities, contact, boat count and cheapest day rate, plus tracked-AIS insight: how busy it is month by month (against its own peak, never another place), how boats use it (overnight vs day stop, median stay, what hour it fills), where boats sail to from it (how many boats made each passage, with estimated sailing times at 6 knots), and the nearest bookable base.
get_trip_detailsDay-by-day stops, distances, activities, highlights, notes.

Full input schemas are exposed via tools/list -- view them in MCP Inspector by connecting to https://charter.boats/mcp.

Prompt presets

The server registers three prompts surfaced as one-click starters in supporting clients:

  • plan-sailing-trip -- destination + optional guests/dates/budget
  • find-charter-boat -- location + optional type/guests/dates
  • explore-destination -- single destination, surfaces marinas + POIs + routes

UI widgets

Two HTML widgets are served as MCP App resources:

WidgetURIUsed by
Boatsui://widget/boats-{hash}.htmlsearch_boats
Locationsui://widget/locations-{hash}.htmlsearch_locations

The {hash} is a hash of that widget's own HTML, so the URI changes if and only if the widget does. ChatGPT caches MCP resources by URI with no expiry — a fixed URI means a redeployed widget is never re-read, however many times it ships — and OpenAI's guidance is to give the template a new URI when its HTML, JS or CSS changes. Read the current values from resources/list; never hard-code one. A read of a previously published URI (including the original unhashed ui://widget/boats.html) still returns the current widget rather than an error, so a host holding a stale URI degrades to old-metadata-but-correct-markup instead of a broken card.

Both widgets:

  • Use the text/html;profile=mcp-app MIME type.
  • Declare their image CSP twice over: _meta.ui.csp.resourceDomains (the MCP Apps standard, which Claude reads) and _meta["openai/widgetCSP"].resource_domains (the snake_case compatibility key, which is the only one ChatGPT reads — without it ChatGPT applies a locked-down default and every boat photo renders as a broken image). _meta.ui.prefersBorder / openai/widgetPrefersBorder and _meta.ui.resourceUri / openai/outputTemplate are paired the same way.
  • List exact origins, never wildcards — ChatGPT rejects a *.example.com entry and one bad entry discards the whole list. The allowed set is charter.boats, media.charter.boats, our storage bucket's direct origin, and wsrv.nl. Only the boats widget shows images: boat photos are served from media.charter.boats (listed explicitly, since a CSP host source matches that host alone and charter.boats does not cover its subdomains), and a boat photo not yet copied to our storage resizes through wsrv. The locations widget shows no images, and search_pois has no widget — its image_url values are passed through as stored (some on our storage bucket, many at a third-party site's own address: a review site, a venue's website, a local guide), and nothing renders them inside a widget.
  • Implement the full MCP Apps handshake: sandbox-resource-readyui/initialize (with protocolVersion, appInfo, appCapabilities) → ui/notifications/initialized → render on ui/notifications/tool-result.
  • Report height via ui/notifications/size-changed, measured from body.scrollHeight / #app (the document element returns 0 inside Claude's sandbox).
  • Route link clicks through ui/open-link since sandboxed iframes block target="_blank".
  • Respect host theme: explicit theme: 'dark' | 'light' from host-context-changed overrides the prefers-color-scheme CSS fallback.

Install

For end users: open https://charter.boats/mcp in any browser -- the page has copy-paste install steps for Claude.ai, Claude Desktop, ChatGPT, Cursor, and MCP Inspector.

Quick reference

Claude.ai / Claude Desktop: Settings → Connectors → Add custom connector → https://charter.boats/mcp (Pro/Max/Team/Enterprise required).

ChatGPT: Settings → Apps → Create app → MCP Server → https://charter.boats/mcp. Widget rendering requires the ChatGPT Apps program; Developer mode shows tool calls as text only.

Cursor: add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "charter-boats": { "url": "https://charter.boats/mcp" }
  }
}

MCP Inspector: npx @modelcontextprotocol/inspector → Streamable HTTP → https://charter.boats/mcp.

Rate limits

Tool calls are subject to the shared /api/ai/* rate limit. That limit is counted per calling client, and for MCP the caller the API sees is this server, not your assistant or its user: the server forwards no end-user identity (no IP, no key). MCP traffic is therefore limited collectively rather than per person, and there is no per-user figure to plan against. A limited call surfaces as a tool error (API 429); wait a little and retry.

Differences from the Custom GPT

Both integrations call the same /api/ai/* endpoints but expose them differently:

FeatureCustom GPT (Actions)MCP Server
ProtocolOpenAPI 3.1Model Context Protocol (Streamable HTTP)
Rich UINoYes -- boats + locations widgets
TransportHTTPSJSON-RPC over HTTP
Session stateGPT conversation IDStateless per request
Rate limit30 / 10 min per OpenAI userShared across MCP traffic — see Rate limits
HostsChatGPT onlyClaude, ChatGPT Apps, Cursor, Continue, any MCP client

On this page