@scavio/mcp-server 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/lib/trim-response.d.ts +6 -0
  2. package/dist/lib/trim-response.js +26 -0
  3. package/dist/lib/trim-response.js.map +1 -0
  4. package/dist/tools/airbnb.js +30 -49
  5. package/dist/tools/airbnb.js.map +1 -1
  6. package/dist/tools/amazon.js +11 -18
  7. package/dist/tools/amazon.js.map +1 -1
  8. package/dist/tools/appstore.js +16 -15
  9. package/dist/tools/appstore.js.map +1 -1
  10. package/dist/tools/booking.js +28 -45
  11. package/dist/tools/booking.js.map +1 -1
  12. package/dist/tools/capterra.js +16 -15
  13. package/dist/tools/capterra.js.map +1 -1
  14. package/dist/tools/companieshouse.js +11 -10
  15. package/dist/tools/companieshouse.js.map +1 -1
  16. package/dist/tools/ebay.js +21 -30
  17. package/dist/tools/ebay.js.map +1 -1
  18. package/dist/tools/extract.js +6 -5
  19. package/dist/tools/extract.js.map +1 -1
  20. package/dist/tools/g2.js +23 -22
  21. package/dist/tools/g2.js.map +1 -1
  22. package/dist/tools/glassdoor.js +18 -17
  23. package/dist/tools/glassdoor.js.map +1 -1
  24. package/dist/tools/google.js +154 -153
  25. package/dist/tools/google.js.map +1 -1
  26. package/dist/tools/googleads.js +20 -19
  27. package/dist/tools/googleads.js.map +1 -1
  28. package/dist/tools/googleplay.js +14 -13
  29. package/dist/tools/googleplay.js.map +1 -1
  30. package/dist/tools/homedepot.js +15 -28
  31. package/dist/tools/homedepot.js.map +1 -1
  32. package/dist/tools/indeed.js +20 -39
  33. package/dist/tools/indeed.js.map +1 -1
  34. package/dist/tools/instagram.js +59 -58
  35. package/dist/tools/instagram.js.map +1 -1
  36. package/dist/tools/kuaishou.js +47 -46
  37. package/dist/tools/kuaishou.js.map +1 -1
  38. package/dist/tools/linkedin.js +26 -25
  39. package/dist/tools/linkedin.js.map +1 -1
  40. package/dist/tools/metaads.js +21 -20
  41. package/dist/tools/metaads.js.map +1 -1
  42. package/dist/tools/reddit.js +48 -47
  43. package/dist/tools/reddit.js.map +1 -1
  44. package/dist/tools/redfin.js +29 -28
  45. package/dist/tools/redfin.js.map +1 -1
  46. package/dist/tools/sec.js +36 -35
  47. package/dist/tools/sec.js.map +1 -1
  48. package/dist/tools/target.js +19 -40
  49. package/dist/tools/target.js.map +1 -1
  50. package/dist/tools/threads.js +20 -19
  51. package/dist/tools/threads.js.map +1 -1
  52. package/dist/tools/tiktok-shop.js +38 -37
  53. package/dist/tools/tiktok-shop.js.map +1 -1
  54. package/dist/tools/tiktok.js +54 -53
  55. package/dist/tools/tiktok.js.map +1 -1
  56. package/dist/tools/tripadvisor.js +20 -43
  57. package/dist/tools/tripadvisor.js.map +1 -1
  58. package/dist/tools/usage.js +3 -2
  59. package/dist/tools/usage.js.map +1 -1
  60. package/dist/tools/walmart.js +35 -34
  61. package/dist/tools/walmart.js.map +1 -1
  62. package/dist/tools/x.js +44 -43
  63. package/dist/tools/x.js.map +1 -1
  64. package/dist/tools/yelp.js +21 -20
  65. package/dist/tools/yelp.js.map +1 -1
  66. package/dist/tools/youtube.js +77 -76
  67. package/dist/tools/youtube.js.map +1 -1
  68. package/dist/tools/zillow.js +38 -53
  69. package/dist/tools/zillow.js.map +1 -1
  70. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ export declare function trimResponse(data: unknown): {
2
+ content: [{
3
+ type: "text";
4
+ text: string;
5
+ }];
6
+ };
@@ -0,0 +1,26 @@
1
+ function stripEmpty(val) {
2
+ if (val === null || val === undefined || val === "")
3
+ return undefined;
4
+ if (Array.isArray(val)) {
5
+ const filtered = val.map(stripEmpty).filter((v) => v !== undefined);
6
+ return filtered.length ? filtered : undefined;
7
+ }
8
+ if (typeof val === "object") {
9
+ const out = {};
10
+ let hasKey = false;
11
+ for (const [k, v] of Object.entries(val)) {
12
+ const clean = stripEmpty(v);
13
+ if (clean !== undefined) {
14
+ out[k] = clean;
15
+ hasKey = true;
16
+ }
17
+ }
18
+ return hasKey ? out : undefined;
19
+ }
20
+ return val;
21
+ }
22
+ export function trimResponse(data) {
23
+ const trimmed = stripEmpty(data) ?? {};
24
+ return { content: [{ type: "text", text: JSON.stringify(trimmed) }] };
25
+ }
26
+ //# sourceMappingURL=trim-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trim-response.js","sourceRoot":"","sources":["../../src/lib/trim-response.ts"],"names":[],"mappings":"AAEA,SAAS,UAAU,CAAC,GAAc;IAChC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAEtE,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAgB,CAAC;QACnF,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,GAAG,GAA8B,EAAE,CAAC;QAC1C,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,UAAU,CAAC,CAAc,CAAC,CAAC;YACzC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;gBACf,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAClC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAiB,CAAC,IAAI,EAAE,CAAC;IACpD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;AACxE,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { handleApiError } from "../lib/tool-error.js";
3
+ import { trimResponse } from "../lib/trim-response.js";
3
4
  // No zod `.default()` anywhere in this file, on purpose. The MCP SDK applies a
