@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.
Files changed (98) hide show
  1. package/README.md +23 -10
  2. package/dist/auth/cimd.d.ts +2 -0
  3. package/dist/auth/cimd.js +20 -0
  4. package/dist/auth/cimd.js.map +1 -0
  5. package/dist/auth/provider.d.ts +12 -0
  6. package/dist/auth/provider.js +43 -0
  7. package/dist/auth/provider.js.map +1 -0
  8. package/dist/auth/routes.d.ts +2 -0
  9. package/dist/auth/routes.js +214 -0
  10. package/dist/auth/routes.js.map +1 -0
  11. package/dist/auth/stores.d.ts +33 -0
  12. package/dist/auth/stores.js +69 -0
  13. package/dist/auth/stores.js.map +1 -0
  14. package/dist/index.js +15 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/lib/client.js +4 -1
  17. package/dist/lib/client.js.map +1 -1
  18. package/dist/lib/env.d.ts +3 -0
  19. package/dist/lib/env.js +4 -0
  20. package/dist/lib/env.js.map +1 -1
  21. package/dist/lib/errors.d.ts +5 -1
  22. package/dist/lib/errors.js +5 -1
  23. package/dist/lib/errors.js.map +1 -1
  24. package/dist/lib/tool-error.js +10 -0
  25. package/dist/lib/tool-error.js.map +1 -1
  26. package/dist/lib/trim-response.d.ts +6 -0
  27. package/dist/lib/trim-response.js +26 -0
  28. package/dist/lib/trim-response.js.map +1 -0
  29. package/dist/tools/airbnb.js +30 -49
  30. package/dist/tools/airbnb.js.map +1 -1
  31. package/dist/tools/amazon.js +11 -18
  32. package/dist/tools/amazon.js.map +1 -1
  33. package/dist/tools/appstore.js +16 -15
  34. package/dist/tools/appstore.js.map +1 -1
  35. package/dist/tools/booking.js +28 -45
  36. package/dist/tools/booking.js.map +1 -1
  37. package/dist/tools/capterra.js +16 -15
  38. package/dist/tools/capterra.js.map +1 -1
  39. package/dist/tools/companieshouse.js +11 -10
  40. package/dist/tools/companieshouse.js.map +1 -1
  41. package/dist/tools/ebay.js +21 -30
  42. package/dist/tools/ebay.js.map +1 -1
  43. package/dist/tools/extract.js +6 -5
  44. package/dist/tools/extract.js.map +1 -1
  45. package/dist/tools/g2.js +23 -22
  46. package/dist/tools/g2.js.map +1 -1
  47. package/dist/tools/glassdoor.js +18 -17
  48. package/dist/tools/glassdoor.js.map +1 -1
  49. package/dist/tools/google.js +154 -153
  50. package/dist/tools/google.js.map +1 -1
  51. package/dist/tools/googleads.js +20 -19
  52. package/dist/tools/googleads.js.map +1 -1
  53. package/dist/tools/googleplay.js +14 -13
  54. package/dist/tools/googleplay.js.map +1 -1
  55. package/dist/tools/homedepot.js +15 -28
  56. package/dist/tools/homedepot.js.map +1 -1
  57. package/dist/tools/indeed.js +20 -39
  58. package/dist/tools/indeed.js.map +1 -1
  59. package/dist/tools/index.d.ts +5 -4
  60. package/dist/tools/index.js +5 -4
  61. package/dist/tools/index.js.map +1 -1
  62. package/dist/tools/instagram.js +59 -58
  63. package/dist/tools/instagram.js.map +1 -1
  64. package/dist/tools/kuaishou.js +47 -46
  65. package/dist/tools/kuaishou.js.map +1 -1
  66. package/dist/tools/linkedin.js +26 -25
  67. package/dist/tools/linkedin.js.map +1 -1
  68. package/dist/tools/metaads.js +21 -20
  69. package/dist/tools/metaads.js.map +1 -1
  70. package/dist/tools/reddit.js +48 -47
  71. package/dist/tools/reddit.js.map +1 -1
  72. package/dist/tools/redfin.js +29 -28
  73. package/dist/tools/redfin.js.map +1 -1
  74. package/dist/tools/sec.js +36 -35
  75. package/dist/tools/sec.js.map +1 -1
  76. package/dist/tools/target.js +19 -40
  77. package/dist/tools/target.js.map +1 -1
  78. package/dist/tools/threads.js +20 -19
  79. package/dist/tools/threads.js.map +1 -1
  80. package/dist/tools/tiktok-shop.js +38 -37
  81. package/dist/tools/tiktok-shop.js.map +1 -1
  82. package/dist/tools/tiktok.js +54 -53
  83. package/dist/tools/tiktok.js.map +1 -1
  84. package/dist/tools/tripadvisor.js +20 -43
  85. package/dist/tools/tripadvisor.js.map +1 -1
  86. package/dist/tools/usage.js +3 -2
  87. package/dist/tools/usage.js.map +1 -1
  88. package/dist/tools/walmart.js +35 -34
  89. package/dist/tools/walmart.js.map +1 -1
  90. package/dist/tools/x.js +44 -43
  91. package/dist/tools/x.js.map +1 -1
  92. package/dist/tools/yelp.js +21 -20
  93. package/dist/tools/yelp.js.map +1 -1
  94. package/dist/tools/youtube.js +77 -76
  95. package/dist/tools/youtube.js.map +1 -1
  96. package/dist/tools/zillow.js +38 -53
  97. package/dist/tools/zillow.js.map +1 -1
  98. package/package.json +1 -1
