@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
@@ -1,99 +1,100 @@
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
  export function registerGoogleTools(server, getClient) {
4
- server.tool("search_google", `Search Google (v2) and return structured SERP results as JSON: organic results (title, URL, snippet), ads, and the AI Overview when Google shows one. Use when the user asks to search the web, find current information, or look something up online. Costs 1 credit.`, {
5
+ server.tool("search_google", `Search Google, return SERP results with organic, ads, AI Overview. 1 credit.`, {
5
6
  query: z.string().min(1).max(500)
6
- .describe("Search query (1-500 characters)."),
7
+ .describe("Search query."),
7
8
  device: z.enum(["desktop", "mobile"]).optional()
8
- .describe("Device to emulate."),
9
+ .describe("Device."),
9
10
  start: z.number().int().optional()
10
- .describe("Result offset: 0 = page 1, 10 = page 2, ... up to 990."),
11
+ .describe("Offset: 0=page 1, 10=page 2."),
11
12
  include_html: z.boolean().optional()
12
- .describe("Include the raw Google HTML in the response."),
13
+ .describe("Include raw HTML."),
13
14
  hl: z.string().optional()
14
- .describe("UI language (ISO 639-1, e.g. 'en')."),
15
+ .describe("Language, e.g. 'en'."),
15
16
  gl: z.string().optional()
16
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
17
+ .describe("Country, e.g. 'us'."),
17
18
  google_domain: z.string().optional()
18
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
19
+ .describe("e.g. 'google.co.uk'."),
19
20
  location: z.string().optional()
20
- .describe("Canonical location name; auto-encoded to a UULE string."),
21
+ .describe("Location name, auto-encoded to UULE."),
21
22
  uule: z.string().optional()
22
- .describe("Pre-encoded UULE location string (takes priority over location)."),
23
+ .describe("UULE string, overrides location."),
23
24
  lr: z.string().optional()
24
- .describe("Language restrict (e.g. 'lang_en')."),
25
+ .describe("e.g. 'lang_en'."),
25
26
  cr: z.string().optional()
26
- .describe("Country restrict (e.g. 'countryUS')."),
27
+ .describe("e.g. 'countryUS'."),
27
28
  safe: z.enum(["active"]).optional()
28
- .describe("SafeSearch filter."),
29
+ .describe("SafeSearch."),
29
30
  nfpr: z.boolean().optional()
30
- .describe("Disable spelling correction / auto-fixes when true."),
31
+ .describe("Disable spelling correction."),
31
32
  filter: z.enum(["0", "1"]).optional()
32
- .describe("'0' disables the omitted/similar-results filter."),
33
+ .describe("'0' disables omitted-results filter."),
33
34
  time_period: z.enum(["last_hour", "last_day", "last_week", "last_month", "last_year"]).optional()
34
- .describe("Restrict results to a recent time window."),
35
+ .describe("Time window."),
35
36
  resolve_ai_overview: z.boolean().optional()
36
- .describe("Resolve a deferred AI Overview (server default true)."),
37
+ .describe("Resolve deferred AI Overview (default true)."),
37
38
  }, async (params) => {
38
39
  try {
39
40
  const data = await getClient().post("/api/v2/google", 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("google_ai_mode", `Get a Google AI Mode conversational answer with references as JSON. Use when the user wants a synthesized, AI-generated answer to a question with cited sources rather than a plain list of links. Costs 1 credit.`, {
47
+ server.tool("google_ai_mode", `Google AI Mode: synthesized answer with cited sources. 1 credit.`, {
47
48
  query: z.string().min(1).max(500)
48
- .describe("Question or prompt (1-500 characters)."),
49
+ .describe("Question or prompt."),
49
50
  device: z.enum(["desktop", "mobile"]).optional()
50
- .describe("Device to emulate."),
51
+ .describe("Device."),
51
52
  include_html: z.boolean().optional()
52
- .describe("Include the raw Google HTML in the response."),
53
+ .describe("Include raw HTML."),
53
54
  hl: z.string().optional()
54
- .describe("UI language (ISO 639-1, e.g. 'en')."),
55
+ .describe("Language, e.g. 'en'."),
55
56
  gl: z.string().optional()
56
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
57
+ .describe("Country, e.g. 'us'."),
57
58
  google_domain: z.string().optional()
58
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
59
+ .describe("e.g. 'google.co.uk'."),
59
60
  location: z.string().optional()
60
- .describe("Canonical location name; auto-encoded to a UULE string."),
61
+ .describe("Location name, auto-encoded to UULE."),
61
62
  uule: z.string().optional()
62
- .describe("Pre-encoded UULE location string (takes priority over location)."),
63
+ .describe("UULE string, overrides location."),
63
64
  safe: z.enum(["active"]).optional()
64
- .describe("SafeSearch filter."),
65
+ .describe("SafeSearch."),
65
66
  }, async (params) => {
66
67
  try {
67
68
  const data = await getClient().post("/api/v2/google/ai-mode", params);
68
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
69
+ return trimResponse(data);
69
70
  }
70
71
  catch (err) {
71
72
  return handleApiError(err);
72
73
  }
73
74
  });
74
- server.tool("google_maps_search", `Search Google Maps for local businesses as JSON. Each result includes name, address, rating, review count, place_id, data_cid, and coordinates. Use when the user wants to find places, businesses, or points of interest. Costs 1 credit.`, {
75
+ server.tool("google_maps_search", `Search Google Maps for local businesses and places. 1 credit.`, {
75
76
  query: z.string().min(1).max(500)
76
- .describe("Search query (1-500 characters)."),
77
+ .describe("Search query."),
77
78
  start: z.number().int().optional()
78
- .describe("Result offset; must be a multiple of 20 (0, 20, 40, ...)."),
79
+ .describe("Offset, multiples of 20."),
79
80
  ll: z.string().optional()
80
- .describe("Map center as '@lat,lng,zoomz'; controls where results come from."),
81
+ .describe("Map center '@lat,lng,zoomz'."),
81
82
  hl: z.string().optional()
82
- .describe("UI language (ISO 639-1, e.g. 'en')."),
83
+ .describe("Language, e.g. 'en'."),
83
84
  gl: z.string().optional()
84
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
85
+ .describe("Country, e.g. 'us'."),
85
86
  google_domain: z.string().optional()
86
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
87
+ .describe("e.g. 'google.co.uk'."),
87
88
  }, async (params) => {
88
89
  try {
89
90
  const data = await getClient().post("/api/v2/google/maps/search", params);
90
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
91
+ return trimResponse(data);
91
92
  }
92
93
  catch (err) {
93
94
  return handleApiError(err);
94
95
  }
95
96
  });
96
- server.tool("google_maps_place", `Get Google Maps place details as JSON: name, address, phone, hours, rating, categories, and more. Provide one of place_id or data_cid. Costs 1 credit.`, {
97
+ server.tool("google_maps_place", `Google Maps place details. Provide place_id or data_cid. 1 credit.`, {
97
98
  place_id: z.string().optional()
98
99
  .describe("Place ID (ChIJ...)."),
99
100
  data_cid: z.string().optional()
@@ -101,315 +102,315 @@ export function registerGoogleTools(server, getClient) {
101
102
  }, async (params) => {
102
103
  try {
103
104
  const data = await getClient().post("/api/v2/google/maps/place", params);
104
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
105
+ return trimResponse(data);
105
106
  }
106
107
  catch (err) {
107
108
  return handleApiError(err);
108
109
  }
109
110
  });
110
- server.tool("google_maps_reviews", `Get Google Maps reviews for a place as JSON. Each review includes author, rating, text, and timestamp. Provide one of data_id or place_id. Use next_page_token for pagination. Costs 1 credit.`, {
111
+ server.tool("google_maps_reviews", `Google Maps reviews for a place. Provide data_id or place_id. Paginate with next_page_token. 1 credit.`, {
111
112
  data_id: z.string().optional()
112
113
  .describe("Data ID (0xHEX:0xHEX)."),
113
114
  place_id: z.string().optional()
114
115
  .describe("Place ID (ChIJ...)."),
115
116
  num: z.number().int().optional()
116
- .describe("Reviews per page (1-20)."),
117
+ .describe("Reviews per page, 1-20."),
117
118
  next_page_token: z.string().optional()
118
- .describe("Pagination cursor from a prior response."),
119
+ .describe("Pagination cursor."),
119
120
  sort_by: z.enum(["relevance", "newest", "highest_rating", "lowest_rating"]).optional()
120
121
  .describe("Sort order."),
121
122
  hl: z.string().optional()
122
- .describe("UI language (ISO 639-1, e.g. 'en')."),
123
+ .describe("Language, e.g. 'en'."),
123
124
  gl: z.string().optional()
124
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
125
+ .describe("Country, e.g. 'us'."),
125
126
  google_domain: z.string().optional()
126
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
127
+ .describe("e.g. 'google.co.uk'."),
127
128
  }, async (params) => {
128
129
  try {
129
130
  const data = await getClient().post("/api/v2/google/maps/reviews", params);
130
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
131
+ return trimResponse(data);
131
132
  }
132
133
  catch (err) {
133
134
  return handleApiError(err);
134
135
  }
135
136
  });
136
- server.tool("google_shopping", `Search Google Shopping product listings as JSON. Each result includes title, price, source/store, rating, and product identifiers. Use when the user wants to compare products and prices across retailers. Costs 1 credit.`, {
137
+ server.tool("google_shopping", `Search Google Shopping for products with prices across retailers. 1 credit.`, {
137
138
  query: z.string().min(1).max(500)
138
- .describe("Product search query (1-500 characters)."),
139
+ .describe("Product query."),
139
140
  device: z.enum(["desktop", "mobile"]).optional()
140
- .describe("Device to emulate."),
141
+ .describe("Device."),
141
142
  start: z.number().int().optional()
142
143
  .describe("Result offset."),
143
144
  min_price: z.number().int().optional()
144
- .describe("Minimum price filter."),
145
+ .describe("Min price."),
145
146
  max_price: z.number().int().optional()
146
- .describe("Maximum price filter."),
147
+ .describe("Max price."),
147
148
  sort_by: z.number().int().optional()
148
- .describe("0 = relevance, 1 = price ascending, 2 = price descending."),
149
+ .describe("0=relevance, 1=price asc, 2=price desc."),
149
150
  free_shipping: z.boolean().optional()
150
- .describe("Only items with free shipping."),
151
+ .describe("Free shipping only."),
151
152
  on_sale: z.boolean().optional()
152
- .describe("Only items on sale."),
153
+ .describe("On sale only."),
153
154
  shoprs: z.string().optional()
154
- .describe("Opaque Google Shopping filter token."),
155
+ .describe("Shopping filter token."),
155
156
  hl: z.string().optional()
156
- .describe("UI language (ISO 639-1, e.g. 'en')."),
157
+ .describe("Language, e.g. 'en'."),
157
158
  gl: z.string().optional()
158
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
159
+ .describe("Country, e.g. 'us'."),
159
160
  google_domain: z.string().optional()
160
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
161
+ .describe("e.g. 'google.co.uk'."),
161
162
  location: z.string().optional()
162
- .describe("Canonical location name; auto-encoded to a UULE string."),
163
+ .describe("Location name, auto-encoded to UULE."),
163
164
  uule: z.string().optional()
164
- .describe("Pre-encoded UULE location string (takes priority over location)."),
165
+ .describe("UULE string, overrides location."),
165
166
  }, async (params) => {
166
167
  try {
167
168
  const data = await getClient().post("/api/v2/google/shopping", params);
168
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
169
+ return trimResponse(data);
169
170
  }
170
171
  catch (err) {
171
172
  return handleApiError(err);
172
173
  }
173
174
  });
174
- server.tool("google_shopping_product", `Get Google Shopping product detail and sellers as JSON. Pass catalog_id together with query for full data. Costs 1 credit.`, {
175
+ server.tool("google_shopping_product", `Google Shopping product detail and sellers. Pass catalog_id+query for full data. 1 credit.`, {
175
176
  catalog_id: z.string().optional()
176
- .describe("Durable product catalog id."),
177
+ .describe("Product catalog id."),
177
178
  query: z.string().optional()
178
- .describe("Product query; required when catalog_id is set."),
179
+ .describe("Required with catalog_id."),
179
180
  immersive_product_page_token: z.string().optional()
180
- .describe("Immersive product page token."),
181
+ .describe("Product page token."),
181
182
  page_token: z.string().optional()
182
183
  .describe("Alias for immersive_product_page_token."),
183
184
  product_id: z.string().optional()
184
185
  .describe("Product id."),
185
186
  device: z.enum(["desktop", "mobile", "tablet"]).optional()
186
- .describe("Device to emulate."),
187
+ .describe("Device."),
187
188
  google_domain: z.string().optional()
188
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
189
+ .describe("e.g. 'google.co.uk'."),
189
190
  sort_by: z.enum(["base_price", "total_price", "promotion", "seller_rating"]).optional()
190
- .describe("Seller sort order."),
191
+ .describe("Seller sort."),
191
192
  load_all_stores: z.boolean().optional()
192
- .describe("Load all available stores."),
193
+ .describe("Load all stores."),
193
194
  more_stores: z.boolean().optional()
194
- .describe("Fetch additional stores."),
195
+ .describe("Fetch more stores."),
195
196
  hl: z.string().optional()
196
- .describe("UI language (ISO 639-1, e.g. 'en')."),
197
+ .describe("Language, e.g. 'en'."),
197
198
  gl: z.string().optional()
198
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
199
+ .describe("Country, e.g. 'us'."),
199
200
  location: z.string().optional()
200
- .describe("Canonical location name; auto-encoded to a UULE string."),
201
+ .describe("Location name, auto-encoded to UULE."),
201
202
  uule: z.string().optional()
202
- .describe("Pre-encoded UULE location string (takes priority over location)."),
203
+ .describe("UULE string, overrides location."),
203
204
  }, async (params) => {
204
205
  try {
205
206
  const data = await getClient().post("/api/v2/google/shopping/product", params);
206
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
207
+ return trimResponse(data);
207
208
  }
208
209
  catch (err) {
209
210
  return handleApiError(err);
210
211
  }
211
212
  });
212
- server.tool("google_shopping_stores", `Get more sellers for a Google Shopping product as JSON (paginate google_shopping_product). Requires catalog_id and next_page_token. Costs 1 credit.`, {
213
+ server.tool("google_shopping_stores", `Paginate google_shopping_product sellers. 1 credit.`, {
213
214
  catalog_id: z.string()
214
- .describe("Durable product catalog id."),
215
+ .describe("Product catalog id."),
215
216
  next_page_token: z.string()
216
- .describe("Pagination cursor from google_shopping_product."),
217
+ .describe("Cursor from google_shopping_product."),
217
218
  }, async (params) => {
218
219
  try {
219
220
  const data = await getClient().post("/api/v2/google/shopping/product/stores", params);
220
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
221
+ return trimResponse(data);
221
222
  }
222
223
  catch (err) {
223
224
  return handleApiError(err);
224
225
  }
225
226
  });
226
- server.tool("google_flights", `Search Google Flights as JSON: itineraries with prices, durations, airlines, and stops. Requires departure_id, arrival_id, and outbound_date; set return_date when type=1 (round trip). Costs 1 credit.`, {
227
+ server.tool("google_flights", `Search Google Flights. Requires departure_id, arrival_id, outbound_date; set return_date for round trip (type=1). 1 credit.`, {
227
228
  departure_id: z.string()
228
- .describe("Departure IATA code(s); comma-separated allowed."),
229
+ .describe("IATA code(s), comma-separated."),
229
230
  arrival_id: z.string()
230
- .describe("Arrival IATA code(s); comma-separated allowed."),
231
+ .describe("IATA code(s), comma-separated."),
231
232
  outbound_date: z.string()
232
- .describe("Outbound date (YYYY-MM-DD)."),
233
+ .describe("YYYY-MM-DD."),
233
234
  type: z.number().int().optional()
234
- .describe("1 = round trip, 2 = one way, 3 = multi-city."),
235
+ .describe("1=round trip, 2=one way, 3=multi-city."),
235
236
  return_date: z.string().optional()
236
- .describe("Return date (YYYY-MM-DD); required when type=1."),
237
+ .describe("YYYY-MM-DD; required when type=1."),
237
238
  adults: z.number().int().optional()
238
- .describe("Number of adults (1-9)."),
239
+ .describe("Adults, 1-9."),
239
240
  children: z.number().int().optional()
240
- .describe("Number of children (0-9)."),
241
+ .describe("Children, 0-9."),
241
242
  infants_in_seat: z.number().int().optional()
242
- .describe("Infants in seat (0-4)."),
243
+ .describe("0-4."),
243
244
  infants_on_lap: z.number().int().optional()
244
- .describe("Infants on lap (0-4)."),
245
+ .describe("0-4."),
245
246
  travel_class: z.number().int().optional()
246
- .describe("1 = economy, 2 = premium, 3 = business, 4 = first."),
247
+ .describe("1=economy, 2=premium, 3=business, 4=first."),
247
248
  stops: z.number().int().optional()
248
- .describe("0 = any, 1 = nonstop, 2 = <=1 stop, 3 = <=2 stops."),
249
+ .describe("0=any, 1=nonstop, 2=1 stop, 3=2 stops."),
249
250
  sort_by: z.number().int().optional()
250
- .describe("1 = top, 2 = price, 3 = departure, 4 = arrival, 5 = duration, 6 = emissions."),
251
+ .describe("1=top, 2=price, 3=departure, 4=arrival, 5=duration, 6=emissions."),
251
252
  include_airlines: z.string().optional()
252
- .describe("Comma-separated airline codes/alliances to include."),
253
+ .describe("Airline codes to include, comma-separated."),
253
254
  exclude_airlines: z.string().optional()
254
- .describe("Comma-separated airline codes/alliances to exclude."),
255
+ .describe("Airline codes to exclude, comma-separated."),
255
256
  hl: z.string().optional()
256
- .describe("UI language (ISO 639-1, e.g. 'en')."),
257
+ .describe("Language, e.g. 'en'."),
257
258
  gl: z.string().optional()
258
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
259
+ .describe("Country, e.g. 'us'."),
259
260
  currency: z.string().optional()
260
- .describe("Currency code (ISO 4217, e.g. 'USD')."),
261
+ .describe("e.g. 'USD'."),
261
262
  }, async (params) => {
262
263
  try {
263
264
  const data = await getClient().post("/api/v2/google/flights", params);
264
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
265
+ return trimResponse(data);
265
266
  }
266
267
  catch (err) {
267
268
  return handleApiError(err);
268
269
  }
269
270
  });
270
- server.tool("google_hotels", `Search Google Hotels as JSON: properties with prices, ratings, and detail_tokens. Requires query, check_in_date, and check_out_date. Pass a returned detail_token to google_hotels_detail. Costs 1 credit.`, {
271
+ server.tool("google_hotels", `Search Google Hotels. Pass detail_token from results to google_hotels_detail. 1 credit.`, {
271
272
  query: z.string()
272
- .describe("Search query; use a '<City> hotels' form."),
273
+ .describe("e.g. 'Austin hotels'."),
273
274
  check_in_date: z.string()
274
- .describe("Check-in date (YYYY-MM-DD)."),
275
+ .describe("YYYY-MM-DD."),
275
276
  check_out_date: z.string()
276
- .describe("Check-out date (YYYY-MM-DD)."),
277
+ .describe("YYYY-MM-DD."),
277
278
  hl: z.string().optional()
278
- .describe("UI language (ISO 639-1, e.g. 'en')."),
279
+ .describe("Language, e.g. 'en'."),
279
280
  gl: z.string().optional()
280
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
281
+ .describe("Country, e.g. 'us'."),
281
282
  currency: z.string().optional()
282
- .describe("Currency code (ISO 4217, e.g. 'USD')."),
283
+ .describe("e.g. 'USD'."),
283
284
  sort_by: z.number().int().optional()
284
- .describe("3 = lowest price, 8 = highest rating, 13 = most reviewed."),
285
+ .describe("3=lowest price, 8=highest rating, 13=most reviewed."),
285
286
  min_price: z.number().int().optional()
286
- .describe("Minimum nightly price."),
287
+ .describe("Min nightly price."),
287
288
  max_price: z.number().int().optional()
288
- .describe("Maximum nightly price."),
289
+ .describe("Max nightly price."),
289
290
  rating: z.number().int().optional()
290
- .describe("7 = 3.5+, 8 = 4.0+, 9 = 4.5+."),
291
+ .describe("7=3.5+, 8=4.0+, 9=4.5+."),
291
292
  hotel_class: z.string().optional()
292
- .describe("Comma-separated star ratings (2-5)."),
293
+ .describe("Star ratings, e.g. '4,5'."),
293
294
  amenities: z.string().optional()
294
- .describe("Comma-separated amenity ids."),
295
+ .describe("Amenity ids, comma-separated."),
295
296
  property_types: z.string().optional()
296
- .describe("Comma-separated property-type ids (e.g. '12' for vacation rentals)."),
297
+ .describe("Property-type ids, e.g. '12'."),
297
298
  free_cancellation: z.boolean().optional()
298
- .describe("Only properties with free cancellation."),
299
+ .describe("Free cancellation only."),
299
300
  eco_certified: z.boolean().optional()
300
- .describe("Only eco-certified properties."),
301
+ .describe("Eco-certified only."),
301
302
  special_offers: z.boolean().optional()
302
- .describe("Only properties with special offers."),
303
+ .describe("Special offers only."),
303
304
  next_page_token: z.string().optional()
304
- .describe("Pagination cursor from a prior response."),
305
+ .describe("Pagination cursor."),
305
306
  limit: z.number().int().optional()
306
- .describe("Number of properties to return (1-20)."),
307
+ .describe("Results to return, 1-20."),
307
308
  }, async (params) => {
308
309
  try {
309
310
  const data = await getClient().post("/api/v2/google/hotels", params);
310
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
311
+ return trimResponse(data);
311
312
  }
312
313
  catch (err) {
313
314
  return handleApiError(err);
314
315
  }
315
316
  });
316
- server.tool("google_hotels_detail", `Get Google Hotels property details as JSON from a hotels listing detail_token. Requires detail_token, check_in_date, and check_out_date. Costs 1 credit.`, {
317
+ server.tool("google_hotels_detail", `Google Hotels property detail from a detail_token. 1 credit.`, {
317
318
  detail_token: z.string()
318
- .describe("Property detail token from a hotels listing."),
319
+ .describe("From google_hotels results."),
319
320
  check_in_date: z.string()
320
- .describe("Check-in date (YYYY-MM-DD)."),
321
+ .describe("YYYY-MM-DD."),
321
322
  check_out_date: z.string()
322
- .describe("Check-out date (YYYY-MM-DD)."),
323
+ .describe("YYYY-MM-DD."),
323
324
  currency: z.string().optional()
324
- .describe("Currency code (ISO 4217, e.g. 'USD')."),
325
+ .describe("e.g. 'USD'."),
325
326
  gl: z.string().optional()
326
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
327
+ .describe("Country, e.g. 'us'."),
327
328
  hl: z.string().optional()
328
- .describe("UI language (ISO 639-1, e.g. 'en')."),
329
+ .describe("Language, e.g. 'en'."),
329
330
  }, async (params) => {
330
331
  try {
331
332
  const data = await getClient().post("/api/v2/google/hotels/detail", params);
332
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
333
+ return trimResponse(data);
333
334
  }
334
335
  catch (err) {
335
336
  return handleApiError(err);
336
337
  }
337
338
  });
338
- server.tool("google_news", `Get Google News results as JSON. Provide a query, or browse via a topic/story/publication token. Costs 1 credit.`, {
339
+ server.tool("google_news", `Google News results. Query or browse via topic/story/publication token. 1 credit.`, {
339
340
  query: z.string().optional()
340
341
  .describe("Keyword search."),
341
342
  topic_token: z.string().optional()
342
- .describe("Browse a news topic."),
343
+ .describe("Topic token."),
343
344
  section_token: z.string().optional()
344
- .describe("Browse a topic section."),
345
+ .describe("Section token."),
345
346
  story_token: z.string().optional()
346
- .describe("Fetch full coverage of a story."),
347
+ .describe("Story token."),
347
348
  publication_token: z.string().optional()
348
- .describe("Browse a publication."),
349
+ .describe("Publication token."),
349
350
  kgmid: z.string().optional()
350
351
  .describe("Knowledge Graph entity id."),
351
352
  hl: z.string().optional()
352
- .describe("UI language (ISO 639-1, e.g. 'en')."),
353
+ .describe("Language, e.g. 'en'."),
353
354
  gl: z.string().optional()
354
- .describe("Country of the search (ISO 3166-1 alpha-2, e.g. 'us')."),
355
+ .describe("Country, e.g. 'us'."),
355
356
  google_domain: z.string().optional()
356
- .describe("Regional Google domain (e.g. 'google.co.uk')."),
357
+ .describe("e.g. 'google.co.uk'."),
357
358
  so: z.number().int().optional()
358
- .describe("Sort order: 0 = relevance, 1 = date (only with query or kgmid)."),
359
+ .describe("0=relevance, 1=date."),
359
360
  }, async (params) => {
360
361
  try {
361
362
  const data = await getClient().post("/api/v2/google/news", params);
362
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
363
+ return trimResponse(data);
363
364
  }
364
365
  catch (err) {
365
366
  return handleApiError(err);
366
367
  }
367
368
  });
368
- server.tool("google_trends", `Get Google Trends interest data as JSON. Requires a query (comma-separate terms to compare). Choose the dataset via data_type (interest over time, geo map, related queries/topics). Costs 1 credit.`, {
369
+ server.tool("google_trends", `Google Trends interest data. Comma-separate query terms to compare. 1 credit.`, {
369
370
  query: z.string()
370
- .describe("Search term(s); comma-separated for comparisons."),
371
+ .describe("Term(s), comma-separated to compare."),
371
372
  geo: z.string().optional()
372
- .describe("Location code (e.g. 'US', 'GB', 'US-CA')."),
373
+ .describe("e.g. 'US', 'US-CA'."),
373
374
  hl: z.string().optional()
374
- .describe("UI language (ISO 639-1, e.g. 'en')."),
375
+ .describe("Language, e.g. 'en'."),
375
376
  date: z.string().optional()
376
- .describe("Time range (e.g. 'today 12-m', 'now 7-d')."),
377
+ .describe("e.g. 'today 12-m', 'now 7-d'."),
377
378
  tz: z.string().optional()
378
379
  .describe("Timezone offset in minutes."),
379
380
  data_type: z.enum(["TIMESERIES", "GEO_MAP", "GEO_MAP_0", "RELATED_QUERIES", "RELATED_TOPICS"]).optional()
380
- .describe("Which trends dataset to return."),
381
+ .describe("Dataset to return."),
381
382
  cat: z.string().optional()
382
383
  .describe("Category id."),
383
384
  gprop: z.enum(["images", "news", "youtube", "froogle"]).optional()
384
- .describe("Google property filter."),
385
+ .describe("Property filter."),
385
386
  region: z.enum(["COUNTRY", "REGION", "DMA", "CITY"]).optional()
386
- .describe("Resolution for GEO_MAP data."),
387
+ .describe("GEO_MAP resolution."),
387
388
  }, async (params) => {
388
389
  try {
389
390
  const data = await getClient().post("/api/v2/google/trends", params);
390
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
391
+ return trimResponse(data);
391
392
  }
392
393
  catch (err) {
393
394
  return handleApiError(err);
394
395
  }
395
396
  });
396
- server.tool("google_trending", `Get Google Trending Now searches for a country as JSON. Requires geo (country code). Costs 1 credit.`, {
397
+ server.tool("google_trending", `Google Trending Now searches for a country. 1 credit.`, {
397
398
  geo: z.string()
398
- .describe("Country code (e.g. 'US')."),
399
+ .describe("e.g. 'US'."),
399
400
  hl: z.string().optional()
400
- .describe("UI language (ISO 639-1, e.g. 'en')."),
401
+ .describe("Language, e.g. 'en'."),
401
402
  hours: z.number().int().optional()
402
- .describe("Trending window: 4, 24, 48, or 168."),
403
+ .describe("Window: 4, 24, 48, or 168."),
403
404
  cat: z.number().int().optional()
404
- .describe("Category id (0-20)."),
405
+ .describe("Category, 0-20."),
405
406
  sort: z.enum(["relevance", "search_volume", "recency", "title"]).optional()
406
- .describe("Sort order."),
407
+ .describe("Sort."),
407
408
  status: z.enum(["all", "active"]).optional()
408
- .describe("Filter by trend status."),
409
+ .describe("Trend status filter."),
409
410
  }, async (params) => {
410
411
  try {
411
412
  const data = await getClient().post("/api/v2/google/trending", params);
412
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
413
+ return trimResponse(data);
413
414
  }
414
415
  catch (err) {
415
416
  return handleApiError(err);