4
5
  // zod default BEFORE the handler runs, so a defaulted field is posted on every
5
6
  // single call whether the model set it or not — which is exactly how the
@@ -11,107 +12,87 @@ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
11
12
  // tool schemas share an instance.
12
13
  const stayFields = () => ({
13
14
  check_in: z.string().regex(ISO_DATE, "check_in must be YYYY-MM-DD").optional()
14
- .describe("Check-in date, YYYY-MM-DD. MUST be sent together with check_out — Airbnb prices a window, not a day."),
15
+ .describe("YYYY-MM-DD. Must pair with check_out."),
15
16
  check_out: z.string().regex(ISO_DATE, "check_out must be YYYY-MM-DD").optional()
16
- .describe("Check-out date, YYYY-MM-DD. Must be after check_in and sent together with it."),
17
+ .describe("YYYY-MM-DD. Must pair with check_in."),
17
18
  adults: z.number().int().min(1).optional()
18
- .describe("Adults in the party."),
19
+ .describe("Adults."),
19
20
  children: z.number().int().min(0).optional()
20
- .describe("Children aged 2-12."),
21
+ .describe("Children 2-12."),
21
22
  infants: z.number().int().min(0).optional()
22
23
  .describe("Infants under 2."),
23
24
  pets: z.number().int().min(0).optional()
24
- .describe("Pets travelling with the party."),
25
+ .describe("Pets."),
25
26
  });
26
27
  const currencyField = () => z.string().regex(/^[A-Za-z]{3}$/, "currency must be a 3-letter ISO 4217 code").optional()
27
- .describe("ISO 4217 display currency, e.g. 'USD'. Defaults to USD; without it Airbnb prices off the proxy exit and two identical requests can disagree.");
28
+ .describe("Currency, e.g. 'USD'. Default USD.");
28
29
  const listingIdField = () => z.string().min(1).max(500)