@@ -1,91 +1,92 @@
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 registerInstagramTools(server, getClient) {
4
- server.tool("get_instagram_profile", `Get an Instagram user's profile as JSON. Returns user ID, username, full name, biography, external URL, profile picture, verified/private flags, and follower/following/post counts. The profile fields are inlined at the root of data, there is no data.user wrapper. Provide either username or user_id. Costs 10 credits.`, {
5
+ server.tool("get_instagram_profile", `Get an Instagram user's profile. 10 credits.`, {
5
6
  username: z.string().optional()
6
- .describe("Instagram handle without the @ symbol, e.g. 'instagram'."),
7
+ .describe("Handle without @, e.g. 'instagram'."),
7
8
  user_id: z.string().optional()
8
- .describe("Numeric Instagram user ID. Use if you already have it from a previous request."),
9
+ .describe("Numeric user ID from a previous request."),
9
10
  }, async (params) => {
10
11
  try {
11
12
  const data = await getClient().post("/api/v1/instagram/profile", params);
12
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
13
+ return trimResponse(data);
13
14
  }
14
15
  catch (err) {
15
16
  return handleApiError(err);
16
17
  }
17
18
  });
18
- server.tool("get_instagram_user_posts", `List an Instagram user's posts (timeline media) with pagination as JSON. Provide username or user_id. The response shape depends on which upstream leg served it: posts arrive either in data.data with data.pagination_token, or in data.items with data.next_max_id. Pass whichever token is present back as the cursor; stop when neither is present. Costs 2 credits, the cheapest Instagram endpoint - prefer it over get_instagram_user_reels when plain posts will do.`, {
19
+ server.tool("get_instagram_user_posts", `List an Instagram user's posts. Pass pagination_token or next_max_id as cursor. 2 credits.`, {
19
20
  username: z.string().optional()
20
- .describe("Instagram handle without the @ symbol."),
21
+ .describe("Handle without @."),
21
22
  user_id: z.string().optional()
22
- .describe("Numeric Instagram user ID."),
23
+ .describe("Numeric user ID."),
23
24
  count: z.number().int().min(1).max(50).default(12)
24
- .describe("Number of posts per page (1-50). Best effort: the primary upstream leg for this endpoint ignores it and returns its own page size."),
25
+ .describe("Posts per page (best effort)."),
25
26
  cursor: z.string().optional()
26
- .describe("Pagination cursor (pagination_token or next_max_id) from a previous response."),
27
+ .describe("pagination_token or next_max_id from previous response."),
27
28
  }, async (params) => {
28
29
  try {
29
30
  const data = await getClient().post("/api/v1/instagram/user/posts", params);
30
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
31
+ return trimResponse(data);
31
32
  }
32
33
  catch (err) {
33
34
  return handleApiError(err);
34
35
  }
35
36
  });
36
- server.tool("get_instagram_user_reels", `List an Instagram user's Reels with pagination as JSON. Provide username or user_id. Reels arrive in data.items; use data.next_max_id as the next cursor and stop when it is absent. Costs 10 credits.`, {
37
+ server.tool("get_instagram_user_reels", `List an Instagram user's Reels. Paginate with next_max_id. 10 credits.`, {
37
38
  username: z.string().optional()
38
- .describe("Instagram handle without the @ symbol."),
39
+ .describe("Handle without @."),
39
40
  user_id: z.string().optional()
40
- .describe("Numeric Instagram user ID."),
41
+ .describe("Numeric user ID."),
41
42
  count: z.number().int().min(1).max(50).default(12)
42
- .describe("Number of reels per page (1-50)."),
43
+ .describe("Reels per page."),
43
44
  cursor: z.string().optional()
44
- .describe("Pagination cursor (next_max_id) from a previous response."),
45
+ .describe("next_max_id from previous response."),
45
46
  }, async (params) => {
46
47
  try {
47
48
  const data = await getClient().post("/api/v1/instagram/user/reels", params);
48
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
49
+ return trimResponse(data);
49
50
  }
50
51
  catch (err) {
51
52
  return handleApiError(err);
52
53
  }
53
54
  });
54
- server.tool("get_instagram_user_tagged", `List posts an Instagram user is tagged in, with pagination as JSON. Provide username or user_id. Posts arrive in data.items; use data.next_max_id as the next cursor and stop when data.more_available is false. Costs 10 credits.`, {
55
+ server.tool("get_instagram_user_tagged", `List posts an Instagram user is tagged in. Paginate with next_max_id/more_available. 10 credits.`, {
55
56
  username: z.string().optional()
56
- .describe("Instagram handle without the @ symbol."),
57
+ .describe("Handle without @."),
57
58
  user_id: z.string().optional()
58
- .describe("Numeric Instagram user ID."),
59
+ .describe("Numeric user ID."),
59
60
  count: z.number().int().min(1).max(50).default(12)
60
- .describe("Number of posts per page (1-50)."),
61
+ .describe("Posts per page."),
61
62
  cursor: z.string().optional()
62
- .describe("Pagination cursor (next_max_id) from a previous response."),
63
+ .describe("next_max_id from previous response."),
63
64
  }, async (params) => {
64
65
  try {
65
66
  const data = await getClient().post("/api/v1/instagram/user/tagged", params);
66
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
67
+ return trimResponse(data);
67
68
  }
68
69
  catch (err) {
69
70
  return handleApiError(err);
70
71
  }
71
72
  });
72
- server.tool("get_instagram_user_stories", `Get an Instagram user's currently active stories as JSON. Returns each story's media URLs, type, and timestamp in data.items. Stories are not paginated. Provide either username or user_id. Costs 10 credits.`, {
73
+ server.tool("get_instagram_user_stories", `Get an Instagram user's active stories. Not paginated. 10 credits.`, {
73
74
  username: z.string().optional()
74
- .describe("Instagram handle without the @ symbol."),
75
+ .describe("Handle without @."),
75
76
  user_id: z.string().optional()
76
- .describe("Numeric Instagram user ID."),
77
+ .describe("Numeric user ID."),
77
78
  }, async (params) => {
78
79
  try {
79
80
  const data = await getClient().post("/api/v1/instagram/user/stories", params);
80
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
81
+ return trimResponse(data);
81
82
  }
82
83
  catch (err) {
83
84
  return handleApiError(err);
84
85
  }
85
86
  });
86
- server.tool("get_instagram_post", `Get a single Instagram post (or reel) as JSON, including caption, author, media URLs, and engagement stats. The post is data.items[0]: video URLs are in video_versions[].url (there is no video_url), the cover is in image_versions2.candidates (there is no thumbnail_url), and media_type is 1 for an image, 2 for a video, 8 for a carousel. Provide one of: url (a /p/, /reel/, /reels/, or /tv/ link), media_id, or shortcode. Costs 8 credits.`, {
87
+ server.tool("get_instagram_post", `Get a single Instagram post/reel. Provide url, media_id, or shortcode. media_type: 1=image, 2=video, 8=carousel. 8 credits.`, {
87
88
  url: z.string().optional()
88
- .describe("Instagram post URL, e.g. 'https://www.instagram.com/p/DUajw4YkorV/'."),
89
+ .describe("Post URL."),
89
90
  media_id: z.string().optional()
90
91
  .describe("Numeric media ID."),
91
92
  shortcode: z.string().optional()
@@ -93,105 +94,105 @@ export function registerInstagramTools(server, getClient) {
93
94
  }, async (params) => {
94
95
  try {
95
96
  const data = await getClient().post("/api/v1/instagram/post", params);
96
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
97
+ return trimResponse(data);
97
98
  }
98
99
  catch (err) {
99
100
  return handleApiError(err);
100
101
  }
101
102
  });
102
- server.tool("get_instagram_post_comments", `Get comments on an Instagram post as JSON. Each comment includes ID (pk), text, author, timestamp, and like count, under data.comments. Provide shortcode or url; media_id is not accepted here. Use data.next_min_id as the next cursor; stop when it is absent or data.has_more_comments is false. Costs 10 credits.`, {
103
+ server.tool("get_instagram_post_comments", `Get comments on an Instagram post. Provide shortcode or url (not media_id). Paginate with next_min_id. 10 credits.`, {
103
104
  shortcode: z.string().optional()
104
- .describe("Post shortcode, e.g. 'DUajw4YkorV'."),
105
+ .describe("Post shortcode."),
105
106
  url: z.string().optional()
106
- .describe("Instagram post URL; the shortcode is extracted from it."),
107
+ .describe("Post URL."),
107
108
  cursor: z.string().optional()
108
- .describe("Pagination cursor (next_min_id) from a previous response."),
109
+ .describe("next_min_id from previous response."),
109
110
  sort_order: z.enum(["popular", "newest"]).default("popular")
110
- .describe("Comment sort order. Best effort: the fallback upstream leg drops it and returns popular order."),
111
+ .describe("Sort order (best effort)."),
111
112
  }, async (params) => {
112
113
  try {
113
114
  const data = await getClient().post("/api/v1/instagram/post/comments", params);
114
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
115
+ return trimResponse(data);
115
116
  }
116
117
  catch (err) {
117
118
  return handleApiError(err);
118
119
  }
119
120
  });
120
- server.tool("get_instagram_comment_replies", `Get replies to a specific Instagram comment as JSON, under data.child_comments. Requires the post's media_id and the parent comment_id (the pk from get_instagram_post_comments). get_instagram_post_comments does not return a media_id, so resolve it with get_instagram_post first. Use data.next_min_child_id as the next cursor. Costs 8 credits.`, {
121
+ server.tool("get_instagram_comment_replies", `Get replies to an Instagram comment. Requires media_id (from get_instagram_post) and comment_id (pk from get_instagram_post_comments). 8 credits.`, {
121
122
  media_id: z.string()
122
- .describe("Numeric media ID of the post."),
123
+ .describe("Post media ID (from get_instagram_post)."),
123
124
  comment_id: z.string()
124
- .describe("Parent comment ID (pk) from the get_instagram_post_comments response."),
125
+ .describe("Comment pk from get_instagram_post_comments."),
125
126
  cursor: z.string().optional()
126
- .describe("Pagination cursor (next_min_child_id) from a previous response."),
127
+ .describe("next_min_child_id from previous response."),
127
128
  }, async (params) => {
128
129
  try {
129
130
  const data = await getClient().post("/api/v1/instagram/post/comments/replies", 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("search_instagram_users", `Search Instagram users by keyword as JSON. Each result includes user ID, username, full name, verified flag, and follower count, under data.users. Pass data.rank_token from a previous response as the cursor for the next page; pagination is best effort because the fallback upstream leg drops the cursor and re-serves page 1. Page size is not controllable. Costs 10 credits.`, {
137
+ server.tool("search_instagram_users", `Search Instagram users by keyword. Pagination via rank_token is best effort. 10 credits.`, {
137
138
  keyword: z.string().min(1).max(500)
138
139
  .describe("Search query."),
139
140
  cursor: z.string().optional()
140
- .describe("Rank token from a previous response for pagination."),
141
+ .describe("rank_token from previous response."),
141
142
  }, async (params) => {
142
143
  try {
143
144
  const data = await getClient().post("/api/v1/instagram/search/users", params);
144
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
145
+ return trimResponse(data);
145
146
  }
146
147
  catch (err) {
147
148
  return handleApiError(err);
148
149
  }
149
150
  });
150
- server.tool("search_instagram_hashtags", `Search Instagram hashtags by keyword as JSON. Each result includes the hashtag name, ID, and media count, under data.hashtags. Pass data.rank_token from a previous response as the cursor for the next page; pagination is best effort because the fallback upstream leg drops the cursor and re-serves page 1. Page size is not controllable. Costs 10 credits.`, {
151
+ server.tool("search_instagram_hashtags", `Search Instagram hashtags by keyword. Pagination via rank_token is best effort. 10 credits.`, {
151
152
  keyword: z.string().min(1).max(500)
152
153
  .describe("Search query."),
153
154
  cursor: z.string().optional()
154
- .describe("Rank token from a previous response for pagination."),
155
+ .describe("rank_token from previous response."),
155
156
  }, async (params) => {
156
157
  try {
157
158
  const data = await getClient().post("/api/v1/instagram/search/hashtags", params);
158
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
159
+ return trimResponse(data);
159
160
  }
160
161
  catch (err) {
161
162
  return handleApiError(err);
162
163
  }
163
164
  });
164
- server.tool("get_instagram_user_followers", `Get an Instagram user's follower list as JSON, under data.users. Each follower includes user ID, username, full name, and verified flag. Provide username or user_id. Use data.next_max_id as the next cursor when present; stop when it is absent or data.has_more is false. Costs 10 credits.`, {
165
+ server.tool("get_instagram_user_followers", `Get an Instagram user's followers. Paginate with next_max_id/has_more. 10 credits.`, {
165
166
  username: z.string().optional()
166
- .describe("Instagram handle without the @ symbol."),
167
+ .describe("Handle without @."),
167
168
  user_id: z.string().optional()
168
- .describe("Numeric Instagram user ID."),
169
+ .describe("Numeric user ID."),
169
170
  count: z.number().int().min(1).max(100).default(12)
170
- .describe("Number of users per page (1-100)."),
171
+ .describe("Users per page."),
171
172
  cursor: z.string().optional()
172
- .describe("Pagination cursor (next_max_id) from a previous response."),
173
+ .describe("next_max_id from previous response."),
173
174
  }, async (params) => {
174
175
  try {
175
176
  const data = await getClient().post("/api/v1/instagram/user/followers", params);
176
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
177
+ return trimResponse(data);
177
178
  }
178
179
  catch (err) {
179
180
  return handleApiError(err);
180
181
  }
181
182
  });
182
- server.tool("get_instagram_user_followings", `Get the list of accounts an Instagram user follows as JSON, under data.users. Each entry includes user ID, username, full name, and verified flag. Provide username or user_id. Use data.next_max_id as the next cursor; stop when it is absent or data.has_more is false. Costs 10 credits.`, {
183
+ server.tool("get_instagram_user_followings", `Get accounts an Instagram user follows. Paginate with next_max_id/has_more. 10 credits.`, {
183
184
  username: z.string().optional()
184
- .describe("Instagram handle without the @ symbol."),
185
+ .describe("Handle without @."),
185
186
  user_id: z.string().optional()
186
- .describe("Numeric Instagram user ID."),
187
+ .describe("Numeric user ID."),
187
188
  count: z.number().int().min(1).max(100).default(12)
188
- .describe("Number of users per page (1-100)."),
189
+ .describe("Users per page."),
189
190
  cursor: z.string().optional()
190
- .describe("Pagination cursor (next_max_id) from a previous response."),
191
+ .describe("next_max_id from previous response."),
191
192
  }, async (params) => {
192
193
  try {
193
194
  const data = await getClient().post("/api/v1/instagram/user/followings", params);
194
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
195
+ return trimResponse(data);
195
196
  }
196
197
  catch (err) {
197
198
  return handleApiError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"instagram.js","sourceRoot":"","sources":["../../src/tools/instagram.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instagram.js","sourceRoot":"","sources":["../../src/tools/instagram.ts"],"names":[],"mappings":"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"}
@@ -1,127 +1,128 @@
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
  // Kuaishou is priced PER ENDPOINT off four upstream price points, so every tool
4
5
  // carries its own cost line. A single platform-wide "costs N credits" claim is
5
6
  // wrong by up to 40x, which is why this sentence is appended everywhere rather
6
7
  // than stated once.
7
- const PRICING = "Kuaishou is priced PER ENDPOINT (1, 2, 10 or 40 credits), never per platform.";
8
+ const PRICING = "";
8
9
  // This is CHINA Kuaishou (kuaishou.com). Our upstream source does not serve
9
10
  // Kwai international (kwai.com): a real kwai.com id returns an empty envelope and
10
11
  // still bills, so the distinction goes in the description, not a comment.
11
- const NOT_KWAI = "Kuaishou China (kuaishou.com) only; Kwai international (kwai.com) is not served upstream.";
12
- const CURSOR_DESC = "Opaque pagination cursor: the data.pcursor value from the previous response (the field is pcursor, NOT next_cursor). Omit for the first page; a pcursor of 'no_more' means there are no further pages.";
13
- const SEARCH_CURSOR_DESC = "Opaque pagination cursor: the data.pcursor value from the previous response (the field is pcursor, NOT next_cursor, and NOT data.recoPcursor, which belongs to a separate recommendation stream and will not page the search). Omit for the first page; 'no_more' means the end.";
14
- const KEYWORD_DESC = "Search keyword, 1-200 characters. Kuaishou is a Chinese-language platform: Chinese keywords reach the real catalogue, Latin-script queries return whatever Kuaishou matches, which is usually much thinner.";
12
+ const NOT_KWAI = "China kuaishou.com only, not Kwai international.";
13
+ const CURSOR_DESC = "data.pcursor from previous response (NOT next_cursor). 'no_more' = last page.";
14
+ const SEARCH_CURSOR_DESC = "data.pcursor (NOT next_cursor or recoPcursor). 'no_more' = last page.";
15
+ const KEYWORD_DESC = "Search keyword. Chinese keywords recommended (Chinese-language platform).";
15
16
  export function registerKuaishouTools(server, getClient) {
16
- server.tool("get_kuaishou_profile", `Get the full profile of a Kuaishou (China) creator by user id as JSON: display name, bio, avatar, gender, IP-location province, and the follower / public-video / lifetime-like counters. Read followers from data.userProfile.ownerCount.fan, public videos from ownerCount.photo, and lifetime likes from ownerCount.total_photo_like - ownerCount.like is a DIFFERENT counter and is not the video like total. Both id forms work here, numeric and alphanumeric. Not paginated. Costs 10 credits, the dearest single-object call on the platform: resolve_kuaishou_user turns a share link into an id for 1 credit, and get_kuaishou_video already carries the author's follower count for 2, so only pay this when you need the profile itself. ${PRICING} ${NOT_KWAI}`, {
17
+ server.tool("get_kuaishou_profile", `Get a Kuaishou creator profile. 10 credits (resolve_kuaishou_user is 1cr, get_kuaishou_video is 2cr and includes follower count). ${NOT_KWAI}`, {
17
18
  user_id: z.string().min(1)
18
- .describe("Kuaishou user id, non-empty. Both forms work on this endpoint: numeric ('5518803932') and alphanumeric ('3xcuu5habgc8z29'). Get one from resolve_kuaishou_user or search_kuaishou_users and pass it through unchanged."),
19
+ .describe("User id (numeric or alphanumeric form both work)."),
19
20
  }, async (params) => {
20
21
  try {
21
22
  const data = await getClient().post("/api/v1/kuaishou/profile", 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_kuaishou_user_posts", `List a Kuaishou (China) creator's top posts as JSON, under data.feeds[]: caption, play / like / comment / share / collect counts, duration in milliseconds, publish timestamp in epoch milliseconds, cover image and video URL. Cursor-paginated. feeds[].photo_id is a JSON number above 2^53 and therefore arrives ROUNDED - take the exact ids from feeds[].share_info ("userId=<eid>&photoId=<eid>"), which is the form get_kuaishou_video accepts. Costs 1 credit per page, so this is the cheap way to pull a creator's catalogue when get_kuaishou_profile costs 10. ${PRICING} ${NOT_KWAI}`, {
29
+ server.tool("get_kuaishou_user_posts", `List a Kuaishou creator's posts. photo_id in feeds is rounded (>2^53); use share_info for exact ids. 1 credit/page. ${NOT_KWAI}`, {
29
30
  user_id: z.string().min(1)
30
- .describe("Kuaishou user id, non-empty. Get one from resolve_kuaishou_user (alphanumeric form) or search_kuaishou_users (numeric form) and pass it through unchanged rather than converting between forms."),
31
+ .describe("User id."),
31
32
  cursor: z.string().optional()
32
33
  .describe(CURSOR_DESC),
33
34
  }, async (params) => {
34
35
  try {
35
36
  const data = await getClient().post("/api/v1/kuaishou/user/posts", params);
36
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
37
+ return trimResponse(data);
37
38
  }
38
39
  catch (err) {
39
40
  return handleApiError(err);
40
41
  }
41
42
  });
42
- server.tool("get_kuaishou_user_live", `Check whether one Kuaishou (China) creator is broadcasting right now, as JSON. Read data.dynamicIconV2.isLive for the on-air flag and data.dynamicIconV2.liveStream for the stream itself (stream id, title, cover, and the broadcaster's verified details). When the creator is offline the liveStream is simply absent: that is a normal "not broadcasting" answer, not an error, and it still bills. Not paginated - one account, one answer. Costs 1 credit, cheap enough to poll a watchlist; finding live streams by keyword instead is search_kuaishou_live at 10 credits. ${PRICING} ${NOT_KWAI}`, {
43
+ server.tool("get_kuaishou_user_live", `Check if a Kuaishou creator is live. Offline = absent liveStream (still billed). 1 credit. ${NOT_KWAI}`, {
43
44
  user_id: z.string().min(1)
44
- .describe("Kuaishou user id, non-empty. Get one from resolve_kuaishou_user or search_kuaishou_users and pass it through unchanged."),
45
+ .describe("User id."),
45
46
  }, async (params) => {
46
47
  try {
47
48
  const data = await getClient().post("/api/v1/kuaishou/user/live", params);
48
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
49
+ return trimResponse(data);
49
50
  }
50
51
  catch (err) {
51
52
  return handleApiError(err);
52
53
  }
53
54
  });
54
- server.tool("resolve_kuaishou_user", `Turn a Kuaishou (China) share link into the user id that get_kuaishou_profile, get_kuaishou_user_posts and get_kuaishou_user_live all require. Returns a bare { user_id } in the ALPHANUMERIC form (e.g. "3xcuu5habgc8z29"); this is the only Kuaishou endpoint whose response carries no data.result field. Accepts kuaishou.com and v.kuaishou.com links only - Kwai international (kwai.com) is not served upstream, so kwai.com links do not resolve. Not paginated. Costs 1 credit, a tenth of get_kuaishou_profile, so resolve first rather than guessing ids. ${PRICING}`, {
55
+ server.tool("resolve_kuaishou_user", `Resolve a Kuaishou share link to a user_id. kuaishou.com/v.kuaishou.com only (not kwai.com). 1 credit.`, {
55
56
  share_link: z.string().url()
56
- .describe("A kuaishou.com or v.kuaishou.com link, e.g. 'https://v.kuaishou.com/KcdKDwFp'. Must be a syntactically valid URL. kwai.com links are not supported."),
57
+ .describe("kuaishou.com or v.kuaishou.com URL. kwai.com not supported."),
57
58
  }, async (params) => {
58
59
  try {
59
60
  const data = await getClient().post("/api/v1/kuaishou/user/resolve", params);
60
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
61
+ return trimResponse(data);
61
62
  }
62
63
  catch (err) {
63
64
  return handleApiError(err);
64
65
  }
65
66
  });
66
- server.tool("get_kuaishou_video", `Get one Kuaishou (China) video by photo id or URL as JSON: caption, play / like / comment / share counts, duration in milliseconds, source resolution, publish timestamp, cover image and the MP4 URL. data.counts carries the AUTHOR's account totals (fanCount, followCount, collectionCount, photoCount), which saves a 10-credit get_kuaishou_profile call when the follower number is all you need. FIELD NAMES ARE camelCase HERE (photoId, viewCount, likeCount, commentCount, userId) while every feed endpoint on this platform is snake_case, so do not share one parser between them. data.photo.photoId is a STRING and exact, and both author id forms come back (photo.userId numeric, photo.userEid alphanumeric), which makes this the reliable place to get them. coverUrls[] and mainMvUrls[] are CDN mirrors of the same asset, signed and expiring - take the first and fall back to the next. Pass photo_id or url; sending neither is a 400 and is not billed. Not paginated. Costs 2 credits. ${PRICING} ${NOT_KWAI}`, {
67
+ server.tool("get_kuaishou_video", `Get a Kuaishou video by photo_id or URL. Includes author follower count (saves 10cr profile call). camelCase fields (unlike feed endpoints). Pass photo_id or url. 2 credits. ${NOT_KWAI}`, {
67
68
  photo_id: z.string().min(1).optional()
68
- .describe("Kuaishou photo (video) id. Both the alphanumeric form ('3xtdqvdnqd3psuc') and the numeric form ('5211790816292981090') work. Either this or url is required. When piping from a feed endpoint, take the id from that feed's share_info rather than its rounded photo_id number."),
69
+ .describe("Photo/video id (numeric or alphanumeric). Use share_info from feeds, not rounded photo_id. Either this or url required."),
69
70
  url: z.string().url().optional()
70
- .describe("A kuaishou.com or v.kuaishou.com video URL, e.g. 'https://v.kuaishou.com/GKTpYm', as an alternative to photo_id. Either this or photo_id is required."),
71
+ .describe("kuaishou.com video URL. Either this or photo_id required."),
71
72
  }, async (params) => {
72
73
  try {
73
74
  const data = await getClient().post("/api/v1/kuaishou/video", params);
74
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
75
+ return trimResponse(data);
75
76
  }
76
77
  catch (err) {
77
78
  return handleApiError(err);
78
79
  }
79
80
  });
80
- server.tool("get_kuaishou_video_comments", `Get the comment thread under a Kuaishou (China) video as JSON, 30 root comments per page: comment text, author name and id, authorArea (the commenter's IP-location province, the only geography Kuaishou publishes per comment), like count, subCommentCount and timestamp. data.subCommentsMap is a free bonus rather than a duplicate - it is keyed by root comment_id and pre-loads the first replies for the comments Kuaishou considers hot, so read it BEFORE spending a credit on get_kuaishou_comment_replies. data.commentCount is the video's TOTAL including replies and is far larger than the rows you can page through, so never use it as a loop bound; rootComments[].subCommentCount of 0 means there is nothing for get_kuaishou_comment_replies to fetch. Cursor-paginated. Costs 1 credit per page, so a full thread is cheap to mine. ${PRICING} ${NOT_KWAI}`, {
81
+ server.tool("get_kuaishou_video_comments", `Get comments on a Kuaishou video. Check data.subCommentsMap first (free pre-loaded replies) before calling get_kuaishou_comment_replies. 1 credit/page. ${NOT_KWAI}`, {
81
82
  photo_id: z.string().min(1)
82
- .describe("Kuaishou photo (video) id. Id only - this endpoint has no URL form. Use the exact id from get_kuaishou_video (photo.photoId) or a feed's share_info, not a rounded photo_id number."),
83
+ .describe("Photo id (no URL form). Use exact id from share_info, not rounded photo_id."),
83
84
  cursor: z.string().optional()
84
85
  .describe(CURSOR_DESC),
85
86
  }, async (params) => {
86
87
  try {
87
88
  const data = await getClient().post("/api/v1/kuaishou/video/comments", params);
88
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
89
+ return trimResponse(data);
89
90
  }
90
91
  catch (err) {
91
92
  return handleApiError(err);
92
93
  }
93
94
  });
94
- server.tool("get_kuaishou_comment_replies", `Expand one comment thread on a Kuaishou (China) video: every reply under a root comment, as JSON, with reply text, author, authorArea (IP-location province), the user being answered (reply_to plus replyToUserName, so a thread can be rebuilt in order) and like count. THE EMPTY-RESULT TRAP: an unknown, mistyped, or wrong-video root_comment_id is NOT an error - Kuaishou answers HTTP 200 with subComments: [] and pcursor "no_more", and the call is still billed. Take photo_id and root_comment_id from the SAME get_kuaishou_video_comments response and only call this for comments whose subCommentCount is above 0. Check that response's data.subCommentsMap first: for the hottest comments the replies are already in hand for free. Cursor-paginated, and count sizes the page (1-50). Costs 1 credit per page. ${PRICING} ${NOT_KWAI}`, {
95
+ server.tool("get_kuaishou_comment_replies", `Get replies to a Kuaishou comment. Wrong root_comment_id silently returns empty (still billed). Only call when subCommentCount > 0. 1 credit/page. ${NOT_KWAI}`, {
95
96
  photo_id: z.string().min(1)
96
- .describe("Kuaishou photo (video) id the root comment sits on, non-empty. Use your own exact request value, not the rounded photo_id echoed inside comment objects."),
97
+ .describe("Photo id from the same video_comments response."),
97
98
  root_comment_id: z.string().min(1)
98
- .describe("Id of the top-level comment whose replies you want: rootComments[].comment_id from get_kuaishou_video_comments, taken from the same response as photo_id. An id belonging to a different video returns an empty page and still bills."),
99
+ .describe("comment_id from rootComments[]. Must match photo_id's video."),
99
100
  cursor: z.string().optional()
100
101
  .describe(CURSOR_DESC),
101
102
  count: z.number().int().min(1).max(50).optional()
102
- .describe("Replies per page, 1-50. Omit to use Kuaishou's default. A value outside 1-50 is a 400 and is not billed."),
103
+ .describe("Replies per page, 1-50."),
103
104
  }, async (params) => {
104
105
  try {
105
106
  const data = await getClient().post("/api/v1/kuaishou/video/sub-comments", params);
106
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
107
+ return trimResponse(data);
107
108
  }
108
109
  catch (err) {
109
110
  return handleApiError(err);
110
111
  }
111
112
  });
112
- server.tool("get_kuaishou_videos_batch", `Look up as many as 20 Kuaishou (China) videos in ONE request as JSON, under data.photos[]: caption, play / like / comment / share / collect counts, duration, publish time, cover and MP4 URL for each. COSTS 40 CREDITS FLAT, whatever the list length - by far the dearest call on the platform. get_kuaishou_video costs 2, so a batch only pays off from roughly 20 ids down; below that, loop the cheap call. The list is hard-capped at 20 and a longer one is rejected by validation before you are billed. MISSING IDS ARE SILENT: Kuaishou returns only the photos it resolved, so data.photos can be shorter than the ids you sent (deleted, private and region-blocked videos simply do not appear) and the 40 credits are still spent - diff the two lists yourself. Order is not guaranteed to match the request: match on photos[].originalPhotoId, which is a STRING and exact, because photos[].photo_id is a JSON number above 2^53 and arrives rounded. Field names are snake_case here, unlike the camelCase get_kuaishou_video. Not paginated. ${PRICING} Kuaishou China (kuaishou.com) only; a Kwai international (kwai.com) id returns an empty envelope and still costs 40.`, {
113
+ server.tool("get_kuaishou_videos_batch", `Batch lookup up to 20 Kuaishou videos. 40 CREDITS FLAT regardless of count (get_kuaishou_video is 2cr each, cheaper below ~20). Missing ids silently omitted. Match on originalPhotoId not rounded photo_id. ${NOT_KWAI}`, {
113
114
  photo_ids: z.array(z.string().min(1)).min(1).max(20)
114
- .describe("1 to 20 non-empty Kuaishou photo (video) ids; use the numeric id form. More than 20 is rejected before billing - chunk longer lists yourself, at 40 credits per chunk."),
115
+ .describe("1-20 photo ids (numeric form). >20 rejected."),
115
116
  }, async (params) => {
116
117
  try {
117
118
  const data = await getClient().post("/api/v1/kuaishou/videos/batch", params);
118
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
119
+ return trimResponse(data);
119
120
  }
120
121
  catch (err) {
121
122
  return handleApiError(err);
122
123
  }
123
124
  });
124
- server.tool("search_kuaishou", `Run Kuaishou (China)'s own MIXED search for a keyword as JSON: ranked videos with full engagement counts, plus the related-search suggestions the app surfaces alongside them. Rows arrive under data.mixFeeds[] and are HETEROGENEOUS - switch on itemType before reading anything: 5 is a video at item.feed, 9 is related searches at item.relatedSearches[], 4 is a user at item.user, 6 is a live stream at item.live, and 25 is a commerce promo card at item.mixKbox[] which is an ad slot, not an organic result. Never index mixFeeds positionally, and treat an unknown itemType as skippable rather than as an error. THIS IS THE SLOWEST CALL ON THE PLATFORM, roughly 20 seconds against 1.5-3 seconds for the dedicated searches, so set a client timeout above 30 seconds - or prefer search_kuaishou_videos, search_kuaishou_users or search_kuaishou_live, which cost the same 10 credits, answer far faster and come back already filtered. data.isRecommendResult true means Kuaishou filled the page with recommendations rather than keyword matches; do not read those rows as matches. Cursor-paginated. Costs 10 credits PER PAGE - all four search endpoints are the 10, so cap your page count. ${PRICING} ${NOT_KWAI}`, {
125
+ server.tool("search_kuaishou", `Mixed search on Kuaishou (videos, users, live, promos). Filter on itemType: 5=video, 4=user, 6=live, 9=related, 25=ad. Slow (~20s); prefer search_kuaishou_videos/users/live for speed. 10 credits/page. ${NOT_KWAI}`, {
125
126
  keyword: z.string().min(1).max(200)
126
127
  .describe(KEYWORD_DESC),
127
128
  cursor: z.string().optional()
@@ -129,13 +130,13 @@ export function registerKuaishouTools(server, getClient) {
129
130
  }, async (params) => {
130
131
  try {
131
132
  const data = await getClient().post("/api/v1/kuaishou/search", params);
132
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
133
+ return trimResponse(data);
133
134
  }
134
135
  catch (err) {
135
136
  return handleApiError(err);
136
137
  }
137
138
  });
138
- server.tool("search_kuaishou_videos", `Search Kuaishou (China) videos by keyword as JSON, 20 ranked results a page: caption, author, play / like / comment / share / collect counts, duration, publish time and cover image, with no user rows mixed in. Rows arrive under data.mixFeeds[] with itemType 5 and the payload at item.feed - Kuaishou can still inject a card row, so skip anything that is not itemType 5 rather than assuming index positions. feed.photo_id is a JSON number above 2^53 and arrives ROUNDED; use feed.share_info ("userId=<eid>&photoId=<eid>") for the exact ids, which is the form get_kuaishou_video accepts. Field names are snake_case here, unlike the camelCase get_kuaishou_video. Prefer this over search_kuaishou when you only want videos: same 10 credits, roughly eight times faster. Cursor-paginated. Costs 10 credits PER PAGE - all four search endpoints are the 10, so cap your page count. ${PRICING} ${NOT_KWAI}`, {
139
+ server.tool("search_kuaishou_videos", `Search Kuaishou videos by keyword. Filter on itemType=5 (item.feed). Use share_info for exact ids. Faster than search_kuaishou. 10 credits/page. ${NOT_KWAI}`, {
139
140
  keyword: z.string().min(1).max(200)
140
141
  .describe(KEYWORD_DESC),
141
142
  cursor: z.string().optional()
@@ -143,13 +144,13 @@ export function registerKuaishouTools(server, getClient) {
143
144
  }, async (params) => {
144
145
  try {
145
146
  const data = await getClient().post("/api/v1/kuaishou/search/videos", params);
146
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
147
+ return trimResponse(data);
147
148
  }
148
149
  catch (err) {
149
150
  return handleApiError(err);
150
151
  }
151
152
  });
152
- server.tool("search_kuaishou_users", `Find Kuaishou (China) creators by name, handle or topic as JSON, 20 accounts a page: display name, public handle (kwaiId), bio, fansCount, photoCount, verified and isBanned flags, and the numeric user_id. This is the creator-discovery entry point - the ids it returns are what get_kuaishou_profile, get_kuaishou_user_posts and get_kuaishou_user_live all take. Rows arrive under data.mixFeeds[] with itemType 4 and the payload at item.user; skip anything else rather than indexing positionally. user_id arrives as a JSON number, so stringify it and pass it through unchanged, do not reformat it. Field traps: fansCount is an integer but photoCount is a STRING ("20"), photoInfo is a pre-rendered Chinese summary line and not a parseable metric, the row carries NO total like count (that is ownerCount.total_photo_like on get_kuaishou_profile, 10 credits), and isBanned accounts still appear in results. There is NO follower-count or verified filter upstream - filter client-side on fansCount and verified. Cursor-paginated. Costs 10 credits per page; if you already have a share link, resolve_kuaishou_user gets you the id for 1. ${PRICING} ${NOT_KWAI}`, {
153
+ server.tool("search_kuaishou_users", `Search Kuaishou users/creators. Returns user_id needed for profile/posts/live endpoints. Filter on itemType=4. No server-side follower/verified filter. 10 credits/page. ${NOT_KWAI}`, {
153
154
  keyword: z.string().min(1).max(200)
154
155
  .describe(KEYWORD_DESC),
155
156
  cursor: z.string().optional()
@@ -157,13 +158,13 @@ export function registerKuaishouTools(server, getClient) {
157
158
  }, async (params) => {
158
159
  try {
159
160
  const data = await getClient().post("/api/v1/kuaishou/search/users", params);
160
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
161
+ return trimResponse(data);
161
162
  }
162
163
  catch (err) {
163
164
  return handleApiError(err);
164
165
  }
165
166
  });
166
- server.tool("search_kuaishou_live", `Search Kuaishou (China) live streams that are ON AIR RIGHT NOW for a keyword, as JSON: stream id, broadcaster, like count, cover, the commerce badge Kuaishou shows on the card, and the FLV playback URLs. This is how to find live-commerce broadcasts while they are still selling. Rows arrive under data.mixFeeds[]: itemType 6 is a stream at item.live, and itemType 25 is a commerce promo card at item.mixKbox[] - an ad slot carrying no stream, which was the FIRST row of the capture - so filter on itemType 6 and never index positionally. LIVE DATA IS A SNAPSHOT: streams end without notice, a liveStreamId is only good while the broadcast lasts, and live.playInfo.playUrls[] are signed FLV pull URLs that expire and stop working the moment the broadcast ends, so re-run the search instead of caching it. live.likeCount and live.audienceCount are ABBREVIATED STRINGS as the app renders them ("629.1k"), not integers - parse them or you will silently compare strings; live.acu is a number. live.simpleLiveCoverReasonTag.text.content is the card badge and carries the sales line on live-commerce streams. live.user is a reduced user object with no counters; call get_kuaishou_profile for those. Cursor-paginated. Costs 10 credits per page; checking ONE known creator is get_kuaishou_user_live at 1 credit. ${PRICING} ${NOT_KWAI}`, {
167
+ server.tool("search_kuaishou_live", `Search Kuaishou live streams currently on air. Filter on itemType=6. Data is a snapshot (URLs expire when broadcast ends). likeCount/audienceCount are strings ("629.1k"), not ints. 10 credits/page (get_kuaishou_user_live is 1cr for a single user). ${NOT_KWAI}`, {
167
168
  keyword: z.string().min(1).max(200)
168
169
  .describe(KEYWORD_DESC),
169
170
  cursor: z.string().optional()
@@ -171,33 +172,33 @@ export function registerKuaishouTools(server, getClient) {
171
172
  }, async (params) => {
172
173
  try {
173
174
  const data = await getClient().post("/api/v1/kuaishou/search/live", params);
174
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
175
+ return trimResponse(data);
175
176
  }
176
177
  catch (err) {
177
178
  return handleApiError(err);
178
179
  }
179
180
  });
180
- server.tool("get_kuaishou_tag_feed", `Read the feed behind a Kuaishou (China) hashtag as JSON, 20 posts a page: caption, author, play / like / comment / share / collect counts and cover image under data.mixFeeds[] (videos are itemType 5 with the payload at item.feed - skip other card types), plus the tag's own lifetime view total at data.tagInfo.viewCount. Send the tag TEXT without the leading '#'; Kuaishou resolves it to its own tag id and echoes both back as tagInfo.name and tagInfo.tagId, so CHECK tagInfo.name against what you asked for before trusting a run - a near-miss resolves to a different tag rather than erroring. THERE IS NO TAB OR SORT PARAMETER: you always get Kuaishou's default ordering for the tag, so do not read the feed as chronological. feed.photo_id is a JSON number above 2^53 and arrives rounded - use feed.share_info for the exact ids. Cursor-paginated. Costs 1 credit per page, a tenth of what the search endpoints cost, so this is the cheap way to mine a trend or track a campaign tag over time. ${PRICING} ${NOT_KWAI}`, {
181
+ server.tool("get_kuaishou_tag_feed", `Get Kuaishou hashtag feed. Send tag WITHOUT '#'. Check tagInfo.name (near-miss resolves to different tag). No sort param. 1 credit/page. ${NOT_KWAI}`, {
181
182
  tag: z.string().min(1).max(200)
182
- .describe("Hashtag text, 1-200 characters, WITHOUT the leading # - send '挑战', not '#挑战'. Kuaishou is a Chinese-language platform, so Chinese tags are where the volume is."),
183
+ .describe("Tag text without '#'. Chinese tags recommended."),
183
184
  cursor: z.string().optional()
184
185
  .describe(CURSOR_DESC),
185
186
  }, async (params) => {
186
187
  try {
187
188
  const data = await getClient().post("/api/v1/kuaishou/tag/feed", params);
188
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
189
+ return trimResponse(data);
189
190
  }
190
191
  catch (err) {
191
192
  return handleApiError(err);
192
193
  }
193
194
  });
194
- server.tool("get_kuaishou_trending", `Read one Kuaishou (China) leaderboard as JSON - one board per call, and each call bills. board 'hot' (the default when omitted) returns the hot-search keyword list: 50 ranked entries under data.hots already sorted by hotValue descending, plus the pinned data.topHots, each entry carrying id, keyword, hotValue, hotWordType, ksOrderId, pvSoarSignal and viewCount. The live, shopping, brand and music boards are SEPARATE upstream rankings with their OWN response shapes - do not assume they carry hots[]; read the one you need once and map it yourself. Fields that look useful and are not: hotValue is Kuaishou's heat score, not views or searches, and is only comparable within one snapshot of one board; viewCount and pvSoarSignal were 0 on every row of the capture, so treat them as unpopulated rather than as zero traffic; a non-empty ksOrderId marks a promoted slot, so check it before treating an entry as organic. Not paginated, with no limit or region parameter and no historical view - poll on a schedule and store the results if you want a time series. The keywords are the natural input to get_kuaishou_tag_feed (1 credit) or search_kuaishou_videos (10). Costs 1 credit whichever board you ask for, the cheapest trend signal on the platform. ${PRICING} ${NOT_KWAI}`, {
195
+ server.tool("get_kuaishou_trending", `Get a Kuaishou trending leaderboard. Each board has a different response shape. Non-empty ksOrderId = promoted slot. 1 credit. ${NOT_KWAI}`, {
195
196
  board: z.enum(["hot", "live", "shopping", "brand", "music"]).optional()
196
- .describe("Which leaderboard to read: 'hot' (used when omitted) is the hot-search keyword list; 'live', 'shopping', 'brand' and 'music' are the live-stream, e-commerce, brand and music rankings. One board per call, and each board is a different upstream ranking with its own response shape."),
197
+ .describe("Board: 'hot' (default), 'live', 'shopping', 'brand', 'music'."),
197
198
  }, async (params) => {
198
199
  try {
199
200
  const data = await getClient().post("/api/v1/kuaishou/trending", params);
200
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
201
+ return trimResponse(data);
201
202
  }
202
203
  catch (err) {
203
204
  return handleApiError(err);