- Developers
- MCP for AI agents
MCP for AI agents
The ferry-mcp server lets Claude and other AI agents work with the Ferry partner API through the Model Context Protocol: find voyages, view seat maps and prices, look up customers, read bookings, and request ticket cancellations. The server lives in the open-source repository ferry-vn/ferry-sdk, next to the SDK and the CLI.
What an agent can do
Section titled “What an agent can do”With this server you can ask Claude things like “find voyages from Rạch Giá to Phú Quốc tomorrow and compare prices”, “are there adjacent seats for 4 people”, “what is the status of the booking for customer 0901…”, or “quote this booking but do not create it”. The server runs with your API key, so it sees exactly your organization’s data; see Authentication with an API key.
Besides tools, the server provides two ready-made prompts (plan_ferry_trip, check_booking) and a resource ferry://locations with the pier catalogue. The default environment is production; set FERRY_ENV=uat to use UAT, but UAT is not a sandbox: issuing tickets there still books real tickets and spends real money (see Environments).
Install
Section titled “Install”Claude Desktop is the simplest route and needs no Node.js:
- Download the
ferry-mcp.mcpbfile from the latest release on GitHub and open it. - Enter your API key.
- If you want it, allow write operations.
Claude Code needs Node.js 22.12 or later; npx fetches the @ferryvn/mcp package from npm when the server starts:
claude mcp add ferryvn -e FERRY_API_KEY=<key> -- npx -y @ferryvn/mcpTo allow write operations, add -e FERRY_MCP_ENABLE_WRITES=true to the claude mcp add command. If you installed it globally with npm install -g @ferryvn/mcp, you can replace npx -y @ferryvn/mcp with ferry-mcp. For other MCP applications, register the server the way that application expects: the command is npx with the arguments -y @ferryvn/mcp, the environment variable is FERRY_API_KEY, and FERRY_ENV is optional.
Read tools and write tools
Section titled “Read tools and write tools”Read tools are always available:
| Tool | What it does |
|---|---|
search_locations, list_routes | Find piers (accent-insensitive) and view routes with their operators |
search_voyages, get_voyage_calendar | Find voyages by date or date range; see voyages per day |
get_route_prices, get_seat_map | Selling prices of a route; free seats by seat type with prices and harbor fee |
quote_booking | Validate a booking and return the total, creating nothing |
find_customers | Look up customers |
get_booking, list_bookings, list_cancel_requests | Read a booking, the booking list, and cancellation requests |
get_disruptions, get_account, list_vouchers | Delays or cancellations affecting your bookings, balances and credit, vouchers |
Write tools exist only when FERRY_MCP_ENABLE_WRITES=true (exactly this string):
| Tool | Effect |
|---|---|
create_customer | Creates a customer |
create_booking | Creates a validated booking with its seats and passengers, Requested and unpaid; charges nothing |
request_cancellation | Holds the cancellation fee immediately and marks the tickets cancelled at once |
Safety model
Section titled “Safety model”- Never exposed: ticket issuance, payments, transactions, withdrawals and top-ups, balance or credit adjustments, debt ledgers, voucher and price changes, and organization, user, API key, and webhook settings. A booking the agent creates stops at the unpaid state; issuing tickets is a human job through the Dashboard, the SDK, or the CLI.
- A human confirms every write: if the application supports MCP confirmation dialogs, the server shows a dialog that the model cannot answer on your behalf. If not, the model must pass
user_confirmed: trueafter showing you exactly what will happen. The dialog names the change: voyages, seats, passengers, and total; for a cancellation, the voyage and the passengers whose tickets are cancelled. - Strict arguments: unknown or misspelt arguments are rejected rather than silently dropped, because a dropped nationality would change the ticket type and price.
- Write budget: at most
FERRY_MCP_MAX_WRITESwrite calls per process, default 20, to stop a runaway agent loop. - Audit log: each write attempt is one JSON line on stderr, and is also appended to the file named in
FERRY_MCP_AUDIT_FILEif set. The log holds ids and counts only, no passenger personal data. - Personal data in reads: bookings contain names, ID numbers, and phone numbers. The server’s instructions tell the model to share them only with the people concerned. If you do not want the agent to touch this data, hide the tools with
FERRY_MCP_DISABLED_TOOLS, for examplelist_bookings,find_customers.
Environment variables
Section titled “Environment variables”| Environment variable | Meaning |
|---|---|
FERRY_API_KEY | Partner API key; required unless you only look up piers and routes |
FERRY_ENV, FERRY_BASE_URL | production (default) or uat; or an explicit address |
FERRY_MCP_ENABLE_WRITES | true to enable the three write tools |
FERRY_MCP_MAX_WRITES | Maximum write calls per process (default 20) |
FERRY_MCP_AUDIT_FILE | File to append write audit lines to |
FERRY_MCP_DISABLED_TOOLS | Tools to leave out, comma-separated |
FERRY_MCP_HTTP_HOST, FERRY_MCP_HTTP_TOKEN, FERRY_MCP_HTTP_ALLOW_WRITES, FERRY_MCP_ALLOWED_HOSTS, FERRY_MCP_ALLOWED_ORIGINS | HTTP mode only, see the next section |
FERRY_LANG, FERRY_TIMEOUT_MS | Optional: message language and timeout |
Invalid values stop the server at startup with a clear message.
HTTP mode
Section titled “HTTP mode”By default the server runs over stdio for an application on the same machine. When you need to run it as a service, ferry-mcp --http --port 3333 serves http://127.0.0.1:3333/mcp (plus GET /healthz for health checks). The server acts with your API key, so this mode has protections:
- DNS-rebinding protection: the
Hostheader must be a loopback name and a browserOriginmust be a loopback origin. Add more withFERRY_MCP_ALLOWED_HOSTSandFERRY_MCP_ALLOWED_ORIGINS. - Token required whenever anything beyond loopback can reach it: set
FERRY_MCP_HTTP_TOKEN(16 characters or more) so every request must sendAuthorization: Bearer <token>. The server refuses to start without the token when it binds a non-loopback address, or when you allow a non-loopback host or origin (for example behind a reverse proxy). - Read-only by default: HTTP mode has no application confirmation dialog, so the write tools are left out even with
FERRY_MCP_ENABLE_WRITES=true, unless you also setFERRY_MCP_HTTP_ALLOW_WRITES=true. - Request limits: POST only, with a 1 MB body limit.
The repository includes a Dockerfile for HTTP mode. Beyond a private network, put the service behind TLS.
Common mistakes
Section titled “Common mistakes”| Misconception | Reality | Correct practice |
|---|---|---|
| Ask the agent to issue tickets or pay | The server has no tool that does either | Issue tickets through the Dashboard, SDK, or CLI after a human review |
| Enabling writes lets the agent book on its own | Every write still needs human confirmation | Read the dialog content carefully before agreeing |
| Expose the HTTP port to a network without a token | The server acts with your API key | Set FERRY_MCP_HTTP_TOKEN and put it behind TLS |
| The agent can share customer data freely | Read results contain personal information | Hide the read tools with FERRY_MCP_DISABLED_TOOLS when not needed |
| Trying on UAT is as safe as a sandbox | Issuing tickets on UAT books real tickets and spends real money | Only issue on voyages agreed with Ferry; see Environments |
Where to look in Scalar
Section titled “Where to look in Scalar”Configuration details and the release file are in the MCP package README on GitHub and on the releases page. The raw HTTP reference is on Scalar UI.
| What to look up | Where |
|---|---|
| Full tool list and environment variables | The MCP package README on GitHub |
| Installer file for Claude Desktop | The latest release on GitHub |
| HTTP routes and schemas behind each tool | Scalar, groups Voyages, Bookings, Cancel Ticket Requests |
Read next
Section titled “Read next”| Page | When to read |
|---|---|
| TypeScript SDK | Call the API from your own code |
| The ferry CLI | Manual work with confirmation when issuing tickets |
| Authentication with an API key | Understand the power of the key the server uses |
| The booking flow | Understand the booking state create_booking produces |