29
- .describe("Airbnb listing id, e.g. '1704452615554844556', or a full airbnb.com/rooms/ URL. Query parameters on a pasted URL are discarded — they carry someone else's dates.");
30
+ .describe("Listing id or airbnb.com/rooms/ URL.");
30
31
  export function registerAirbnbTools(server, getClient) {
31
- server.tool("search_airbnb", `Search Airbnb stays in a location as JSON. Each listing carries the STAY-TOTAL and PER-NIGHT price with the full discount ledger, rating and review count, bedrooms/beds/baths, coordinates, badges (Superhost, Guest Favourite), images, and the listing id.
32
-
33
- This is the ONLY source of Airbnb prices. get_airbnb_listing returns no nightly rate under any parameters, so any pricing question has to be answered from here.
34
-
35
- Send check_in and check_out TOGETHER. A dateless search silently defaults to a window 30 days out for 5 nights AND Airbnb A/B-tests both the window and the prices — the same URL has returned first-row prices of $680, $802 and $2,238 across runs. The response flags this as dates_are_defaulted. Pass explicit dates for anything you intend to reproduce or compare.
36
-
37
- min_price and max_price are WHOLE-STAY totals, not per night. currency defaults to USD. room_type and the amenity names are validated before the scrape on purpose: an unrecognised value is not an error upstream, it returns the UNFILTERED set under a 200.
38
-
39
- Paginate with page (18 listings per page) OR with cursor from a previous response — never both, because cursor wins and the request is rejected rather than silently billed for a discarded page number. Costs 1 credit per page.`, {
32
+ server.tool("search_airbnb", `Search Airbnb stays. ONLY source of prices (get_airbnb_listing has none). Send check_in+check_out together or prices default to random window. Prices are whole-stay totals, not per night. Paginate with page OR cursor, not both. 1 credit/page.`, {
40
33
  location: z.string().min(1).max(200)
41
- .describe("Where to search: a city ('Austin, TX'), a region ('Lake Tahoe'), a ZIP, or a pasted airbnb.com/s/ URL. A location Airbnb cannot resolve is a 404, not an empty result."),
34
+ .describe("City, region, ZIP, or airbnb.com/s/ URL."),
42
35
  ...stayFields(),
43
36
  min_price: z.number().min(0).optional()
44
- .describe("Minimum price for the WHOLE STAY (not per night), in the currency field."),
37
+ .describe("Min whole-stay total (not per night)."),
45
38
  max_price: z.number().min(0).optional()
46
- .describe("Maximum price for the WHOLE STAY (not per night), in the currency field. Must not be below min_price."),
39
+ .describe("Max whole-stay total."),
47
40
  room_type: z.enum(["entire_home", "private_room", "shared_room", "hotel_room"]).optional()
48
- .describe("Restrict to one kind of place. Closed set — an unrecognised value would return the UNFILTERED set under a 200 upstream, so it is rejected here."),
41
+ .describe("Room type filter."),
49
42
  min_bedrooms: z.number().int().min(0).optional()
50
- .describe("Minimum bedrooms."),
43
+ .describe("Min bedrooms."),
51
44
  min_beds: z.number().int().min(0).optional()
52
- .describe("Minimum beds."),
45
+ .describe("Min beds."),
53
46
  min_bathrooms: z.number().int().min(0).optional()
54
- .describe("Minimum bathrooms."),
47
+ .describe("Min baths."),
55
48
  superhost: z.boolean().optional()
56
- .describe("Only Superhost listings."),
49
+ .describe("Superhost only."),
57
50
  instant_book: z.boolean().optional()
58
- .describe("Only Instant Book listings."),
51
+ .describe("Instant Book only."),
59
52
  guest_favorite: z.boolean().optional()
60
- .describe("Only listings badged Guest Favourite."),
53
+ .describe("Guest Favourite only."),
61
54
  free_cancellation: z.boolean().optional()
62
- .describe("Only listings offering free cancellation."),
55
+ .describe("Free cancellation only."),
63
56
  amenities: z.string().min(1).max(200).optional()
64
- .describe("Comma-separated amenity filter, e.g. 'wifi,pool'. Named vocabulary: wifi, air_conditioning, pool, kitchen, free_parking, washer, self_check_in, tv — or raw numeric Airbnb amenity ids. An unrecognised NAME is rejected before the scrape, because upstream it would silently return the unfiltered set."),
57
+ .describe("Comma-separated, e.g. 'wifi,pool'. Names: wifi, air_conditioning, pool, kitchen, free_parking, washer, self_check_in, tv."),
65
58
  currency: currencyField(),
66
59
  page: z.number().int().min(1).optional()
67
- .describe("Results page, 1-based. 18 listings per page. CANNOT be combined with cursor — sending both is rejected."),
60
+ .describe("Page, 1-based. Cannot combine with cursor."),
68
61
  cursor: z.string().min(1).max(500).optional()
69
- .describe("A next_cursor from a previous response. Use INSTEAD of page — cursor wins, so sending both is rejected."),
62
+ .describe("Cursor from previous response. Cannot combine with page."),
70
63
  }, async (params) => {
71
64
  try {
72
65
  const data = await getClient().post("/api/v1/airbnb/search", params);
73
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
66
+ return trimResponse(data);
74
67
  }
75
68
  catch (err) {
76
69
  return handleApiError(err);
77
70
  }
78
71
  });
79
- server.tool("get_airbnb_listing", `Get one Airbnb listing in full as JSON: description, property and room type, capacity and room counts, the complete grouped amenity list (including the ones the place does NOT have), host profile and stats, house rules with parsed check-in/check-out times, cancellation policy, sleeping arrangements, photo tour and every image — and the RATING BREAKDOWN: six category ratings, the five-bucket star distribution, and Airbnb's AI-synthesised review tags.
80
-
81
- IT RETURNS NO NIGHTLY PRICE. The Airbnb room page carries no rate under ANY parameters, with or without dates. Prices are search-only: use search_airbnb for them.
82
-
83
- The rating breakdown lives here and NOT on get_airbnb_reviews, so this is the tool for aggregate review sentiment; get_airbnb_reviews is for individual review bodies.
84
-
85
- Accepts a listing id or a full /rooms/ URL (query parameters are discarded — they carry someone else's dates). The stay parameters are accepted and echoed back, but they do not price anything. No pagination. Costs 1 credit.`, {
72
+ server.tool("get_airbnb_listing", `Get full Airbnb listing details, amenities, host, rules, photos, rating breakdown. NO PRICES here; use search_airbnb for pricing. 1 credit.`, {
86
73
  listing_id: listingIdField(),
87
74
  ...stayFields(),
88
75
  currency: currencyField(),
89
76
  }, async (params) => {
90
77
  try {
91
78
  const data = await getClient().post("/api/v1/airbnb/listing", params);
92
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
79
+ return trimResponse(data);
93
80
  }
94
81
  catch (err) {
95
82
  return handleApiError(err);
96
83
  }
97
84
  });
98
- server.tool("get_airbnb_reviews", `Get Airbnb review BODIES for one listing as JSON: the guest's written comment, per-review rating, review date, and the reviewer's name, photo and location.
99
-
100
- It does NOT carry the rating breakdown or the review tags — those are on get_airbnb_listing. Use this tool when you need what guests actually wrote.
101
-
102
- Paginate with limit and offset: count is the listing's TOTAL review count, returned is how many this response holds, so page while offset + returned is below count. limit accepts 1-50 and defaults to 30 upstream. Send limit explicitly when paging — with no explicit limit the upstream returns a fixed 7 rows.
103
-
104
- Accepts a listing id or a full /rooms/ URL. Costs 1 credit per page.`, {
85
+ server.tool("get_airbnb_reviews", `Get Airbnb review text for a listing. Rating breakdown is on get_airbnb_listing, not here. Paginate with limit+offset. Send limit explicitly or upstream returns only 7. 1 credit/page.`, {
105
86
  listing_id: listingIdField(),
106
87
  currency: currencyField(),
107
88
  limit: z.number().int().min(1).max(50).optional()
108
- .describe("Review bodies per page, 1-50. Upstream default is 30. Send it explicitly when paging — with no explicit limit the upstream returns a fixed 7 rows."),
89
+ .describe("Reviews per page, 1-50. Default 30, but returns 7 if omitted."),
109
90
  offset: z.number().int().min(0).optional()
110
- .describe("Rows to skip, 0-based. Use with limit to page past the first set."),
91
+ .describe("Skip rows, 0-based."),
111
92
  }, async (params) => {
112
93
  try {
113
94
  const data = await getClient().post("/api/v1/airbnb/reviews", params);
114
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
95
+ return trimResponse(data);
115
96
  }
116
97
  catch (err) {
117
98
  return handleApiError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"airbnb.js","sourceRoot":"","sources":["../../src/tools/airbnb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"airbnb.js","sourceRoot":"","sources":["../../src/tools/airbnb.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { handleApiError } from "../lib/tool-error.js";
3
+ import { trimResponse } from "../lib/trim-response.js";
3
4
  // Amazon moved to a new upstream provider and now returns a normalized shape
4
5
  // instead of the old raw passthrough. Two consequences for these tool schemas:
5
6
  //
@@ -23,7 +24,7 @@ import { handleApiError } from "../lib/tool-error.js";
23
24
  // Kept in one place so search, product and offers cannot drift. `gb` is spelled
24
25
  // out because `uk` is the mistake a model makes here, and an unknown code
25
26
  // silently falls back to `us` rather than erroring, which would be invisible.
26
- const COUNTRY_DESCRIPTION = "Marketplace country code, ISO 3166-1 alpha-2, lowercase. Defaults to us. Valid: us, ae, au, be, br, ca, cn, de, eg, es, fr, gb, in, it, jp, mx, nl, pl, sa, se, sg, tr. Note the UK is 'gb'. An unrecognised code falls back to us instead of failing, so a typo silently returns US results.";
27
+ const COUNTRY_DESCRIPTION = "Country, e.g. 'us'. UK is 'gb'. Unknown codes silently fall back to us.";
27
28
  const countryField = z
28
29
  .string()
29
30
  .regex(/^[A-Za-z]{2}$/)
@@ -32,44 +33,36 @@ const countryField = z
32
33
  const asinField = z
33
34
  .string()
34
35
  .length(10)
35
- .describe("Amazon ASIN - the 10-character product id, e.g. 'B09V3KXJPB'. Extract it from the product URL (/dp/ASIN).");
36
+ .describe("ASIN, e.g. 'B09V3KXJPB'.");
36
37
  export function registerAmazonTools(server, getClient) {
37
38
  const call = (path) => async (params) => {
38
39
  try {
39
40
  const data = await getClient().post(path, params);
40
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
41
+ return trimResponse(data);
41
42
  }
42
43
  catch (err) {
43
44
  return handleApiError(err);
44
45
  }
45
46
  };
46
- server.tool("search_amazon", `Search an Amazon marketplace and return the product grid as JSON: query, page, total_results, count, products, filters and related_searches. Each product has asin, title, url, image, price (number) with currency, rating, reviews_count, is_sponsored, position, badge, sales_volume and delivery { is_free, date, fastest_date }. Use it to find products, compare prices or collect ASINs to pass to get_amazon_product and get_amazon_offers.
47
-
48
- There is NO sort option. The marketplace ignores every sort value and always returns its default relevance ranking, so results are unordered with respect to price, rating and date. To answer "the cheapest" or "the best rated", fetch the results and sort them yourself; do not expect the API to do it. There is also no category, merchant or price-range filter - filters[] carries the marketplace's own refinement URLs for reference only, and cannot be sent back as a param.
49
-
50
- Two fields to read carefully: reviews_count is derived from Amazon's rounded display value, so anything above 1000 is approximate (a page showing "1.3K" returns 1300); position is Amazon's grid slot index including ad and carousel slots, so it starts above 1 and has gaps. Costs 1 credit per page.`, {
47
+ server.tool("search_amazon", `Search Amazon products. No sort option (always relevance). No category/price filter. 1 credit/page.`, {
51
48
  query: z.string().min(1).max(500)
52
- .describe("Product search query, e.g. 'wireless noise cancelling headphones'."),
49
+ .describe("Product query."),
53
50
  country: countryField,
54
51
  page: z.number().int().min(1).optional()
55
- .describe("Results page, 1-based. One page per call, 1 credit each; there is no multi-page fetch."),
52
+ .describe("Page, 1-based."),
56
53
  }, call("/api/v1/amazon/search"));
57
- server.tool("get_amazon_product", `Get the full product page for an Amazon ASIN as JSON: title, brand, url, description, features, price, list_price, currency, rating, reviews_count, is_prime, has_buy_box, availability (free text such as "In Stock"), max_quantity, sold_by, other_sellers_count, sales_volume, climate_pledge_friendly, image, images, videos, best_sellers_rank, categories, specifications, variants and shipping { is_prime, zipcode, options }. Use it when you have a specific ASIN or Amazon product URL and need details, pricing or specs.
58
-
59
- Caveats worth knowing: price is the current buy-box price and other_sellers_count only counts the rest - call get_amazon_offers for the actual competing sellers and their prices. reviews[] carries review metadata only (id, author, date, verified_purchase); there is no review text and no per-review rating anywhere in the response. availability is marketplace- and language-specific free text, so match on the ASIN's own wording rather than parsing it for stock status. Costs 1 credit.`, {
54
+ server.tool("get_amazon_product", `Full Amazon product page for an ASIN. Price is buy-box price; use get_amazon_offers for competing sellers. 1 credit.`, {
60
55
  asin: asinField,
61
56
  country: countryField,
62
57
  }, call("/api/v1/amazon/product"));
63
- server.tool("get_amazon_offers", `List every seller currently offering an Amazon ASIN, as JSON: asin, title, image, rating, reviews_count, note, count, total_offers, has_more_pages, page and offers. Each offer has condition (New, Used - Like New, ...), seller_id, seller_name, ships_from, is_fulfilled_by_amazon, is_buy_box_winner, is_prime, is_national_prime, price with currency, list_price, shipping_price, discount_percentage, discount_amount, quantity, delivery { min_hours, max_hours, date, is_free } and prime_delivery { date, order_deadline }. Use it to find the cheapest seller for a known ASIN, to check who holds the buy box, to compare new against used pricing, or to see whether a third-party seller undercuts Amazon.
64
-
65
- This returns the first page of the offer list only - has_more_pages may be true, and there is no way to request the next page. An ASIN sold only by Amazon returns an empty offers list plus an explanatory note, which is a normal answer, not an error. Costs 1 credit.`, {
58
+ server.tool("get_amazon_offers", `All sellers for an Amazon ASIN with prices and conditions. First page only, no pagination. 1 credit.`, {
66
59
  asin: asinField,
67
60
  country: countryField,
68
61
  }, call("/api/v1/amazon/offers"));
69
- server.tool("get_amazon_options", `List the Amazon marketplaces the API supports, as JSON: countries (the two-letter codes to pass as the country param on search_amazon, get_amazon_product and get_amazon_offers) and domains (the matching amazon.* suffixes, for display only - the API does not take a domain). Two rows break the pattern a model usually assumes: amazon.com is 'us' and amazon.co.uk is 'gb'. The response also carries languages and currencies, which are always empty arrays because language and currency are not request params anywhere. Takes no arguments, costs no credits.`, {}, async () => {
62
+ server.tool("get_amazon_options", `List supported Amazon marketplaces and country codes. Free, no args.`, {}, async () => {
70
63
  try {
71
64
  const data = await getClient().get("/api/v1/amazon/options");
72
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
65
+ return trimResponse(data);
73
66
  }
74
67
  catch (err) {
75
68
  return handleApiError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"amazon.js","sourceRoot":"","sources":["../../src/tools/amazon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"amazon.js","sourceRoot":"","sources":["../../src/tools/amazon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,6EAA6E;AAC7E,+EAA+E;AAC/E,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAC3E,iFAAiF;AACjF,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,iCAAiC;AACjC,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,iBAAiB;AAEjB,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,MAAM,mBAAmB,GACvB,yEAAyE,CAAC;AAE5E,MAAM,YAAY,GAAG,CAAC;KACnB,MAAM,EAAE;KACR,KAAK,CAAC,eAAe,CAAC;KACtB,QAAQ,EAAE;KACV,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAEjC,MAAM,SAAS,GAAG,CAAC;KAChB,MAAM,EAAE;KACR,MAAM,CAAC,EAAE,CAAC;KACV,QAAQ,CAAC,0BAA0B,CAAC,CAAC;AAExC,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,SAA6B;IAClF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,EAAE,MAA+B,EAAE,EAAE;QACvE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAClD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,qGAAqG,EACrG;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;aAC9B,QAAQ,CAAC,gBAAgB,CAAC;QAC7B,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrC,QAAQ,CAAC,gBAAgB,CAAC;KAC9B,EACD,IAAI,CAAC,uBAAuB,CAAC,CAC9B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,sHAAsH,EACtH;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,YAAY;KACtB,EACD,IAAI,CAAC,wBAAwB,CAAC,CAC/B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,sGAAsG,EACtG;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,YAAY;KACtB,EACD,IAAI,CAAC,uBAAuB,CAAC,CAC9B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,sEAAsE,EACtE,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YAC7D,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1,55 +1,56 @@
1
1
  import { z } from "zod";
2
2
  import { handleApiError } from "../lib/tool-error.js";
3
+ import { trimResponse } from "../lib/trim-response.js";
3
4
  // Two-letter ISO only. The transport falls back to the US storefront for
4
5
  // anything that is not two letters, so 'usa' would silently buy a US result set
5
6
  // on a billed call — rejecting it here costs nothing.
6
7
  const countryField = z.string().regex(/^[A-Za-z]{2}$/, "country must be a two-letter ISO country code, e.g. us").optional()
7
- .describe("Storefront to answer from, as a two-letter ISO code ('us', 'gb', 'jp'). Decides price, currency, localised title and whether the app is sold there at all. Must be exactly two letters - 'usa' is rejected rather than silently answered from the US. Defaults to 'us'.");
8
+ .describe("Two-letter country code, e.g. 'us', 'gb'. Decides price/currency/availability.");
8
9
  export function registerAppStoreTools(server, getClient) {
9
- server.tool("search_app_store", `Search Apple's App Store (iPhone, iPad and Mac apps) and return up to 200 fully-shaped app rows as JSON - the same 43-field row get_app_store_app returns, so this doubles as a bulk metadata fetch. Apple matches an app name, a keyword OR a publisher name, so searching a developer returns their whole catalogue. SEARCH HAS NO PAGINATION: limit (1-200) is the only lever on result volume - raise limit, never ask for a page or an offset, which are silently ignored. country decides price, currency, localised title and whether the app is sold in that storefront at all. With entity=mac_software the iPad/Apple TV screenshots, advisories, features, supported devices and Game Center flag come back EMPTY rather than absent. Costs 1 credit.`, {
10
+ server.tool("search_app_store", `Search Apple App Store. NO PAGINATION: use limit (1-200) only. Searching a developer name returns their catalogue. 1 credit.`, {
10
11
  term: z.string().min(1).max(500)
11
- .describe("What to search for. Apple matches an app name, a keyword, or a PUBLISHER name - searching a developer returns their catalogue even when the words appear in none of their app titles."),
12
+ .describe("App name, keyword, or publisher name."),
12
13
  limit: z.number().int().min(1).max(200).optional()
13
- .describe("Apps to return, 1-200. THE ONLY lever on result volume - the search API has no pagination and there is no second page to ask for. Defaults to 25."),
14
+ .describe("Results 1-200. No pagination, this is the only volume control. Default 25."),
14
15
  country: countryField,
15
16
  entity: z.enum(["software", "ipad_software", "mac_software"]).optional()
16
- .describe("Which catalogue to search: 'software' (iPhone/iPad apps, the default), 'ipad_software', or 'mac_software'. Mac rows carry no iPad/Apple TV screenshots, advisories, features, supported devices or Game Center flag - those come back empty rather than absent."),
17
+ .describe("Catalogue: 'software' (default), 'ipad_software', 'mac_software'."),
17
18
  lang: z.string().regex(/^[A-Za-z]{2}_[A-Za-z]{2}$/, "lang must be a five-letter code, e.g. en_us or ja_jp").optional()
18
- .describe("Language of the returned listing text as a five-letter code ('en_us', 'ja_jp'). Independent of country: the storefront decides the price, this decides the words."),
19
+ .describe("Listing text language, e.g. 'en_us', 'ja_jp'."),
19
20
  }, async (params) => {
20
21
  try {
21
22
  const data = await getClient().post("/api/v1/appstore/search", params);
22
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
23
+ return trimResponse(data);
23
24
  }
24
25
  catch (err) {
25
26
  return handleApiError(err);
26
27
  }
27
28
  });
28
- server.tool("get_app_store_app", `Get one Apple App Store listing in full as JSON: title, description, developer and seller identity, price and currency, all-time and current-version ratings, version and release notes, genres, content rating and advisories, icons at three sizes, screenshots, download size, minimum OS, languages, supported devices, Game Center and VPP flags. Accepts BOTH a numeric App Store id (the digits after 'id' in an apps.apple.com URL) and a bundle id such as 'notion.id' - both resolve to the identical payload. A pasted apps.apple.com URL is rejected with a free 400, so pull the id out of it first. An id Apple cannot resolve is a BILLED 404. Mac apps carry no iPad/Apple TV screenshots, advisories, features, supported devices or Game Center flag - empty rather than absent. Costs 1 credit.`, {
29
+ server.tool("get_app_store_app", `Get full App Store listing. Accepts numeric id or bundle id (e.g. 'notion.id'). Full URL rejected (extract the id). Unknown id = billed 404. 1 credit.`, {
29
30
  app_id: z.string().min(1).max(255).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "app_id must be a numeric App Store id (e.g. 1232780281) or a bundle id (e.g. notion.id)")
30
- .describe("App Store id - the digits after 'id' in an apps.apple.com URL, e.g. '1232780281' - or the app's bundle id ('notion.id', 'com.burbn.instagram'). Both resolve to the identical payload. A full apps.apple.com URL is rejected."),
31
+ .describe("Numeric App Store id or bundle id, e.g. '1232780281' or 'notion.id'."),
31
32
  country: countryField,
32
33
  }, async (params) => {
33
34
  try {
34
35
  const data = await getClient().post("/api/v1/appstore/app", params);
35
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
36
+ return trimResponse(data);
36
37
  }
37
38
  catch (err) {
38
39
  return handleApiError(err);
39
40
  }
40
41
  });
41
- server.tool("get_app_store_reviews", `Get a page of Apple App Store customer reviews as JSON: star rating, title, full text, author, and the APP VERSION each review was written against. NUMERIC APP STORE IDS ONLY - unlike get_app_store_app, the reviews feed has no bundle-id form. HARD STOP AT PAGE 10 at 50 reviews per page: 500 reviews per storefront is Apple's anonymous ceiling, and the way to reach further is to ask a different country. Under sort=most_recent the review vote fields come back as ZEROES (those reviews are too new to have been voted on); most_helpful returns them densely populated. These reviews CANNOT 404 - an unknown id and a real app with zero reviews return the same empty feed, so an empty result does not mean the app is missing. Costs 1 credit per page.`, {
42
+ server.tool("get_app_store_reviews", `Get App Store reviews. NUMERIC ID ONLY (no bundle id). Max page 10, 50 reviews/page (500 max per storefront). Empty result doesn't mean app missing. 1 credit/page.`, {
42
43
  app_id: z.string().regex(/^\d+$/, "app_id must be a numeric App Store id")
43
- .describe("App Store id, e.g. '1232780281'. NUMERIC ONLY - unlike get_app_store_app, the reviews feed has no bundle-id form."),
44
+ .describe("Numeric App Store id only, e.g. '1232780281'."),
44
45
  country: countryField,
45
46
  page: z.number().int().min(1).max(10).optional()
46
- .describe("Reviews page, 1-10, 50 reviews each. Apple hard-stops at page 10, so 500 reviews per storefront is the ceiling - reach further by asking a different country. Defaults to 1."),
47
+ .describe("Page 1-10, 50 reviews each."),
47
48
  sort: z.enum(["most_recent", "most_helpful"]).optional()
48
- .describe("Review ordering. Also decides whether the vote fields mean anything: under 'most_recent' almost every review is too new to have been voted on and the vote fields return zeroes, while 'most_helpful' returns them densely populated. Defaults to 'most_recent'."),
49
+ .describe("Sort order. 'most_recent' vote fields are zeroes; 'most_helpful' has real votes."),
49
50
  }, async (params) => {
50
51
  try {
51
52
  const data = await getClient().post("/api/v1/appstore/reviews", params);
52
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
53
+ return trimResponse(data);
53
54
  }
54
55
  catch (err) {
55
56
  return handleApiError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"appstore.js","sourceRoot":"","sources":["../../src/tools/appstore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,yEAAyE;AACzE,gFAAgF;AAChF,sDAAsD;AACtD,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,wDAAwD,CAAC,CAAC,QAAQ,EAAE;KACxH,QAAQ,CAAC,yQAAyQ,CAAC,CAAC;AAEvR,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,SAA6B;IACpF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,kuBAAkuB,EACluB;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;aAC7B,QAAQ,CAAC,uLAAuL,CAAC;QACpM,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;aAC/C,QAAQ,CAAC,mJAAmJ,CAAC;QAChK,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrE,QAAQ,CAAC,iQAAiQ,CAAC;QAC9Q,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,2BAA2B,EAAE,sDAAsD,CAAC,CAAC,QAAQ,EAAE;aACnH,QAAQ,CAAC,mKAAmK,CAAC;KACjL,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,oxBAAoxB,EACpxB;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,8BAA8B,EAAE,yFAAyF,CAAC;aAChK,QAAQ,CAAC,+NAA+N,CAAC;QAC5O,OAAO,EAAE,YAAY;KACtB,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;YACpE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,4uBAA4uB,EAC5uB;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,uCAAuC,CAAC;aACvE,QAAQ,CAAC,mHAAmH,CAAC;QAChI,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;aAC7C,QAAQ,CAAC,8KAA8K,CAAC;QAC3L,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrD,QAAQ,CAAC,kQAAkQ,CAAC;KAChR,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC;YACxE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"appstore.js","sourceRoot":"","sources":["../../src/tools/appstore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,yEAAyE;AACzE,gFAAgF;AAChF,sDAAsD;AACtD,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,wDAAwD,CAAC,CAAC,QAAQ,EAAE;KACxH,QAAQ,CAAC,gFAAgF,CAAC,CAAC;AAE9F,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,SAA6B;IACpF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,8HAA8H,EAC9H;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;aAC7B,QAAQ,CAAC,uCAAuC,CAAC;QACpD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;aAC/C,QAAQ,CAAC,4EAA4E,CAAC;QACzF,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrE,QAAQ,CAAC,mEAAmE,CAAC;QAChF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,2BAA2B,EAAE,sDAAsD,CAAC,CAAC,QAAQ,EAAE;aACnH,QAAQ,CAAC,+CAA+C,CAAC;KAC7D,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAAC;YACvE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,wJAAwJ,EACxJ;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,8BAA8B,EAAE,yFAAyF,CAAC;aAChK,QAAQ,CAAC,sEAAsE,CAAC;QACnF,OAAO,EAAE,YAAY;KACtB,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;YACpE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,qKAAqK,EACrK;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,uCAAuC,CAAC;aACvE,QAAQ,CAAC,+CAA+C,CAAC;QAC5D,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;aAC7C,QAAQ,CAAC,6BAA6B,CAAC;QAC1C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrD,QAAQ,CAAC,kFAAkF,CAAC;KAChG,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC;YACxE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { handleApiError } from "../lib/tool-error.js";
3
+ import { trimResponse } from "../lib/trim-response.js";
3
4
  // No zod `.default()` anywhere in this file, on purpose. The MCP SDK applies a
4
5
  // zod default BEFORE the handler runs, so a defaulted field is posted on every
5
6
  // single call whether the model set it or not — which is exactly how the
@@ -11,102 +12,84 @@ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
11
12
  // occupancy block. Built per-tool so no two tool schemas share an instance.
12
13
  const stayFields = () => ({
13
14
  checkin: z.string().regex(ISO_DATE, "checkin must be YYYY-MM-DD").optional()
14
- .describe("Check-in date, YYYY-MM-DD. MUST be sent together with checkout — Booking ignores a lone checkin and prices a default range of its own instead."),
15
+ .describe("YYYY-MM-DD. MUST send with checkout."),
15
16
  checkout: z.string().regex(ISO_DATE, "checkout must be YYYY-MM-DD").optional()
16
- .describe("Check-out date, YYYY-MM-DD. Must be after checkin and sent together with it."),
17
+ .describe("YYYY-MM-DD. MUST send with checkin."),
17
18
  adults: z.number().int().min(1).optional()
18
- .describe("Adults in the party. Upstream default is 2."),
19
+ .describe("Default 2."),
19
20
  children_ages: z.array(z.number().int().min(0).max(17)).max(10).optional()
20
- .describe("One AGE (0-17) per child, not a count — Booking prices children by age. Max 10 entries."),
21
+ .describe("One AGE per child, not a count."),
21
22
  rooms: z.number().int().min(1).optional()
22
- .describe("Rooms required. Upstream default is 1."),
23
+ .describe("Default 1."),
23
24
  currency: z.string().regex(/^[A-Za-z]{3}$/, "currency must be a 3-letter ISO 4217 code").optional()
24
- .describe("ISO 4217 display currency, e.g. 'USD'. Defaults to USD; without it Booking prices off the proxy exit and two identical requests can disagree."),
25
+ .describe("e.g. 'USD'. Default USD."),
25
26
  });
26
27
  const hotelField = () => z.string().min(1).max(500)
27
- .describe("A booking.com property URL, or the bare page slug, e.g. 'citizenm-austin-downtown'. Query parameters on a pasted URL are discarded. Prefer the url a search_booking row returns — a bare slug with the wrong country_code is a real, BILLED 404.");
28
+ .describe("Booking.com URL or slug. Prefer URL from search_booking; wrong slug+country is a BILLED 404.");
28
29
  const countryCodeField = () => z.string().regex(/^[A-Za-z]{2}$/, "country_code must be a two-letter country code").optional()
29
- .describe("Two-letter country segment of the property path, e.g. 'us'. Only consulted for a bare slug (a full URL carries its own). Defaults to us — a wrong value is a billed 404.");
30
+ .describe("e.g. 'us'. Only for bare slugs. Wrong value is a billed 404.");
30
31
  export function registerBookingTools(server, getClient) {
31
- server.tool("search_booking", `Search Booking.com for hotels, apartments, hostels, resorts and other stays in a destination, as JSON. Each property carries its name and property url, the live price for the stay you asked for, review score and review count, star rating, location and distance, room type, and deal badges.
32
-
33
- Either destination or dest_id is required. A search with NEITHER returns Booking's homepage — it still costs a credit and returns nothing. dest_type is silently ignored by Booking without dest_id, so it is rejected here rather than billed.
34
-
35
- checkin and checkout must be sent TOGETHER. Send only one and Booking prices a default range of its own, returning real prices for dates you never asked for. currency defaults to USD; without it Booking prices off the proxy exit and two identical requests can disagree.
36
-
37
- Chain the url on a result row into get_booking_hotel or get_booking_reviews — that is always cheaper than guessing a slug plus country_code, which is a real, BILLED 404 when wrong.
38
-
39
- Paginate with page: 25 properties per page. min_review_score is an ENUM of '6', '7', '8' or '9' — Booking silently drops any other threshold. Costs 1 credit per page.`, {
32
+ server.tool("search_booking", `Search Booking.com stays. Requires destination or dest_id. checkin+checkout MUST be sent together. 25/page. 1 credit/page.`, {
40
33
  destination: z.string().min(1).max(200).optional()
41
- .describe("Free-text destination, e.g. 'Austin, Texas'. Required unless dest_id is given."),
34
+ .describe("e.g. 'Austin, Texas'. Required unless dest_id."),
42
35
  dest_id: z.string().regex(/^\d+$/, "dest_id must be numeric").optional()
43
- .describe("Booking's own NUMERIC destination id, e.g. '20126662'. Use it to pin an ambiguous name; a search response returns the dest_id it resolved. Required unless destination is given."),
36
+ .describe("Numeric destination id. Required unless destination."),
44
37
  dest_type: z.enum(["city", "region", "country", "district", "landmark", "airport", "hotel"]).optional()
45
- .describe("What dest_id refers to. Only meaningful WITH dest_id and rejected without it, because Booking ignores it on its own."),
38
+ .describe("Only with dest_id."),
46
39
  page: z.number().int().min(1).optional()
47
- .describe("Results page, 1-based. 25 properties per page, 1 credit per page."),
40
+ .describe("Page, 1-based."),
48
41
  sort_by: z.enum(["popularity", "price_low", "price_high", "stars_high", "stars_low", "stars_and_price", "distance", "review_score"]).optional()
49
- .describe("Result sort order. Upstream default is 'popularity'. 'distance' ranks from the searched destination centre, not from your location."),
42
+ .describe("Sort order."),
50
43
  min_price: z.number().min(0).optional()
51
- .describe("Minimum price PER NIGHT, in the currency field."),
44
+ .describe("Min price/night."),
52
45
  max_price: z.number().min(0).optional()
53
- .describe("Maximum price PER NIGHT, in the currency field. Must not be below min_price."),
46
+ .describe("Max price/night."),
54
47
  stars: z.array(z.number().int().min(1).max(5)).min(1).max(5).optional()
55
- .describe("Star ratings to keep, e.g. [4, 5]. Multiple values are OR'd."),
48
+ .describe("e.g. [4, 5]."),
56
49
  min_review_score: z.enum(["6", "7", "8", "9"]).optional()
57
- .describe("Minimum guest review score. ONLY '6', '7', '8' or '9' — Booking offers exactly these four floors and silently drops any other threshold."),
50
+ .describe("Min review score."),
58
51
  property_type: z.union([
59
52
  z.enum(["apartments", "hostels", "hotels", "motels", "resorts", "bed_and_breakfasts", "villas", "campgrounds", "vacation_homes", "lodges", "homestays"]),
60
53
  z.number().int().min(1),
61
54
  ]).optional()
62
- .describe("Property type name, or a raw numeric Booking accommodation-type id for a type this list does not name."),
55
+ .describe("Type name or numeric id."),
63
56
  free_cancellation: z.boolean().optional()
64
- .describe("Only properties offering free cancellation."),
57
+ .describe("Free cancellation only."),
65
58
  no_prepayment: z.boolean().optional()
66
- .describe("Only properties that take no prepayment."),
59
+ .describe("No prepayment only."),
67
60
  breakfast_included: z.boolean().optional()
68
- .describe("Only rates that include breakfast."),
61
+ .describe("Breakfast included only."),
69
62
  ...stayFields(),
70
63
  }, async (params) => {
71
64
  try {
72
65
  const data = await getClient().post("/api/v1/booking/search", params);
73
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
66
+ return trimResponse(data);
74
67
  }
75
68
  catch (err) {
76
69
  return handleApiError(err);
77
70
  }
78
71
  });
79
- server.tool("get_booking_hotel", `Get one Booking.com property in full as JSON: its rooms and rate plans priced for the stay you asked for, facilities, house rules, check-in and check-out windows, cancellation and prepayment policies, images, location, and review scores.
80
-
81
- It takes dates because Booking prices a STAY, not a property. Omit them and you do not get a property without prices — you get prices for a two-night range Booking chose. The response echoes whichever dates were used, so a price is never for an unknown stay. checkin and checkout must be sent together.
82
-
83
- Accepts a booking.com property URL or the bare page slug. country_code is only consulted for a bare slug and defaults to 'us'; a wrong country on a bare slug is a real, BILLED 404, so chaining the url a search_booking row returns is always cheaper.
84
-
85
- Single-property lookup with NO pagination. Costs 1 credit.`, {
72
+ server.tool("get_booking_hotel", `Full Booking.com property: rooms, rates, facilities, policies. Send checkin+checkout together. 1 credit.`, {
86
73
  hotel: hotelField(),
87
74
  country_code: countryCodeField(),
88
75
  ...stayFields(),
89
76
  }, async (params) => {
90
77
  try {
91
78
  const data = await getClient().post("/api/v1/booking/hotel", params);
92
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
79
+ return trimResponse(data);
93
80
  }
94
81
  catch (err) {
95
82
  return handleApiError(err);
96
83
  }
97
84
  });
98
- server.tool("get_booking_reviews", `Get Booking.com guest reviews for one property as JSON, with the score breakdown by category (staff, cleanliness, value, location, comfort, facilities) and Booking's own summary of what guests praised and complained about.
99
-
100
- There is NO PAGE PARAMETER — do not try to paginate this and do not invent one. One call returns one response: total_count is the property's WHOLE review history, count is how many reviews this response actually holds.
101
-
102
- Accepts a booking.com property URL or the bare page slug, exactly like get_booking_hotel; country_code is only consulted for a bare slug and a wrong one is a real, BILLED 404. The stay parameters are accepted because Booking serves the review page inside a priced stay context. Costs 1 credit.`, {
85
+ server.tool("get_booking_reviews", `Booking.com guest reviews with score breakdown. No pagination. 1 credit.`, {
103
86
  hotel: hotelField(),
104
87
  country_code: countryCodeField(),
105
88
  ...stayFields(),
106
89
  }, async (params) => {
107
90
  try {
108
91
  const data = await getClient().post("/api/v1/booking/reviews", params);
109
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
92
+ return trimResponse(data);
110
93
  }
111
94
  catch (err) {
112
95
  return handleApiError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"booking.js","sourceRoot":"","sources":["../../src/tools/booking.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"booking.js","sourceRoot":"","sources":["../../src/tools/booking.ts"],"names":[],"mappings":"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"}