{"slug": "my-vacation-agent-is-struggling-how-ai-ready-are-services", "title": "My vacation agent is struggling: how AI-ready are services?", "summary": "A writer's one-off test of an AI vacation-booking agent found that the three travel providers he pointed it at differed sharply in AI-readiness: two had no API and no Model Context Protocol (MCP) server, forcing the agent to use a browser, while the third offered an MCP server the agent connected to directly. Using the same reusable Claude Agent Skill, called `vacation-scout`, with identical criteria and a fixed 100-point scoring formula, the MCP-connected provider returned a different answer that was also the correct one, leading the author to conclude that a provider's readiness for an AI reader changes what an agent can conclude and has little to do with which protocol is offered. The author framed the result as a single-run testimony rather than a benchmark, run once on one campaign with one model.", "body_md": "# My vacation agent is struggling: how AI-ready are services?\n\nMy wife and I wanted a few days on the French west coast in August, without the children. Two adults, a dog, somewhere with a spa, under €1,500, leaving around the 15th. Nothing exotic, except that we were booking it in the first week of August, because we always book late.\n\nComparing that properly across several sites is tedious, and I’m not good at it. Every site quotes prices differently, the filters never line up, and by the fourth tab I’ve forgotten what the second one offered. So instead of opening six tabs, I wrote a skill and let it do the comparing.\n\nThat decision turned into an accidental experiment. The first two providers I pointed the agent at had no API and no [Model Context Protocol (MCP) server](https://blog.postman.com/what-is-mcp-server/), so the only route in was a browser. The third had an MCP server I could connect to directly. Same skill, same criteria, same scoring, two completely different experiences for the agent. One of them also gave me a different answer, and the different answer was the correct one.\n\nThis is a testimony, not a benchmark. I ran it once, on one campaign, with one model, for my own holiday. But it turned into a useful lesson about how much the industry still has to change to work in a world where software does the shopping. The interesting part is not that MCP was faster. It’s that how ready a provider is for an AI reader changes what the agent can conclude, and readiness turned out to have surprisingly little to do with which protocol was on offer.\n\nA note on naming: I’ve named every service I tested, because a testimony you can’t reproduce isn’t worth much. None of this is a verdict on those companies. It’s one run, on one campaign, on one day, and a site that frustrated my agent in July may have shipped a fix since. Read it as a snapshot of what an agent met, not a review.\n\n## The skill, before the search\n\nThe first thing I built was not a search. It was a reusable [Agent Skill](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) called `vacation-scout`, so the criteria and the scoring survived between sessions and I could re-run the same campaign later. A skill is a folder with a `SKILL.md` file, some frontmatter, and instructions:\n\n```\n---\nname: vacation-scout\ndescription: >\n  Search configured travel providers for vacation offers matching a saved\n  campaign (dates, budget, region, theme, pet policy), score every offer\n  out of 100, and return one ranked table across all providers.\n---\n```\n\nIts body defines four things: a campaign model, a per-provider search procedure, an extraction spec, and a scoring formula. That extraction spec matters more than it sounds. It lists the exact fields to pull per offer and includes one hard rule: if a field is not on the page, mark it `\"not specified\"` and never guess.\n\n```\n{\n  \"title\": \"4-star thalasso resort, Concarneau\",\n  \"price_total_eur\": 859,\n  \"nights\": 4,\n  \"travelers\": 2,\n  \"check_in\": \"2026-08-15\",\n  \"location\": \"Concarneau, Finistère\",\n  \"theme\": [\"spa\", \"thalasso\"],\n  \"pets_allowed\": \"not specified\",\n  \"breakfast_included\": false,\n  \"url\": \"https://provider.example.com/offers/1a2b3c\"\n}\n```\n\nThe scoring formula is deliberately boring, which is what makes it a fair constant across providers:\n\n```\n// Score one offer out of 100 against the saved campaign.\nfunction scoreOffer(offer, campaign) {\n  let score = 0;\n\n  score += priceFit(offer.price_total_eur, campaign.budget_eur) * 30;\n  score += themeMatch(offer.theme, campaign.theme) * 25;\n  score += geoMatch(offer.location, campaign.regions) * 20;\n  score += dateMatch(offer.check_in, campaign.window) * 15;\n  score += durationMatch(offer.nights, campaign.nights) * 10;\n\n  // Pet policy is a hard requirement, applied after the base total.\n  if (offer.pets_allowed === true) score += 10;\n  else if (offer.pets_allowed === false) score -= 20;\n  else score -= 5; // \"not specified\" costs you, but less than a no\n\n  return Math.max(0, Math.min(100, Math.round(score)));\n}\n```\n\nThat `else score -= 5` line is the whole post, in a way. A missing field is not neutral. It’s a small, silent tax on the quality of the answer, and I’ll come back to it.\n\nHere’s the campaign I saved:\n\n| Criterion | Value | \n|---|---|\n| Departure | 15 August 2026, flexible after | \n| Duration | 4 or 5 nights | \n| Travelers | 2 adults | \n| Dog allowed | Required | \n| Region | France, Atlantic or Channel coast | \n| Theme | Spa, thalasso, relaxation | \n| Budget | Under €1,500 total | \n| Nice to have | Breakfast included | \n\n## Two providers, zero machine-readable entry points\n\nNeither had a public API, and neither had an MCP server. So the agent drove a real browser: read the accessibility tree, click, screenshot, read again.\n\nStaycation needed no account. It also turned out to sell 24-hour and 48-hour city getaways by default, and the multi-night option was hidden behind a secondary dropdown inside the date picker. The first two clicks on that dropdown closed the entire dialog. Coordinate-based clicks landed on the right pixels, and the handler still didn’t fire the way the agent expected. Switching to element-reference clicks after re-reading the accessibility tree fixed it. There was no pet filter anywhere on the results page, so confirming a pet policy meant opening each candidate’s detail page and reading the text. Four out of four candidates had no pet policy text at all. About 33 tool calls, 10 of them full-page screenshots.\n\nVeepee had the better filters of the two, including an actual “pets accepted” facet, which felt like a win until combining it with the wellness facet behaved like an OR rather than an AND in the result count. Most of the pet-friendly coastal results were self-catering apartments with a seven-night minimum stay, and that minimum was invisible from the results grid. It only appeared after opening an individual listing, so the agent burned turns on inventory that could never satisfy a four-night trip. Narrowing to hotels collapsed the set to exactly one property: a real thalasso hotel, pets confirmed, and 39% over budget for August. About 32 tool calls, 19 of them screenshots.\n\nRoughly 65 tool calls across the two. About 29 of them screenshots. One manual sign-in.\n\n## What the ranking looked like after the scrape\n\n| Rank | Score | Offer | Provider | Location | Price, 4 nights, 2 people | Dog allowed | \n|---|---|---|---|---|---|---|\n| 1 | 95 | 3-star hotel and spa | Staycation | Morbihan, Atlantic | €815 | Not specified | \n| 2 | 95 | 4-star thalasso resort | Veepee | Finistère, Atlantic | €2,084 | Yes, confirmed | \n| 3 | 92 | 4-star thalasso hotel | Staycation | Île de Ré, Atlantic | €1,267 | Not specified | \n| 4 | 91 | 4-star hotel | Staycation | Saint-Malo, Channel | €1,408 | Not specified | \n| 5 | 80 | 4-star hotel | Staycation | La Baule, Atlantic | €1,471 | Not specified | \n\nFour of the five had no retrievable pet policy. I’m traveling with a dog. The single criterion that could disqualify an entire booking was unknown on most of the shortlist, and the agent had done nothing wrong: the information was not on the page.\n\n## Why the bill tracks turns, not information\n\nI estimated the content-only token floor for the browser phase at around 60,000 input tokens and 4,300 output tokens. At the [current Claude Sonnet 5 rate](https://platform.claude.com/docs/en/about-claude/pricing) of $2 per million input tokens and $10 per million output, that’s about $0.16. Which sounds like nothing.\n\nIt is nothing, and it’s also not the bill. The Messages API is stateless, so every turn re-sends the full conversation: every prior screenshot, every prior page extraction, the system prompt, the skill text, and the schema of every loaded tool. For a general-purpose browser-automation agent, that fixed footprint is realistically somewhere in the 15,000 to 30,000 token range before you’ve done anything.\n\nTake 20,000 as a working number:\n\n``` php\nPer provider (~33 turns):\n  33 x 20,000  =   660,000 tokens of repetition  ->  ~$1.32\nBoth providers (~65 turns):\n  65 x 20,000  = 1,300,000 tokens of repetition  ->  ~$2.60\nContent floor for both:                              ~$0.16\n```\n\nWith [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) active, cache reads bill at roughly 10% of the base input rate with periodic write refreshes, which pulls that figure closer to $0.65 to $0.85. Either way, the repetition dominates the actual information by a factor of four or more. A long browser loop costs what it costs because of how many times you go around, not because of how much you learn per lap.\n\nOne caveat on applying that same arithmetic to the MCP run: you can’t. The 20,000-token footprint is a general-purpose browser toolbox plus accumulated screenshots. lastminute.com’s session carried eight compact tool schemas and no images, so its per-turn footprint is a different and much smaller number that I did not measure. Fewer turns is the part I can count. A lighter turn is the part I can only argue. Either way, a narrower tool surface attacks both sides of that multiplication at once.\n\n## lastminute.com had an MCP server\n\nThe third provider published an [MCP](https://modelcontextprotocol.io/) endpoint. That gave the agent a set of typed tools instead of a viewport.\n\n### Check the contract before the agent uses it\n\nI did not wire the endpoint into the skill first. I opened it in Postman as an [MCP request](https://learning.postman.com/docs/use/send-requests/protocols/mcp-requests/overview) and looked at what it actually exposed, the same way I’d read an OpenAPI document before writing a client for it.\n\nCreate a new MCP request, point it at the server URL, and list the tools:\n\n```\nhttps://mcp.lastminute.com/mcp\n```\n\nThe response tells you what the agent will be working with, including whether the fields you care about exist as real types:\n\n```\n{\n  \"tools\": [\n    {\n      \"name\": \"resolve_destination_id\",\n      \"description\": \"Resolve a place name to a numeric destination ID\",\n      \"inputSchema\": {\n        \"type\": \"object\",\n        \"properties\": { \"query\": { \"type\": \"string\" } },\n        \"required\": [\"query\"]\n      }\n    },\n    {\n      \"name\": \"search_only_hotel\",\n      \"description\": \"Search hotels by destination, dates, and occupancy\",\n      \"inputSchema\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"destination_id\": { \"type\": \"integer\" },\n          \"check_in\": { \"type\": \"string\", \"format\": \"date\" },\n          \"check_out\": { \"type\": \"string\", \"format\": \"date\" },\n          \"adults\": { \"type\": \"integer\" }\n        },\n        \"required\": [\"destination_id\", \"check_in\", \"check_out\", \"adults\"]\n      }\n    },\n    {\n      \"name\": \"select_hotel_options\",\n      \"description\": \"Room-level detail including a typed policies object\"\n    }\n  ]\n}\n```\n\nTwo minutes of reading told me the thing I most needed to know: `select_hotel_options` returns a `policies` object with a `pets_allowed` boolean, which is a very different thing from a sentence about pets.\n\nRunning a few sample calls by hand before letting the agent loose is ordinary integration hygiene, and it paid for itself immediately. It also caught a formatting quirk in the price filter that would otherwise have surfaced mid-run. If you’re on the other side of this and publishing a server, the same checks apply from the [MCP server docs](https://learning.postman.com/docs/postman-ai/mcp-servers/overview) before you ship one.\n\n### The same skill, 15 tool calls\n\nThen I re-ran the identical campaign. Five coastal towns, the same 15 to 19 August dates, the same scoring function.\n\n```\n{\n  \"hotel_id\": \"fr-29900-0117\",\n  \"name\": \"4-star thalasso resort\",\n  \"price\": { \"total\": 859, \"currency\": \"EUR\", \"board\": \"room_only\" },\n  \"stars\": 4,\n  \"rating\": 8.4,\n  \"distance_to_center_km\": 1.2,\n  \"facilities\": [\"spa\", \"thalassotherapy\", \"indoor_pool\", \"parking\"],\n  \"policies\": {\n    \"pets_allowed\": true,\n    \"check_in_from\": \"15:00\",\n    \"cancellation\": \"free_until_2026-08-08\"\n  }\n}\n```\n\nSide by side, per provider:\n\n| Metric | Staycation | Veepee | lastminute.com (MCP) | \n|---|---|---|---|\n| Tool calls | ~33 | ~32 | ~15 | \n| Screenshots | ~10 | ~19 | 0 | \n| Estimated input tokens, content floor | ~27,000 | ~33,000 | ~10,300 | \n| Estimated output tokens | ~2,000 | ~2,300 | ~2,500 | \n| Estimated cost, floor | ~$0.07 | ~$0.09 | ~$0.05 | \n| Wall clock | ~5 to 7 min | ~5 to 7 min | ~65 sec | \n| Sign-in required | No | Yes, mid-run | Yes, once at setup | \n| Offers with a readable pet policy | 0 of 4 | 1 of 1 | Every offer queried | \n\nPer provider, the turn count drops from about 33 to about 15. That’s a bit better than half, not the four-fold cut the session totals suggest. The screenshot row is where the gap is not close: around 15 per browser provider, against zero.\n\nEach browser provider took somewhere around five to seven minutes of clicking, reading, and waiting. The MCP data pull took about 65 seconds. I wasn’t running a stopwatch, so treat those as the rough shape of it rather than measurements.\n\nOn authentication, the fair comparison is where the sign-in sits, not whether there is one. The MCP connector did need authorizing, and I went through that flow like any other. But I did it once, before the run, and never touched it again. Veepee pulled me into the loop mid-search, and the agent sat idle waiting on a human. That’s the real difference: you pay setup friction once, and you pay interactive friction every time the agent runs.\n\nWorth saying plainly in the other direction too: lastminute.com’s 15 calls covered five coastal towns, which is wider ground than either browser provider got in its ~33. So the per-provider row understates the gap somewhat. I’d rather point that out than quietly bank it.\n\n## The part I did not expect: the answer changed\n\nI went in expecting cheaper and faster. I got a different result, and that’s the finding I keep thinking about.\n\nRunning the detail call on the properties that mapped to the browser-phase winners rewrote the top of the ranking:\n\n- The browser phase’s number one, scored 95 with pets “not specified,” came back `pets_allowed: false` . Under the same formula, that flips a minus-5 into a minus-20 and drops it to 80. My top recommendation was disqualifying for a dog owner, and scraping never surfaced that, because the page never said it.\n- The browser phase’s number three, also “not specified,” came back `pets_allowed: true` . A clean upgrade from a guess to a fact.\n\nStructured search returned fields the point-and-click pass could not read, on the one criterion that decided the booking.\n\nCheaper and faster would have been a nice footnote. Being right about the dog is the part that actually changed where we slept.\n\n## What this test is not\n\nSetting expectations honestly, because I’d want someone to do the same for me:\n\n- It’s one campaign, run once. No repeated trials, no confidence intervals, nothing controlled.\n- The token and cost figures are estimates built from tool-call counts and typical per-call sizes, not exact metering. Pull the real numbers from your own usage dashboard before quoting any of them.\n- These three are not equivalent businesses with equivalent inventory. A flash-sale club and a global aggregator stock different rooms, so some of the difference in results is catalog, not protocol.\n- I never ran the campaign against an ordinary REST API, which is the tier most services occupy. Every travel API I looked at needed a partner agreement first, so that comparison is still missing.\n- Two browser providers and one MCP provider is a small sample. The per-provider table is the fair read, the session totals are not, and one MCP server is no evidence that every MCP server behaves this way.\n- The browser-driving tooling is a research preview and behaved like one, including a mid-session disconnect.\n\nNone of those caveats move the part that mattered to me. Four of five offers with an unknowable pet policy is not a measurement artifact. That’s what a webpage had to say on the subject.\n\n## Agent experience is the thing, not the protocol\n\nThe tempting conclusion is “ship an MCP server.” That’s not quite it, and it lets too many teams off the hook.\n\nWhat actually changed between the two phases was how much work the agent had to do to reach a fact, and how confident it could be once it got there. A boolean beats a paragraph. A typed price with a `board` field beats a number on a card that might be per person, per room, or per stay. A filter the server applies beats a filter the agent has to re-verify. MCP is one good way to deliver that, and [now that the protocol is stateless](https://blog.postman.com/mcp-goes-stateless-and-postmans-ready/) it deploys like any ordinary HTTP service, which removes most of the operational excuse for not having one.\n\nIt’s not the only way either, and I’d be overselling one afternoon’s experiment if I claimed otherwise. A REST API with an accurate [OpenAPI](https://www.openapis.org/) document and honestly typed fields hands an agent exactly the same thing. So does a published SDK. So does a command-line tool with predictable output and real exit codes. What my agent needed was a value it could act on. Whether that arrives over MCP, over HTTP, or out of a binary matters far less than whether it arrives as a type or as a sentence somebody has to interpret. I’d expect lastminute.com is reading that boolean out of their own database rather than inventing it at the protocol layer.\n\nSo Staycation and Veepee didn’t lose because they lacked MCP. They lost because there was no machine-readable path to their inventory in any form, so the only reader they had ever designed for was a human with a mouse.\n\nThat splits into two questions worth keeping separate. Can an agent find and reach your data at all, and once it has, can it tell what it’s holding? The first is a discovery problem, and almost anything machine-readable solves it: a specification, a collection, an SDK, a command-line tool, a server. The second is a modelling problem, and no protocol fixes it for you. You can ship a well-built API and still answer “does this hotel take dogs” with a paragraph of prose.\n\nSome of the traffic reading your API now is software working on somebody’s behalf, and it can’t infer what you meant from a page layout or a helpfully worded sentence.\n\n## Score your own API\n\nIf you want to know how legible your APIs are to an agent before someone else finds out for you, Postman published an assessment for exactly this: the [AI-Ready APIs score](https://www.postman.com/ai/ai-ready-apis/). It grades how well agents like Claude, ChatGPT, and Cursor can find, read, and build with what you publish across your specifications, docs, collections, SDKs, and MCP servers, and there’s a public leaderboard.\n\nNote what it doesn’t do. It doesn’t ask whether you shipped an MCP server. It asks whether an agent can find you, read you, and act on you without a human standing in the middle. Which is all my agent ever wanted, on its way to finding us a hotel.\n\nRun the assessment on your own organization and read the low scores as a to-do list, not a grade. Missing types, undocumented enums, and fields that exist in the response but not in the specification are the exact things that turn into “not specified” in someone’s agent output. If your Agent Understanding row is the weak one, the fix is usually cheap: an `AGENTS.md`, an `llms.txt`, a public Postman Collection, and structured data on the docs pages.\n\nI got a better hotel out of this, which was the entire point. Four nights on the Finistère coast, thalasso spa on site, in budget, and a dog who is welcome in writing rather than on a hunch.\n\nWhat I also got was a much clearer sense of which providers had thought about who, or what, would be reading them.", "url": "https://wpnews.pro/news/my-vacation-agent-is-struggling-how-ai-ready-are-services", "canonical_source": "https://blog.postman.com/my-vacation-agent-is-struggling-how-ai-ready-are-services/", "published_at": "2026-11-03 16:00:12+00:00", "updated_at": "2026-11-03 16:19:57.036621+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools"], "entities": ["Model Context Protocol", "Claude Agent Skills", "vacation-scout", "Concarneau", "Finistère"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/my-vacation-agent-is-struggling-how-ai-ready-are-services", "markdown": "https://wpnews.pro/news/my-vacation-agent-is-struggling-how-ai-ready-are-services.md", "text": "https://wpnews.pro/news/my-vacation-agent-is-struggling-how-ai-ready-are-services.txt", "jsonld": "https://wpnews.pro/news/my-vacation-agent-is-struggling-how-ai-ready-are-services.jsonld"}}