@fload-ai/mcp 0.2.1 → 0.2.2
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 +1 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/api-client.d.ts +5 -4
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +59 -6
- package/dist/api-client.js.map +3 -3
- package/dist/bin.js +450 -184
- package/dist/bin.js.map +4 -4
- package/dist/index.js +450 -184
- package/dist/index.js.map +4 -4
- package/dist/lib/format.d.ts +1 -1
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/public-errors.d.ts +2 -0
- package/dist/public-errors.d.ts.map +1 -0
- package/dist/tools/actions.d.ts +0 -3
- package/dist/tools/actions.d.ts.map +1 -1
- package/dist/tools/ads.d.ts +2 -2
- package/dist/tools/ads.d.ts.map +1 -1
- package/dist/tools/agents.d.ts +4 -4
- package/dist/tools/agents.d.ts.map +1 -1
- package/dist/tools/analytics.d.ts +2 -2
- package/dist/tools/analytics.d.ts.map +1 -1
- package/dist/tools/anomalies.d.ts +2 -2
- package/dist/tools/anomalies.d.ts.map +1 -1
- package/dist/tools/apps.d.ts +2 -2
- package/dist/tools/apps.d.ts.map +1 -1
- package/dist/tools/aso.d.ts +14 -5
- package/dist/tools/aso.d.ts.map +1 -1
- package/dist/tools/chat.d.ts +7 -9
- package/dist/tools/chat.d.ts.map +1 -1
- package/dist/tools/dashboard.d.ts.map +1 -1
- package/dist/tools/forecasting.d.ts +9 -3
- package/dist/tools/forecasting.d.ts.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +392 -178
- package/dist/tools/index.js.map +3 -3
- package/dist/tools/reviews.d.ts +19 -4
- package/dist/tools/reviews.d.ts.map +1 -1
- package/package.json +11 -13
package/dist/tools/index.js
CHANGED
|
@@ -53,9 +53,12 @@ async function listApps(input, client) {
|
|
|
53
53
|
try {
|
|
54
54
|
const response = await client.get("/api/assets", {
|
|
55
55
|
limit: input.limit,
|
|
56
|
-
offset: 0
|
|
56
|
+
offset: 0,
|
|
57
|
+
// The listing hides assets without a valuation by default; "list all
|
|
58
|
+
// apps" must include apps that haven't been valued yet (FLO-734).
|
|
59
|
+
includeUnvalued: "true"
|
|
57
60
|
});
|
|
58
|
-
const apps = response.data
|
|
61
|
+
const apps = response.data ?? [];
|
|
59
62
|
const filtered = input.platform ? apps.filter((app) => {
|
|
60
63
|
if (input.platform === "ios")
|
|
61
64
|
return !!app.appleAppId;
|
|
@@ -99,8 +102,14 @@ async function getAppDetails(input, client) {
|
|
|
99
102
|
}
|
|
100
103
|
let assetId = input.assetId;
|
|
101
104
|
if (!assetId && input.bundleId) {
|
|
102
|
-
const allApps = await client.get("/api/assets", {
|
|
103
|
-
|
|
105
|
+
const allApps = await client.get("/api/assets", {
|
|
106
|
+
limit: 100,
|
|
107
|
+
offset: 0,
|
|
108
|
+
includeUnvalued: "true"
|
|
109
|
+
});
|
|
110
|
+
const match = (allApps.data ?? []).find(
|
|
111
|
+
(app2) => app2.bundleId === input.bundleId
|
|
112
|
+
);
|
|
104
113
|
if (!match) {
|
|
105
114
|
return {
|
|
106
115
|
content: [
|
|
@@ -114,8 +123,13 @@ async function getAppDetails(input, client) {
|
|
|
114
123
|
}
|
|
115
124
|
assetId = match.id;
|
|
116
125
|
}
|
|
117
|
-
const response = await client.get(
|
|
118
|
-
|
|
126
|
+
const response = await client.get(
|
|
127
|
+
`/api/assets/${assetId}`
|
|
128
|
+
);
|
|
129
|
+
const app = response.data;
|
|
130
|
+
if (!app) {
|
|
131
|
+
throw new Error("App details response did not include app data");
|
|
132
|
+
}
|
|
119
133
|
return {
|
|
120
134
|
content: [
|
|
121
135
|
{
|
|
@@ -172,14 +186,27 @@ var sendReviewReplySchema = z2.object({
|
|
|
172
186
|
});
|
|
173
187
|
var translateReviewSchema = z2.object({
|
|
174
188
|
reviewId: z2.string().describe("The review UUID to translate"),
|
|
175
|
-
assetId: z2.string().uuid().describe("The app UUID the review belongs to")
|
|
189
|
+
assetId: z2.string().uuid().describe("The app UUID the review belongs to"),
|
|
190
|
+
// The translate endpoint receives the review content in the request body
|
|
191
|
+
// (it does not re-fetch it). Pass the fields from a get_reviews result.
|
|
192
|
+
rating: z2.number().int().min(1).max(5).describe("The review star rating, from get_reviews"),
|
|
193
|
+
title: z2.string().default("").describe("The review title, from get_reviews"),
|
|
194
|
+
body: z2.string().describe("The review text to translate, from get_reviews"),
|
|
195
|
+
storeFront: z2.string().default("").describe('The review storefront (e.g. "SE"), from get_reviews'),
|
|
196
|
+
appVersionString: z2.string().default("").describe("The app version the review was left on, from get_reviews")
|
|
176
197
|
});
|
|
177
198
|
async function getReviews(input, client) {
|
|
178
199
|
try {
|
|
179
200
|
let assetId = input.assetId;
|
|
180
201
|
if (!assetId && input.bundleId) {
|
|
181
|
-
const allApps = await client.get("/api/assets", {
|
|
182
|
-
|
|
202
|
+
const allApps = await client.get("/api/assets", {
|
|
203
|
+
limit: 100,
|
|
204
|
+
offset: 0,
|
|
205
|
+
includeUnvalued: "true"
|
|
206
|
+
});
|
|
207
|
+
const match = (allApps.data ?? []).find(
|
|
208
|
+
(app) => app.bundleId === input.bundleId
|
|
209
|
+
);
|
|
183
210
|
if (!match) {
|
|
184
211
|
return {
|
|
185
212
|
content: [
|
|
@@ -193,12 +220,20 @@ async function getReviews(input, client) {
|
|
|
193
220
|
}
|
|
194
221
|
assetId = match.id;
|
|
195
222
|
}
|
|
223
|
+
if (!assetId) {
|
|
224
|
+
return {
|
|
225
|
+
content: [
|
|
226
|
+
{
|
|
227
|
+
type: "text",
|
|
228
|
+
text: "Provide an assetId (or bundleId) to fetch reviews for. Use list_apps to find app IDs."
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
isError: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
196
234
|
const params = {
|
|
197
|
-
limit: input.limit
|
|
198
|
-
sortBy: input.sortBy
|
|
235
|
+
limit: input.limit
|
|
199
236
|
};
|
|
200
|
-
if (assetId)
|
|
201
|
-
params.assetId = assetId;
|
|
202
237
|
if (input.platform)
|
|
203
238
|
params.platform = input.platform;
|
|
204
239
|
if (input.rating !== void 0)
|
|
@@ -209,13 +244,25 @@ async function getReviews(input, client) {
|
|
|
209
244
|
params.startDate = input.startDate;
|
|
210
245
|
if (input.endDate)
|
|
211
246
|
params.endDate = input.endDate;
|
|
212
|
-
const response = await client.get(
|
|
213
|
-
|
|
247
|
+
const response = await client.get(
|
|
248
|
+
`/api/assets/${assetId}/reviews`,
|
|
249
|
+
params
|
|
250
|
+
);
|
|
251
|
+
let reviews = response.data?.reviews ?? [];
|
|
252
|
+
if (input.sortBy === "rating") {
|
|
253
|
+
reviews = [...reviews].sort(
|
|
254
|
+
(a, b) => Number(b.rating ?? 0) - Number(a.rating ?? 0)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const hasReply = (review) => !!(review.developerResponse || review.replySource || review.hasReply);
|
|
214
258
|
const summary = {
|
|
215
259
|
totalReviews: reviews.length,
|
|
216
|
-
averageRating: reviews.length > 0 ? (reviews.reduce((sum,
|
|
217
|
-
|
|
218
|
-
|
|
260
|
+
averageRating: reviews.length > 0 ? (reviews.reduce((sum, review) => {
|
|
261
|
+
const rating = Number(review.rating ?? 0);
|
|
262
|
+
return sum + (Number.isFinite(rating) ? rating : 0);
|
|
263
|
+
}, 0) / reviews.length).toFixed(2) : 0,
|
|
264
|
+
repliedCount: reviews.filter((review) => hasReply(review)).length,
|
|
265
|
+
unrepliedCount: reviews.filter((review) => !hasReply(review)).length
|
|
219
266
|
};
|
|
220
267
|
return {
|
|
221
268
|
content: [
|
|
@@ -223,19 +270,23 @@ async function getReviews(input, client) {
|
|
|
223
270
|
type: "text",
|
|
224
271
|
text: formatAsJson({
|
|
225
272
|
summary,
|
|
226
|
-
reviews: reviews.map((
|
|
227
|
-
id:
|
|
228
|
-
appId:
|
|
229
|
-
platform:
|
|
230
|
-
rating:
|
|
231
|
-
title:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
273
|
+
reviews: reviews.map((review) => ({
|
|
274
|
+
id: review.id,
|
|
275
|
+
appId: review.appId,
|
|
276
|
+
platform: review.platform,
|
|
277
|
+
rating: review.rating,
|
|
278
|
+
title: review.title,
|
|
279
|
+
// The handler exposes the review text as `review`.
|
|
280
|
+
body: review.review ?? review.body,
|
|
281
|
+
author: review.nickname || review.author,
|
|
282
|
+
// Epoch ms from the handler.
|
|
283
|
+
date: review.lastModified || review.date,
|
|
284
|
+
version: review.appVersionString || review.version,
|
|
285
|
+
storeFront: review.storeFront,
|
|
286
|
+
hasReply: hasReply(review),
|
|
287
|
+
replySource: review.replySource ?? null,
|
|
288
|
+
hasDraft: review.hasDraft ?? false,
|
|
289
|
+
reply: review.developerResponse ? typeof review.developerResponse === "object" ? review.developerResponse.response : review.developerResponse : review.reply || null
|
|
239
290
|
}))
|
|
240
291
|
})
|
|
241
292
|
}
|
|
@@ -255,9 +306,10 @@ async function getReviews(input, client) {
|
|
|
255
306
|
}
|
|
256
307
|
async function generateReviewReply(input, client) {
|
|
257
308
|
try {
|
|
258
|
-
const response = await client.post(
|
|
259
|
-
assetId
|
|
260
|
-
|
|
309
|
+
const response = await client.post(
|
|
310
|
+
`/api/assets/${input.assetId}/reviews/${input.reviewId}/generate-reply`,
|
|
311
|
+
void 0
|
|
312
|
+
);
|
|
261
313
|
return {
|
|
262
314
|
content: [
|
|
263
315
|
{
|
|
@@ -280,10 +332,11 @@ async function generateReviewReply(input, client) {
|
|
|
280
332
|
}
|
|
281
333
|
async function sendReviewReply(input, client) {
|
|
282
334
|
try {
|
|
283
|
-
const response = await client.post(
|
|
284
|
-
assetId
|
|
285
|
-
|
|
286
|
-
|
|
335
|
+
const response = await client.post(
|
|
336
|
+
`/api/assets/${input.assetId}/reviews/${input.reviewId}/respond`,
|
|
337
|
+
// The API handler reads `responseText` from the body.
|
|
338
|
+
{ responseText: input.response }
|
|
339
|
+
);
|
|
287
340
|
return {
|
|
288
341
|
content: [
|
|
289
342
|
{
|
|
@@ -306,9 +359,18 @@ async function sendReviewReply(input, client) {
|
|
|
306
359
|
}
|
|
307
360
|
async function translateReview(input, client) {
|
|
308
361
|
try {
|
|
309
|
-
const response = await client.post(
|
|
310
|
-
assetId
|
|
311
|
-
|
|
362
|
+
const response = await client.post(
|
|
363
|
+
`/api/assets/${input.assetId}/reviews/${input.reviewId}/translate`,
|
|
364
|
+
// The translate endpoint expects the review content in the body.
|
|
365
|
+
{
|
|
366
|
+
rating: input.rating,
|
|
367
|
+
title: input.title,
|
|
368
|
+
review: input.body,
|
|
369
|
+
storeFront: input.storeFront,
|
|
370
|
+
appVersionString: input.appVersionString,
|
|
371
|
+
lastModified: Date.now()
|
|
372
|
+
}
|
|
373
|
+
);
|
|
312
374
|
return {
|
|
313
375
|
content: [
|
|
314
376
|
{
|
|
@@ -388,9 +450,11 @@ var DIMENSION_INFO = {
|
|
|
388
450
|
};
|
|
389
451
|
async function discoverMetrics(input, client) {
|
|
390
452
|
try {
|
|
391
|
-
const response = await client.get(
|
|
392
|
-
|
|
393
|
-
|
|
453
|
+
const response = await client.get(
|
|
454
|
+
`/api/assets/${input.assetId}/metrics/availability`
|
|
455
|
+
);
|
|
456
|
+
const availableMetrics = response.data?.availableMetrics ?? [];
|
|
457
|
+
const metricGroups = response.data?.metricGroups ?? {};
|
|
394
458
|
const enriched = availableMetrics.map((metricName) => {
|
|
395
459
|
const info = METRIC_INFO[metricName];
|
|
396
460
|
return {
|
|
@@ -441,10 +505,13 @@ async function getMetrics(input, client) {
|
|
|
441
505
|
if (input.dimensionFilter && input.dimension) {
|
|
442
506
|
params[`filter.${input.dimension}`] = input.dimensionFilter;
|
|
443
507
|
}
|
|
444
|
-
const response = await client.get(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
508
|
+
const response = await client.get(
|
|
509
|
+
`/api/assets/${input.assetId}/metrics/timeseries`,
|
|
510
|
+
params
|
|
511
|
+
);
|
|
512
|
+
const data = response.data ?? {};
|
|
513
|
+
const changes = response.changes ?? {};
|
|
514
|
+
const meta = response.meta ?? {};
|
|
448
515
|
const summaries = {};
|
|
449
516
|
for (const [metricName, timeseries] of Object.entries(data)) {
|
|
450
517
|
const points = timeseries;
|
|
@@ -500,17 +567,20 @@ async function discoverDimensions(input, client) {
|
|
|
500
567
|
assetId: input.assetId,
|
|
501
568
|
dimension: input.dimension,
|
|
502
569
|
displayName: DIMENSION_INFO[input.dimension] || input.dimension,
|
|
503
|
-
values: response2.data
|
|
570
|
+
values: response2.data ?? response2
|
|
504
571
|
})
|
|
505
572
|
}]
|
|
506
573
|
};
|
|
507
574
|
}
|
|
508
575
|
const response = await client.get(`/api/assets/${input.assetId}/metrics/dimensions`);
|
|
509
|
-
const dimensions = response.data
|
|
510
|
-
const enriched =
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
576
|
+
const dimensions = Array.isArray(response.data) ? response.data : [];
|
|
577
|
+
const enriched = dimensions.map((dim) => {
|
|
578
|
+
if (typeof dim === "string") {
|
|
579
|
+
return { name: dim, displayName: DIMENSION_INFO[dim] || dim };
|
|
580
|
+
}
|
|
581
|
+
const id = dim.id ?? "";
|
|
582
|
+
return { name: id, displayName: dim.label ?? DIMENSION_INFO[id] ?? id };
|
|
583
|
+
});
|
|
514
584
|
return {
|
|
515
585
|
content: [{
|
|
516
586
|
type: "text",
|
|
@@ -528,6 +598,9 @@ async function discoverDimensions(input, client) {
|
|
|
528
598
|
|
|
529
599
|
// src/tools/agents.ts
|
|
530
600
|
import { z as z4 } from "zod";
|
|
601
|
+
function asRecord(value) {
|
|
602
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
603
|
+
}
|
|
531
604
|
var AGENT_TYPES = [
|
|
532
605
|
"review",
|
|
533
606
|
"monitoring",
|
|
@@ -576,7 +649,7 @@ async function listAgents(_input, client) {
|
|
|
576
649
|
let apiAgents = null;
|
|
577
650
|
try {
|
|
578
651
|
const response = await client.get("/api/agents");
|
|
579
|
-
apiAgents = response.data
|
|
652
|
+
apiAgents = Array.isArray(response) ? response : response.data ?? response.agents ?? null;
|
|
580
653
|
} catch {
|
|
581
654
|
}
|
|
582
655
|
if (apiAgents && Array.isArray(apiAgents)) {
|
|
@@ -626,21 +699,26 @@ async function getAgentDetails(input, client) {
|
|
|
626
699
|
const params = {};
|
|
627
700
|
if (input.assetId)
|
|
628
701
|
params.assetId = input.assetId;
|
|
629
|
-
const response2 = await client.get(`/api/agents/${input.agentType}`, params);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
702
|
+
const response2 = await client.get(`/api/agents/by-type/${input.agentType}`, params);
|
|
703
|
+
if (response2.agent) {
|
|
704
|
+
return {
|
|
705
|
+
content: [
|
|
706
|
+
{
|
|
707
|
+
type: "text",
|
|
708
|
+
text: formatAsJson({
|
|
709
|
+
agent: response2.agent,
|
|
710
|
+
recentRuns: response2.recentRuns ?? []
|
|
711
|
+
})
|
|
712
|
+
}
|
|
713
|
+
]
|
|
714
|
+
};
|
|
715
|
+
}
|
|
638
716
|
} catch {
|
|
639
717
|
}
|
|
640
718
|
const response = {
|
|
641
719
|
type: input.agentType,
|
|
642
720
|
description: AGENT_DESCRIPTIONS[input.agentType],
|
|
643
|
-
message:
|
|
721
|
+
message: `No ${input.agentType} agent is provisioned for this organization yet. Activate it from the Agents page or with trigger_agent_run once created.`
|
|
644
722
|
};
|
|
645
723
|
return {
|
|
646
724
|
content: [
|
|
@@ -664,33 +742,44 @@ async function getAgentDetails(input, client) {
|
|
|
664
742
|
}
|
|
665
743
|
async function getAgentRunHistory(input, client) {
|
|
666
744
|
try {
|
|
745
|
+
let agentId;
|
|
667
746
|
try {
|
|
668
|
-
const
|
|
669
|
-
limit: input.limit
|
|
670
|
-
};
|
|
747
|
+
const byTypeParams = {};
|
|
671
748
|
if (input.assetId)
|
|
672
|
-
|
|
673
|
-
const
|
|
749
|
+
byTypeParams.assetId = input.assetId;
|
|
750
|
+
const byType = await client.get(
|
|
751
|
+
`/api/agents/by-type/${input.agentType}`,
|
|
752
|
+
byTypeParams
|
|
753
|
+
);
|
|
754
|
+
agentId = byType.agent?.id;
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
if (!agentId) {
|
|
674
758
|
return {
|
|
675
759
|
content: [
|
|
676
760
|
{
|
|
677
761
|
type: "text",
|
|
678
762
|
text: formatAsJson({
|
|
679
763
|
agentType: input.agentType,
|
|
680
|
-
|
|
764
|
+
runs: [],
|
|
765
|
+
message: `No ${input.agentType} agent is provisioned for this organization yet, so there is no run history.`
|
|
681
766
|
})
|
|
682
767
|
}
|
|
683
768
|
]
|
|
684
769
|
};
|
|
685
|
-
} catch {
|
|
686
770
|
}
|
|
771
|
+
const response = await client.get(
|
|
772
|
+
`/api/agents/${agentId}/runs`,
|
|
773
|
+
{ limit: input.limit }
|
|
774
|
+
);
|
|
687
775
|
return {
|
|
688
776
|
content: [
|
|
689
777
|
{
|
|
690
778
|
type: "text",
|
|
691
779
|
text: formatAsJson({
|
|
692
780
|
agentType: input.agentType,
|
|
693
|
-
|
|
781
|
+
agentId,
|
|
782
|
+
...asRecord(response.data ?? response)
|
|
694
783
|
})
|
|
695
784
|
}
|
|
696
785
|
]
|
|
@@ -709,15 +798,16 @@ async function getAgentRunHistory(input, client) {
|
|
|
709
798
|
}
|
|
710
799
|
async function triggerAgentRun(input, client) {
|
|
711
800
|
try {
|
|
712
|
-
const body = {};
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
801
|
+
const body = input.assetId ? { triggerContext: { assetId: input.assetId } } : {};
|
|
802
|
+
const response = await client.post(
|
|
803
|
+
`/api/agents/${input.agentId}/run`,
|
|
804
|
+
body
|
|
805
|
+
);
|
|
716
806
|
return {
|
|
717
807
|
content: [
|
|
718
808
|
{
|
|
719
809
|
type: "text",
|
|
720
|
-
text: formatAsJson(response.data
|
|
810
|
+
text: formatAsJson(response.data ?? response)
|
|
721
811
|
}
|
|
722
812
|
]
|
|
723
813
|
};
|
|
@@ -735,12 +825,14 @@ async function triggerAgentRun(input, client) {
|
|
|
735
825
|
}
|
|
736
826
|
async function pauseAgent(input, client) {
|
|
737
827
|
try {
|
|
738
|
-
const response = await client.post(
|
|
828
|
+
const response = await client.post(
|
|
829
|
+
`/api/agents/${input.agentId}/pause`
|
|
830
|
+
);
|
|
739
831
|
return {
|
|
740
832
|
content: [
|
|
741
833
|
{
|
|
742
834
|
type: "text",
|
|
743
|
-
text: formatAsJson(response.data
|
|
835
|
+
text: formatAsJson(response.data ?? response)
|
|
744
836
|
}
|
|
745
837
|
]
|
|
746
838
|
};
|
|
@@ -758,12 +850,14 @@ async function pauseAgent(input, client) {
|
|
|
758
850
|
}
|
|
759
851
|
async function resumeAgent(input, client) {
|
|
760
852
|
try {
|
|
761
|
-
const response = await client.post(
|
|
853
|
+
const response = await client.post(
|
|
854
|
+
`/api/agents/${input.agentId}/resume`
|
|
855
|
+
);
|
|
762
856
|
return {
|
|
763
857
|
content: [
|
|
764
858
|
{
|
|
765
859
|
type: "text",
|
|
766
|
-
text: formatAsJson(response.data
|
|
860
|
+
text: formatAsJson(response.data ?? response)
|
|
767
861
|
}
|
|
768
862
|
]
|
|
769
863
|
};
|
|
@@ -781,12 +875,14 @@ async function resumeAgent(input, client) {
|
|
|
781
875
|
}
|
|
782
876
|
async function getAgentActivity(input, client) {
|
|
783
877
|
try {
|
|
784
|
-
const response = await client.get(
|
|
878
|
+
const response = await client.get(
|
|
879
|
+
`/api/agents/${input.agentId}/activity`
|
|
880
|
+
);
|
|
785
881
|
return {
|
|
786
882
|
content: [
|
|
787
883
|
{
|
|
788
884
|
type: "text",
|
|
789
|
-
text: formatAsJson(response.data
|
|
885
|
+
text: formatAsJson(response.data ?? response)
|
|
790
886
|
}
|
|
791
887
|
]
|
|
792
888
|
};
|
|
@@ -844,34 +940,40 @@ async function getAnomalies(input, client) {
|
|
|
844
940
|
params.fromDate = input.fromDate;
|
|
845
941
|
if (input.toDate)
|
|
846
942
|
params.toDate = input.toDate;
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
943
|
+
const response = await client.get(
|
|
944
|
+
"/api/anomalies",
|
|
945
|
+
params
|
|
946
|
+
);
|
|
947
|
+
let anomalies = response.data ?? response.anomalies ?? [];
|
|
948
|
+
if (input.excludeDismissed && !input.status) {
|
|
949
|
+
anomalies = anomalies.filter((a) => a.status !== "dismissed");
|
|
950
|
+
}
|
|
851
951
|
return {
|
|
852
952
|
content: [
|
|
853
953
|
{
|
|
854
954
|
type: "text",
|
|
855
955
|
text: formatAsJson({
|
|
856
956
|
total: anomalies.length,
|
|
857
|
-
anomalies: anomalies.map((
|
|
858
|
-
id:
|
|
859
|
-
assetId:
|
|
860
|
-
assetName:
|
|
861
|
-
assetIcon:
|
|
862
|
-
anomalyDate:
|
|
863
|
-
metricName:
|
|
864
|
-
sourceType:
|
|
865
|
-
type:
|
|
866
|
-
severity:
|
|
867
|
-
actualValue: parseFloat(
|
|
868
|
-
expectedValue: parseFloat(
|
|
869
|
-
deviationPercent: parseFloat(
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
957
|
+
anomalies: anomalies.map((anomaly) => ({
|
|
958
|
+
id: anomaly.id,
|
|
959
|
+
assetId: anomaly.assetId,
|
|
960
|
+
assetName: anomaly.assetName,
|
|
961
|
+
assetIcon: anomaly.assetIcon,
|
|
962
|
+
anomalyDate: anomaly.anomalyDate,
|
|
963
|
+
metricName: anomaly.metricName,
|
|
964
|
+
sourceType: anomaly.sourceType,
|
|
965
|
+
type: anomaly.type,
|
|
966
|
+
severity: anomaly.severity,
|
|
967
|
+
actualValue: parseFloat(String(anomaly.actualValue || "0")),
|
|
968
|
+
expectedValue: parseFloat(String(anomaly.expectedValue || "0")),
|
|
969
|
+
deviationPercent: parseFloat(
|
|
970
|
+
String(anomaly.deviationPercent || "0")
|
|
971
|
+
),
|
|
972
|
+
confidence: parseFloat(String(anomaly.confidence || "0")),
|
|
973
|
+
explanation: anomaly.explanation,
|
|
974
|
+
suggestedActions: anomaly.suggestedActions,
|
|
975
|
+
status: anomaly.status,
|
|
976
|
+
detectedAt: anomaly.detectedAt
|
|
875
977
|
}))
|
|
876
978
|
})
|
|
877
979
|
}
|
|
@@ -970,37 +1072,46 @@ var getAdsPerformanceSchema = z6.object({
|
|
|
970
1072
|
});
|
|
971
1073
|
async function getAdsPerformance(input, client) {
|
|
972
1074
|
try {
|
|
973
|
-
const params = {
|
|
974
|
-
limit: input.limit
|
|
975
|
-
};
|
|
1075
|
+
const params = {};
|
|
976
1076
|
if (input.assetId)
|
|
977
1077
|
params.assetId = input.assetId;
|
|
978
|
-
if (input.platform)
|
|
979
|
-
params.platform = input.platform;
|
|
980
1078
|
if (input.fromDate)
|
|
981
|
-
params.
|
|
1079
|
+
params.from = input.fromDate;
|
|
982
1080
|
if (input.toDate)
|
|
983
|
-
params.
|
|
984
|
-
const response = await client.get(
|
|
985
|
-
|
|
1081
|
+
params.to = input.toDate;
|
|
1082
|
+
const response = await client.get(
|
|
1083
|
+
"/api/ads-agent/campaigns",
|
|
1084
|
+
params
|
|
1085
|
+
);
|
|
1086
|
+
let campaigns = response.data ?? response.campaigns ?? [];
|
|
1087
|
+
if (input.platform) {
|
|
1088
|
+
campaigns = campaigns.filter((c) => c.platform === input.platform);
|
|
1089
|
+
}
|
|
1090
|
+
campaigns = campaigns.slice(0, input.limit);
|
|
986
1091
|
return {
|
|
987
1092
|
content: [
|
|
988
1093
|
{
|
|
989
1094
|
type: "text",
|
|
990
1095
|
text: formatAsJson({
|
|
991
1096
|
total: campaigns.length,
|
|
992
|
-
campaigns: campaigns.map((
|
|
993
|
-
id:
|
|
994
|
-
assetId:
|
|
995
|
-
assetName:
|
|
996
|
-
platformCampaignId:
|
|
997
|
-
name:
|
|
998
|
-
status:
|
|
999
|
-
objective:
|
|
1000
|
-
platform:
|
|
1001
|
-
linkSource:
|
|
1002
|
-
linkedAt:
|
|
1003
|
-
|
|
1097
|
+
campaigns: campaigns.map((campaign) => ({
|
|
1098
|
+
id: campaign.id,
|
|
1099
|
+
assetId: campaign.assetId,
|
|
1100
|
+
assetName: campaign.assetName,
|
|
1101
|
+
platformCampaignId: campaign.platformCampaignId,
|
|
1102
|
+
name: campaign.name,
|
|
1103
|
+
status: campaign.status,
|
|
1104
|
+
objective: campaign.objective,
|
|
1105
|
+
platform: campaign.platform,
|
|
1106
|
+
linkSource: campaign.linkSource,
|
|
1107
|
+
linkedAt: campaign.linkedAt,
|
|
1108
|
+
totalSpend: campaign.totalSpend ?? null,
|
|
1109
|
+
totalImpressions: campaign.totalImpressions ?? null,
|
|
1110
|
+
totalTaps: campaign.totalTaps ?? null,
|
|
1111
|
+
totalInstalls: campaign.totalInstalls ?? null,
|
|
1112
|
+
avgCPI: campaign.avgCPI ?? null,
|
|
1113
|
+
avgTTR: campaign.avgTTR ?? null,
|
|
1114
|
+
avgCVR: campaign.avgCVR ?? null
|
|
1004
1115
|
}))
|
|
1005
1116
|
})
|
|
1006
1117
|
}
|
|
@@ -1078,13 +1189,17 @@ async function getGrowthScore(input, client) {
|
|
|
1078
1189
|
import { z as z8 } from "zod";
|
|
1079
1190
|
var getForecastsSchema = z8.object({
|
|
1080
1191
|
assetId: z8.string().uuid().describe("App UUID to get forecasts for"),
|
|
1081
|
-
|
|
1192
|
+
metric: z8.enum(["proceeds", "total_revenue", "total_downloads", "active_subs", "new_trials"]).describe("The metric to forecast"),
|
|
1193
|
+
forecastWeeks: z8.number().int().min(4).max(156).optional().describe("Weeks to forecast forward (default 104)"),
|
|
1194
|
+
historicalWeeks: z8.number().int().min(12).max(156).optional().describe("Weeks of history to base the forecast on (default 104)")
|
|
1082
1195
|
});
|
|
1083
1196
|
async function getForecasts(input, client) {
|
|
1084
1197
|
try {
|
|
1085
1198
|
const response = await client.get("/api/forecasting/forecast", {
|
|
1086
1199
|
assetId: input.assetId,
|
|
1087
|
-
|
|
1200
|
+
metricName: input.metric,
|
|
1201
|
+
forecastWeeks: input.forecastWeeks,
|
|
1202
|
+
historicalWeeks: input.historicalWeeks
|
|
1088
1203
|
});
|
|
1089
1204
|
return {
|
|
1090
1205
|
content: [
|
|
@@ -1109,18 +1224,34 @@ async function getForecasts(input, client) {
|
|
|
1109
1224
|
|
|
1110
1225
|
// src/tools/dashboard.ts
|
|
1111
1226
|
import { z as z9 } from "zod";
|
|
1227
|
+
function parseDashboardNumber(value) {
|
|
1228
|
+
if (value === null || value === void 0 || value === "")
|
|
1229
|
+
return null;
|
|
1230
|
+
const parsed = typeof value === "number" ? value : Number.parseFloat(value);
|
|
1231
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1232
|
+
}
|
|
1233
|
+
function getSidebarAssets(response) {
|
|
1234
|
+
if (!response)
|
|
1235
|
+
return [];
|
|
1236
|
+
if (Array.isArray(response))
|
|
1237
|
+
return response;
|
|
1238
|
+
return response.data ?? [];
|
|
1239
|
+
}
|
|
1112
1240
|
var getDashboardOverviewSchema = z9.object({});
|
|
1113
1241
|
async function getDashboardOverview(_input, client) {
|
|
1114
1242
|
try {
|
|
1243
|
+
const overviewPromise = client.get("/api/dashboard/overview-metrics").catch(() => null);
|
|
1244
|
+
const sidebarPromise = client.get(
|
|
1245
|
+
"/api/dashboard/sidebar-assets"
|
|
1246
|
+
).catch(() => null);
|
|
1115
1247
|
const [overviewResponse, sidebarResponse] = await Promise.all([
|
|
1116
|
-
|
|
1117
|
-
|
|
1248
|
+
overviewPromise,
|
|
1249
|
+
sidebarPromise
|
|
1118
1250
|
]);
|
|
1119
|
-
const overview = overviewResponse?.data
|
|
1120
|
-
const
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1123
|
-
return sum + parseFloat(a.currentValuation || "0");
|
|
1251
|
+
const overview = overviewResponse?.data ?? overviewResponse ?? {};
|
|
1252
|
+
const apps = getSidebarAssets(sidebarResponse);
|
|
1253
|
+
const totalValuation = apps.reduce((sum, app) => {
|
|
1254
|
+
return sum + (parseDashboardNumber(app.currentValuation) ?? 0);
|
|
1124
1255
|
}, 0);
|
|
1125
1256
|
return {
|
|
1126
1257
|
content: [
|
|
@@ -1130,16 +1261,16 @@ async function getDashboardOverview(_input, client) {
|
|
|
1130
1261
|
portfolio: {
|
|
1131
1262
|
totalApps: apps.length,
|
|
1132
1263
|
totalValuation: totalValuation > 0 ? totalValuation : null,
|
|
1133
|
-
apps: apps.map((
|
|
1134
|
-
id:
|
|
1135
|
-
name:
|
|
1136
|
-
bundleId:
|
|
1137
|
-
platform:
|
|
1138
|
-
currentValuation:
|
|
1139
|
-
rating:
|
|
1140
|
-
ratingCount:
|
|
1141
|
-
category:
|
|
1142
|
-
iconUrl:
|
|
1264
|
+
apps: apps.map((app) => ({
|
|
1265
|
+
id: app.id,
|
|
1266
|
+
name: app.name,
|
|
1267
|
+
bundleId: app.bundleId,
|
|
1268
|
+
platform: app.appleAppId ? "ios" : app.googleAppId ? "android" : "unknown",
|
|
1269
|
+
currentValuation: parseDashboardNumber(app.currentValuation),
|
|
1270
|
+
rating: parseDashboardNumber(app.rating),
|
|
1271
|
+
ratingCount: app.ratingCount ?? null,
|
|
1272
|
+
category: app.category ?? null,
|
|
1273
|
+
iconUrl: app.iconUrl ?? null
|
|
1143
1274
|
}))
|
|
1144
1275
|
},
|
|
1145
1276
|
overview
|
|
@@ -1162,13 +1293,15 @@ async function getDashboardOverview(_input, client) {
|
|
|
1162
1293
|
|
|
1163
1294
|
// src/tools/actions.ts
|
|
1164
1295
|
import { z as z10 } from "zod";
|
|
1296
|
+
function asRecord2(value) {
|
|
1297
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1298
|
+
}
|
|
1165
1299
|
var listPendingActionsSchema = z10.object({
|
|
1166
1300
|
assetId: z10.string().uuid().optional().describe("Filter by app UUID"),
|
|
1167
1301
|
limit: z10.number().int().min(1).max(100).default(50).describe("Maximum number of actions to return")
|
|
1168
1302
|
});
|
|
1169
1303
|
var approveActionSchema = z10.object({
|
|
1170
|
-
actionId: z10.string().describe("The
|
|
1171
|
-
editedReply: z10.string().optional().describe("Optionally modify the reply text before approving")
|
|
1304
|
+
actionId: z10.string().describe("The pending action ID to approve")
|
|
1172
1305
|
});
|
|
1173
1306
|
var rejectActionSchema = z10.object({
|
|
1174
1307
|
actionId: z10.string().describe("The draft reply ID to reject/delete")
|
|
@@ -1178,10 +1311,16 @@ async function listPendingActions(input, client) {
|
|
|
1178
1311
|
const params = {
|
|
1179
1312
|
limit: input.limit
|
|
1180
1313
|
};
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1314
|
+
const response = await client.get(
|
|
1315
|
+
"/api/pending-actions",
|
|
1316
|
+
params
|
|
1317
|
+
);
|
|
1318
|
+
let actions = response.data ?? response.actions ?? [];
|
|
1319
|
+
if (input.assetId) {
|
|
1320
|
+
actions = actions.filter(
|
|
1321
|
+
(action) => action.assetId === input.assetId
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1185
1324
|
return {
|
|
1186
1325
|
content: [
|
|
1187
1326
|
{
|
|
@@ -1207,10 +1346,10 @@ async function listPendingActions(input, client) {
|
|
|
1207
1346
|
}
|
|
1208
1347
|
async function approveAction(input, client) {
|
|
1209
1348
|
try {
|
|
1210
|
-
const
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1349
|
+
const response = await client.post(
|
|
1350
|
+
`/api/pending-actions/${input.actionId}/approve`,
|
|
1351
|
+
{}
|
|
1352
|
+
);
|
|
1214
1353
|
return {
|
|
1215
1354
|
content: [
|
|
1216
1355
|
{
|
|
@@ -1220,7 +1359,7 @@ async function approveAction(input, client) {
|
|
|
1220
1359
|
actionId: input.actionId,
|
|
1221
1360
|
status: "approved",
|
|
1222
1361
|
message: "Reply has been approved and queued for sending",
|
|
1223
|
-
...response.data
|
|
1362
|
+
...asRecord2(response.data)
|
|
1224
1363
|
})
|
|
1225
1364
|
}
|
|
1226
1365
|
]
|
|
@@ -1239,7 +1378,9 @@ async function approveAction(input, client) {
|
|
|
1239
1378
|
}
|
|
1240
1379
|
async function rejectAction(input, client) {
|
|
1241
1380
|
try {
|
|
1242
|
-
const response = await client.post(
|
|
1381
|
+
const response = await client.post(
|
|
1382
|
+
`/api/pending-actions/${input.actionId}/reject`
|
|
1383
|
+
);
|
|
1243
1384
|
return {
|
|
1244
1385
|
content: [
|
|
1245
1386
|
{
|
|
@@ -1249,7 +1390,7 @@ async function rejectAction(input, client) {
|
|
|
1249
1390
|
actionId: input.actionId,
|
|
1250
1391
|
status: "rejected",
|
|
1251
1392
|
message: "Draft reply has been rejected and removed",
|
|
1252
|
-
...response.data
|
|
1393
|
+
...asRecord2(response.data)
|
|
1253
1394
|
})
|
|
1254
1395
|
}
|
|
1255
1396
|
]
|
|
@@ -1283,10 +1424,13 @@ var getAsoExperimentsSchema = z11.object({
|
|
|
1283
1424
|
assetId: z11.string().uuid().describe("App UUID to list ASO experiments for"),
|
|
1284
1425
|
status: z11.enum(["proposed", "approved", "applied", "measuring", "completed", "reverted"]).optional().describe("Filter experiments by status"),
|
|
1285
1426
|
limit: z11.number().int().min(1).max(100).default(20).describe("Maximum number of experiments to return"),
|
|
1286
|
-
|
|
1427
|
+
cursor: z11.string().optional().describe("Pagination cursor from a previous response (meta.nextCursor)")
|
|
1287
1428
|
});
|
|
1288
1429
|
var getAsoLocaleSnapshotsSchema = z11.object({
|
|
1289
|
-
assetId: z11.string().uuid().describe("App UUID to get locale snapshots for")
|
|
1430
|
+
assetId: z11.string().uuid().describe("App UUID to get locale snapshots for"),
|
|
1431
|
+
store: z11.enum(["ios", "android"]).default("ios").describe("Which store listing to read"),
|
|
1432
|
+
locale: z11.string().optional().describe('Filter to a single locale (e.g. "en-US"). Returns all locales if omitted.'),
|
|
1433
|
+
limit: z11.number().int().min(1).max(500).optional().describe("Maximum snapshot rows to return (newest first)")
|
|
1290
1434
|
});
|
|
1291
1435
|
var triggerAsoAnalysisSchema = z11.object({
|
|
1292
1436
|
assetId: z11.string().uuid().describe("App UUID to trigger ASO analysis for")
|
|
@@ -1367,7 +1511,7 @@ async function getAsoExperiments(input, client) {
|
|
|
1367
1511
|
try {
|
|
1368
1512
|
const params = {
|
|
1369
1513
|
limit: input.limit,
|
|
1370
|
-
|
|
1514
|
+
cursor: input.cursor
|
|
1371
1515
|
};
|
|
1372
1516
|
if (input.status)
|
|
1373
1517
|
params.status = input.status;
|
|
@@ -1394,7 +1538,14 @@ async function getAsoExperiments(input, client) {
|
|
|
1394
1538
|
}
|
|
1395
1539
|
async function getAsoLocaleSnapshots(input, client) {
|
|
1396
1540
|
try {
|
|
1397
|
-
const response = await client.get(
|
|
1541
|
+
const response = await client.get(
|
|
1542
|
+
`/api/assets/${input.assetId}/aso/listing-snapshots`,
|
|
1543
|
+
{
|
|
1544
|
+
store: input.store,
|
|
1545
|
+
locale: input.locale,
|
|
1546
|
+
limit: input.limit
|
|
1547
|
+
}
|
|
1548
|
+
);
|
|
1398
1549
|
return {
|
|
1399
1550
|
content: [
|
|
1400
1551
|
{
|
|
@@ -1417,7 +1568,10 @@ async function getAsoLocaleSnapshots(input, client) {
|
|
|
1417
1568
|
}
|
|
1418
1569
|
async function triggerAsoAnalysis(input, client) {
|
|
1419
1570
|
try {
|
|
1420
|
-
const response = await client.post(
|
|
1571
|
+
const response = await client.post(
|
|
1572
|
+
`/api/assets/${input.assetId}/aso/refetch`,
|
|
1573
|
+
{ force: true }
|
|
1574
|
+
);
|
|
1421
1575
|
return {
|
|
1422
1576
|
content: [
|
|
1423
1577
|
{
|
|
@@ -1440,6 +1594,7 @@ async function triggerAsoAnalysis(input, client) {
|
|
|
1440
1594
|
}
|
|
1441
1595
|
|
|
1442
1596
|
// src/tools/chat.ts
|
|
1597
|
+
import { randomUUID } from "node:crypto";
|
|
1443
1598
|
import { z as z12 } from "zod";
|
|
1444
1599
|
var listConversationsSchema = z12.object({
|
|
1445
1600
|
limit: z12.number().int().min(1).max(100).default(20).describe("Maximum number of conversations to return")
|
|
@@ -1509,23 +1664,81 @@ async function getConversationMessages(input, client) {
|
|
|
1509
1664
|
};
|
|
1510
1665
|
}
|
|
1511
1666
|
}
|
|
1667
|
+
function parseUiMessageStream(raw) {
|
|
1668
|
+
let reply = "";
|
|
1669
|
+
const toolsUsed = [];
|
|
1670
|
+
for (const line of raw.split("\n")) {
|
|
1671
|
+
if (!line.startsWith("data:"))
|
|
1672
|
+
continue;
|
|
1673
|
+
const payload = line.slice(5).trim();
|
|
1674
|
+
if (!payload || payload === "[DONE]")
|
|
1675
|
+
continue;
|
|
1676
|
+
try {
|
|
1677
|
+
const event = JSON.parse(payload);
|
|
1678
|
+
if (event.type === "text-delta") {
|
|
1679
|
+
reply += String(event.delta ?? event.textDelta ?? "");
|
|
1680
|
+
} else if (typeof event.type === "string" && event.type === "tool-input-start" && typeof event.toolName === "string") {
|
|
1681
|
+
toolsUsed.push(event.toolName);
|
|
1682
|
+
}
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
return { reply, toolsUsed };
|
|
1687
|
+
}
|
|
1512
1688
|
async function sendChatMessage(input, client) {
|
|
1513
1689
|
try {
|
|
1690
|
+
const conversationId = input.conversationId ?? randomUUID();
|
|
1691
|
+
let priorMessages = [];
|
|
1692
|
+
if (input.conversationId) {
|
|
1693
|
+
const history = await client.get(`/api/chat/messages/${input.conversationId}`, { limit: 1e3 });
|
|
1694
|
+
for (const message of history.data ?? []) {
|
|
1695
|
+
const textParts = (message.parts ?? []).filter(
|
|
1696
|
+
(part) => part.type === "text" && typeof part.text === "string"
|
|
1697
|
+
);
|
|
1698
|
+
if (textParts.length === 0)
|
|
1699
|
+
continue;
|
|
1700
|
+
priorMessages.push({
|
|
1701
|
+
id: message.id,
|
|
1702
|
+
role: message.role,
|
|
1703
|
+
parts: textParts
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
priorMessages = priorMessages.slice(-100);
|
|
1707
|
+
}
|
|
1514
1708
|
const body = {
|
|
1515
|
-
|
|
1709
|
+
id: conversationId,
|
|
1710
|
+
messages: [
|
|
1711
|
+
...priorMessages,
|
|
1712
|
+
{
|
|
1713
|
+
id: randomUUID(),
|
|
1714
|
+
role: "user",
|
|
1715
|
+
parts: [{ type: "text", text: input.message }]
|
|
1716
|
+
}
|
|
1717
|
+
]
|
|
1516
1718
|
};
|
|
1517
|
-
if (input.
|
|
1518
|
-
body.
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1719
|
+
if (input.agentType) {
|
|
1720
|
+
body.agentContext = {
|
|
1721
|
+
agentId: null,
|
|
1722
|
+
agentType: input.agentType,
|
|
1723
|
+
agentName: input.agentType,
|
|
1724
|
+
surface: "mcp"
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
const raw = await client.postStream("/api/chat", body);
|
|
1728
|
+
const { reply, toolsUsed } = parseUiMessageStream(raw);
|
|
1522
1729
|
return {
|
|
1523
1730
|
content: [
|
|
1524
1731
|
{
|
|
1525
1732
|
type: "text",
|
|
1526
|
-
text: formatAsJson(
|
|
1733
|
+
text: formatAsJson({
|
|
1734
|
+
conversationId,
|
|
1735
|
+
reply: reply || "The assistant returned no text response.",
|
|
1736
|
+
toolsUsed,
|
|
1737
|
+
tip: "Pass this conversationId to continue the conversation or to get_conversation_messages for the full transcript."
|
|
1738
|
+
})
|
|
1527
1739
|
}
|
|
1528
|
-
]
|
|
1740
|
+
],
|
|
1741
|
+
...reply ? {} : { isError: true }
|
|
1529
1742
|
};
|
|
1530
1743
|
} catch (error) {
|
|
1531
1744
|
return {
|
|
@@ -1543,8 +1756,8 @@ async function sendChatMessage(input, client) {
|
|
|
1543
1756
|
// src/tools/index.ts
|
|
1544
1757
|
function registerTools(server, client, options = {}) {
|
|
1545
1758
|
const rateLimiter = options.rateLimiter ?? new RateLimiter(100, 6e4);
|
|
1546
|
-
function wrapTool(
|
|
1547
|
-
|
|
1759
|
+
function wrapTool(handler) {
|
|
1760
|
+
const callback = async (input) => {
|
|
1548
1761
|
const rateCheck = rateLimiter.check("default");
|
|
1549
1762
|
if (!rateCheck.allowed) {
|
|
1550
1763
|
return {
|
|
@@ -1561,6 +1774,7 @@ function registerTools(server, client, options = {}) {
|
|
|
1561
1774
|
}
|
|
1562
1775
|
return handler(input, client);
|
|
1563
1776
|
};
|
|
1777
|
+
return callback;
|
|
1564
1778
|
}
|
|
1565
1779
|
function tool(name, title, description, schema, handler, annotations) {
|
|
1566
1780
|
server.registerTool(
|
|
@@ -1571,8 +1785,7 @@ function registerTools(server, client, options = {}) {
|
|
|
1571
1785
|
inputSchema: schema.shape,
|
|
1572
1786
|
annotations: { title, ...annotations }
|
|
1573
1787
|
},
|
|
1574
|
-
|
|
1575
|
-
wrapTool(name, handler)
|
|
1788
|
+
wrapTool(handler)
|
|
1576
1789
|
);
|
|
1577
1790
|
}
|
|
1578
1791
|
tool(
|
|
@@ -1618,7 +1831,7 @@ function registerTools(server, client, options = {}) {
|
|
|
1618
1831
|
tool(
|
|
1619
1832
|
"translate_review",
|
|
1620
1833
|
"Translate review",
|
|
1621
|
-
"Translate a review to English. Useful for reviews written in other languages. Returns the translated text. Does not modify the review in Fload.",
|
|
1834
|
+
"Translate a review to English. Useful for reviews written in other languages. Pass the review content fields (rating, title, body, storeFront, appVersionString) from a get_reviews result. Returns the translated text. Does not modify the review in Fload.",
|
|
1622
1835
|
translateReviewSchema,
|
|
1623
1836
|
translateReview,
|
|
1624
1837
|
{ readOnlyHint: true, openWorldHint: true }
|
|
@@ -1762,7 +1975,7 @@ function registerTools(server, client, options = {}) {
|
|
|
1762
1975
|
tool(
|
|
1763
1976
|
"get_forecasts",
|
|
1764
1977
|
"Get forecasts",
|
|
1765
|
-
"Get
|
|
1978
|
+
"Get a statistical forecast for an app metric (proceeds, total_revenue, total_downloads, active_subs, or new_trials). Returns weekly historical values plus a forward projection with confidence bounds.",
|
|
1766
1979
|
getForecastsSchema,
|
|
1767
1980
|
getForecasts,
|
|
1768
1981
|
{ readOnlyHint: true, openWorldHint: false }
|
|
@@ -1931,6 +2144,7 @@ export {
|
|
|
1931
2144
|
listConversationsSchema,
|
|
1932
2145
|
listPendingActions,
|
|
1933
2146
|
listPendingActionsSchema,
|
|
2147
|
+
parseUiMessageStream,
|
|
1934
2148
|
pauseAgent,
|
|
1935
2149
|
pauseAgentSchema,
|
|
1936
2150
|
registerTools,
|