An assistant can now price and prepare a private chauffeur booking in Paris on its own: resolve the address, compare vehicle classes, get the exact fare for a date, say where the driver waits at Terminal 2E, and hand the traveller a link to confirm. Our Model Context Protocol server is public, and a read-only key is published in the documentation so you can try it in under a minute.
This post is the engineering side of that launch: the one design decision we care about, and the four things that broke before it was safe to publish. If you are building an MCP server over a real transactional business, the failures are the useful part.
The decision: the agent prepares, the human pays
No tool on our server can charge a card. create_booking_draft returns a confirmation link on privatedrive.co, and the traveller reviews the ride and pays there. That is deliberate, and it survived every review.
The reason is not squeamishness about automation. It is that the irreversible step deserves a human, and that this boundary is what makes the server safe to wire into someone else's product. A travel platform can call us without holding payment liability, and a traveller keeps a moment to look at the price before it becomes a charge. We may loosen this later for partners under contract. We will not loosen it for a public key.
Four things that broke
1. A quote without a date is a wrong quote
Our first version passed the addresses and the vehicle to the pricing engine, and forgot the date. Every quote came back at the base fare. A ride during Fashion Week or in August high season was quoted at the low-season price, which the website would not have honoured on the day.
The fix is one line, but the lesson is not: a pricing engine with date-dependent rules will happily answer without a date. It has no way to know the caller meant a specific day. If your prices move with the calendar, make the date mandatory at the tool boundary, and test a date you know carries a surcharge.
2. The timezone trap, and this one books the wrong day
This is the failure we would most like other builders to avoid. We accepted an ISO 8601 datetime and split the string on the letter T to get the date and the time. Clean, obvious, wrong.
Assistants normalise to UTC constantly. A traveller asking for a pickup at 01:00 on 1 October produces 2026-09-30T23:00:00Z from a well-behaved model. Our naive split read that literally and booked 30 September at 23:00: the previous day, two hours early. The price was consistent, so nothing looked wrong in testing. What would have failed is the morning of the ride, with a driver at the wrong place on the wrong day.
We caught it by sending the same instant in three formats and reading what actually landed in the database. Bare local, UTC, and an explicit offset now converge on the same booking. If you take datetimes from an agent, convert to your operating timezone properly, reject what you cannot parse, and never trust a string split.
3. Documented permissions are not permissions
Our API keys carried a list of allowed tools and a rate limit. Both fields had been in the database for months. Neither was ever read. Any valid key could call any tool, including the one that issues refunds.
Worse, once we fixed the code, the schema quietly undid it: the default value for the permission list was "everything", and the database driver fills in defaults when it reads a document, not only when it writes one. A key inserted without that field came back as a full administrator. The application-level check never saw a missing value, so it never refused.
Two habits came out of this. Search for the caller of every security field, because a column named allowedTools proves nothing. And make the default closed, then verify it by actually inserting a partial document and reading it back rather than by reading the code.
4. Business rules must refuse, not warn
We priced 7 passengers into a 3-seat sedan and returned a fare, with a note that 3 vehicles would be needed. A model reads the fare and quotes the fare. We also had no way to express a day trip, so a request to spend the day at Versailles came back as a one-way drop-off: cheaper, and it leaves the traveller stranded when the driver leaves.
The general rule, once you are serving language models rather than forms: anything you express as a warning will be ignored eventually. If a request violates a real constraint, the server has to return an error that names the correct alternative. Our capacity check now refuses and points at the class that fits. Day trips are a first-class parameter with their own fixed rates.
What the tools do
Nine tools, split across three access levels. Reading is open with the published key: address search, vehicle classes, exact quotes, availability, service areas, and per-terminal meeting points at Charles de Gaulle, Orly, Le Bourget and Beauvais. A key issued in one request adds booking drafts and booking lookup. Cancellations, which move real money, stay with partner keys.
Prices come from the same engine as the website, so a quote for a given date matches what a human visitor sees for that date, seasonal and event rates included. Child seats are free and do not need to be priced in. Airport pickups include 60 minutes of free waiting after the actual landing, and the flight is tracked.
Try it
The integration documentation carries a public key, a working curl example, the client configuration for Claude Desktop and Cursor, the full tool reference and the error table. Registering your own key for booking drafts is a single request and takes effect immediately.
If you are researching Paris ground transport rather than building on it, our complete guide to Paris airport transfers and our guide for travellers arriving in Paris cover the same ground for humans. Companies with recurring needs will find the mechanics in our guide to setting up a corporate ground transport account, and assistants booking for executives can use the EA's guide to Paris ground transport.
Questions about the server, or a bug in a tool response: [email protected], with the tool name and the arguments you sent.
