@scavio/mcp-server 0.13.1 → 0.14.1
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.
- package/README.md +23 -10
- package/dist/auth/cimd.d.ts +2 -0
- package/dist/auth/cimd.js +20 -0
- package/dist/auth/cimd.js.map +1 -0
- package/dist/auth/provider.d.ts +12 -0
- package/dist/auth/provider.js +43 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/routes.d.ts +2 -0
- package/dist/auth/routes.js +214 -0
- package/dist/auth/routes.js.map +1 -0
- package/dist/auth/stores.d.ts +33 -0
- package/dist/auth/stores.js +69 -0
- package/dist/auth/stores.js.map +1 -0
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/client.js +4 -1
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/env.d.ts +3 -0
- package/dist/lib/env.js +4 -0
- package/dist/lib/env.js.map +1 -1
- package/dist/lib/errors.d.ts +5 -1
- package/dist/lib/errors.js +5 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/tool-error.js +10 -0
- package/dist/lib/tool-error.js.map +1 -1
- package/dist/lib/trim-response.d.ts +6 -0
- package/dist/lib/trim-response.js +26 -0
- package/dist/lib/trim-response.js.map +1 -0
- package/dist/tools/airbnb.js +30 -49
- package/dist/tools/airbnb.js.map +1 -1
- package/dist/tools/amazon.js +11 -18
- package/dist/tools/amazon.js.map +1 -1
- package/dist/tools/appstore.js +16 -15
- package/dist/tools/appstore.js.map +1 -1
- package/dist/tools/booking.js +28 -45
- package/dist/tools/booking.js.map +1 -1
- package/dist/tools/capterra.js +16 -15
- package/dist/tools/capterra.js.map +1 -1
- package/dist/tools/companieshouse.js +11 -10
- package/dist/tools/companieshouse.js.map +1 -1
- package/dist/tools/ebay.js +21 -30
- package/dist/tools/ebay.js.map +1 -1
- package/dist/tools/extract.js +6 -5
- package/dist/tools/extract.js.map +1 -1
- package/dist/tools/g2.js +23 -22
- package/dist/tools/g2.js.map +1 -1
- package/dist/tools/glassdoor.js +18 -17
- package/dist/tools/glassdoor.js.map +1 -1
- package/dist/tools/google.js +154 -153
- package/dist/tools/google.js.map +1 -1
- package/dist/tools/googleads.js +20 -19
- package/dist/tools/googleads.js.map +1 -1
- package/dist/tools/googleplay.js +14 -13
- package/dist/tools/googleplay.js.map +1 -1
- package/dist/tools/homedepot.js +15 -28
- package/dist/tools/homedepot.js.map +1 -1
- package/dist/tools/indeed.js +20 -39
- package/dist/tools/indeed.js.map +1 -1
- package/dist/tools/index.d.ts +5 -4
- package/dist/tools/index.js +5 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/instagram.js +59 -58
- package/dist/tools/instagram.js.map +1 -1
- package/dist/tools/kuaishou.js +47 -46
- package/dist/tools/kuaishou.js.map +1 -1
- package/dist/tools/linkedin.js +26 -25
- package/dist/tools/linkedin.js.map +1 -1
- package/dist/tools/metaads.js +21 -20
- package/dist/tools/metaads.js.map +1 -1
- package/dist/tools/reddit.js +48 -47
- package/dist/tools/reddit.js.map +1 -1
- package/dist/tools/redfin.js +29 -28
- package/dist/tools/redfin.js.map +1 -1
- package/dist/tools/sec.js +36 -35
- package/dist/tools/sec.js.map +1 -1
- package/dist/tools/target.js +19 -40
- package/dist/tools/target.js.map +1 -1
- package/dist/tools/threads.js +20 -19
- package/dist/tools/threads.js.map +1 -1
- package/dist/tools/tiktok-shop.js +38 -37
- package/dist/tools/tiktok-shop.js.map +1 -1
- package/dist/tools/tiktok.js +54 -53
- package/dist/tools/tiktok.js.map +1 -1
- package/dist/tools/tripadvisor.js +20 -43
- package/dist/tools/tripadvisor.js.map +1 -1
- package/dist/tools/usage.js +3 -2
- package/dist/tools/usage.js.map +1 -1
- package/dist/tools/walmart.js +35 -34
- package/dist/tools/walmart.js.map +1 -1
- package/dist/tools/x.js +44 -43
- package/dist/tools/x.js.map +1 -1
- package/dist/tools/yelp.js +21 -20
- package/dist/tools/yelp.js.map +1 -1
- package/dist/tools/youtube.js +77 -76
- package/dist/tools/youtube.js.map +1 -1
- package/dist/tools/zillow.js +38 -53
- package/dist/tools/zillow.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/errors.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export declare class ApiError extends Error {
|
|
2
2
|
readonly status: number;
|
|
3
3
|
readonly code?: string | undefined;
|
|
4
|
-
|
|
4
|
+
/** Where to top up, when the API sent one (402 responses do). */
|
|
5
|
+
readonly billingUrl?: string | undefined;
|
|
6
|
+
constructor(status: number, message: string, code?: string | undefined,
|
|
7
|
+
/** Where to top up, when the API sent one (402 responses do). */
|
|
8
|
+
billingUrl?: string | undefined);
|
|
5
9
|
}
|
package/dist/lib/errors.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export class ApiError extends Error {
|
|
2
2
|
status;
|
|
3
3
|
code;
|
|
4
|
-
|
|
4
|
+
billingUrl;
|
|
5
|
+
constructor(status, message, code,
|
|
6
|
+
/** Where to top up, when the API sent one (402 responses do). */
|
|
7
|
+
billingUrl) {
|
|
5
8
|
super(message);
|
|
6
9
|
this.status = status;
|
|
7
10
|
this.code = code;
|
|
11
|
+
this.billingUrl = billingUrl;
|
|
8
12
|
this.name = "ApiError";
|
|
9
13
|
}
|
|
10
14
|
}
|
package/dist/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;IAEA;IALlB,YACkB,MAAc,EAC9B,OAAe,EACC,IAAa;IAC7B,iEAAiE;IACjD,UAAmB;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QANC,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAS;QAEb,eAAU,GAAV,UAAU,CAAS;QAGnC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF"}
|
package/dist/lib/tool-error.js
CHANGED
|
@@ -19,6 +19,16 @@ export function handleApiError(err) {
|
|
|
19
19
|
if (err instanceof ApiError) {
|
|
20
20
|
if (err.status === 429)
|
|
21
21
|
return { isError: true, content: [{ type: "text", text: "Rate limited. Wait and retry." }] };
|
|
22
|
+
// The API's 402 body carries billing_url; the bare "Insufficient credits"
|
|
23
|
+
// string used to be all the model saw, so it could not tell the user where
|
|
24
|
+
// to go. Surface the link, and say that nothing needs reconfiguring.
|
|
25
|
+
if (err.status === 402) {
|
|
26
|
+
const url = err.billingUrl ?? "https://dashboard.scavio.dev/billing";
|
|
27
|
+
return {
|
|
28
|
+
isError: true,
|
|
29
|
+
content: [{ type: "text", text: `Out of Scavio credits (402). Top up or upgrade at ${url}; the same key keeps working afterwards, no config change needed.` }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
22
32
|
if (err.status === 401)
|
|
23
33
|
throw new McpError(ErrorCode.InternalError, "Invalid SCAVIO_API_KEY. Check your configuration.");
|
|
24
34
|
return { isError: true, content: [{ type: "text", text: `Scavio API error (${err.status}): ${err.message}` }] };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/lib/tool-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC,EAAE,CAAC;QACrH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,mDAAmD,CAAC,CAAC;QACzH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAClH,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
1
|
+
{"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/lib/tool-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC,EAAE,CAAC;QACrH,0EAA0E;QAC1E,2EAA2E;QAC3E,qEAAqE;QACrE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,IAAI,sCAAsC,CAAC;YACrE,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qDAAqD,GAAG,mEAAmE,EAAE,CAAC;aAC/J,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,mDAAmD,CAAC,CAAC;QACzH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAClH,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -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"}
|
package/dist/tools/airbnb.js
CHANGED
|
@@ -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("
|
|
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("
|
|
17
|
+
.describe("YYYY-MM-DD. Must pair with check_in."),
|
|
17
18
|
adults: z.number().int().min(1).optional()
|
|
18
|
-
.describe("Adults
|
|
19
|
+
.describe("Adults."),
|
|
19
20
|
children: z.number().int().min(0).optional()
|
|
20
|
-
.describe("Children
|
|
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
|
|
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("
|
|
28
|
+
.describe("Currency, e.g. 'USD'. Default USD.");
|
|
28
29
|
const listingIdField = () => z.string().min(1).max(500)
|
|
29
|
-
.describe("
|
|
30
|
+
.describe("Listing id or airbnb.com/rooms/ URL.");
|
|
30
31
|
export function registerAirbnbTools(server, getClient) {
|
|
31
|
-
server.tool("search_airbnb", `Search Airbnb stays
|
|
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("
|
|
34
|
+
.describe("City, region, ZIP, or airbnb.com/s/ URL."),
|
|
42
35
|
...stayFields(),
|
|
43
36
|
min_price: z.number().min(0).optional()
|
|
44
|
-
.describe("
|
|
37
|
+
.describe("Min whole-stay total (not per night)."),
|
|
45
38
|
max_price: z.number().min(0).optional()
|
|
46
|
-
.describe("
|
|
39
|
+
.describe("Max whole-stay total."),
|
|
47
40
|
room_type: z.enum(["entire_home", "private_room", "shared_room", "hotel_room"]).optional()
|
|
48
|
-
.describe("
|
|
41
|
+
.describe("Room type filter."),
|
|
49
42
|
min_bedrooms: z.number().int().min(0).optional()
|
|
50
|
-
.describe("
|
|
43
|
+
.describe("Min bedrooms."),
|
|
51
44
|
min_beds: z.number().int().min(0).optional()
|
|
52
|
-
.describe("
|
|
45
|
+
.describe("Min beds."),
|
|
53
46
|
min_bathrooms: z.number().int().min(0).optional()
|
|
54
|
-
.describe("
|
|
47
|
+
.describe("Min baths."),
|
|
55
48
|
superhost: z.boolean().optional()
|
|
56
|
-
.describe("
|
|
49
|
+
.describe("Superhost only."),
|
|
57
50
|
instant_book: z.boolean().optional()
|
|
58
|
-
.describe("
|
|
51
|
+
.describe("Instant Book only."),
|
|
59
52
|
guest_favorite: z.boolean().optional()
|
|
60
|
-
.describe("
|
|
53
|
+
.describe("Guest Favourite only."),
|
|
61
54
|
free_cancellation: z.boolean().optional()
|
|
62
|
-
.describe("
|
|
55
|
+
.describe("Free cancellation only."),
|
|
63
56
|
amenities: z.string().min(1).max(200).optional()
|
|
64
|
-
.describe("Comma-separated
|
|
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("
|
|
60
|
+
.describe("Page, 1-based. Cannot combine with cursor."),
|
|
68
61
|
cursor: z.string().min(1).max(500).optional()
|
|
69
|
-
.describe("
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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("
|
|
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("
|
|
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
|
|
95
|
+
return trimResponse(data);
|
|
115
96
|
}
|
|
116
97
|
catch (err) {
|
|
117
98
|
return handleApiError(err);
|
package/dist/tools/airbnb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"airbnb.js","sourceRoot":"","sources":["../../src/tools/airbnb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"airbnb.js","sourceRoot":"","sources":["../../src/tools/airbnb.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/amazon.js
CHANGED
|
@@ -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 = "
|
|
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("
|
|
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
|
|
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
|
|
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
|
|
49
|
+
.describe("Product query."),
|
|
53
50
|
country: countryField,
|
|
54
51
|
page: z.number().int().min(1).optional()
|
|
55
|
-
.describe("
|
|
52
|
+
.describe("Page, 1-based."),
|
|
56
53
|
}, call("/api/v1/amazon/search"));
|
|
57
|
-
server.tool("get_amazon_product", `
|
|
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", `
|
|
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
|
|
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
|
|
65
|
+
return trimResponse(data);
|
|
73
66
|
}
|
|
74
67
|
catch (err) {
|
|
75
68
|
return handleApiError(err);
|
package/dist/tools/amazon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
package/dist/tools/appstore.js
CHANGED
|
@@ -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("
|
|
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
|
|
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("
|
|
12
|
+
.describe("App name, keyword, or publisher name."),
|
|
12
13
|
limit: z.number().int().min(1).max(200).optional()
|
|
13
|
-
.describe("
|
|
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("
|
|
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("
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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'.
|
|
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("
|
|
47
|
+
.describe("Page 1-10, 50 reviews each."),
|
|
47
48
|
sort: z.enum(["most_recent", "most_helpful"]).optional()
|
|
48
|
-
.describe("
|
|
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
|
|
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;
|
|
1
|
+
{"version":3,"file":"appstore.js","sourceRoot":"","sources":["../../src/tools/appstore.ts"],"names":[],"mappings":"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"}
|