@fload-ai/mcp 0.1.1 → 0.2.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 (51) hide show
  1. package/README.md +79 -46
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/api-client.d.ts +11 -0
  4. package/dist/api-client.d.ts.map +1 -0
  5. package/dist/api-client.js +53 -0
  6. package/dist/api-client.js.map +7 -0
  7. package/dist/bin.d.ts +2 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +2012 -0
  10. package/dist/bin.js.map +7 -0
  11. package/dist/config.d.ts +8 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/index.d.ts +6 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +933 -247
  16. package/dist/index.js.map +4 -4
  17. package/dist/lib/format.d.ts +5 -0
  18. package/dist/lib/format.d.ts.map +1 -0
  19. package/dist/rate-limiter.d.ts +12 -0
  20. package/dist/rate-limiter.d.ts.map +1 -0
  21. package/dist/server.d.ts +5 -0
  22. package/dist/server.d.ts.map +1 -0
  23. package/dist/tools/actions.d.ts +69 -0
  24. package/dist/tools/actions.d.ts.map +1 -0
  25. package/dist/tools/ads.d.ts +35 -0
  26. package/dist/tools/ads.d.ts.map +1 -0
  27. package/dist/tools/agents.d.ts +149 -0
  28. package/dist/tools/agents.d.ts.map +1 -0
  29. package/dist/tools/analytics.d.ts +84 -0
  30. package/dist/tools/analytics.d.ts.map +1 -0
  31. package/dist/tools/anomalies.d.ts +107 -0
  32. package/dist/tools/anomalies.d.ts.map +1 -0
  33. package/dist/tools/apps.d.ts +49 -0
  34. package/dist/tools/apps.d.ts.map +1 -0
  35. package/dist/tools/aso.d.ts +135 -0
  36. package/dist/tools/aso.d.ts.map +1 -0
  37. package/dist/tools/chat.d.ts +69 -0
  38. package/dist/tools/chat.d.ts.map +1 -0
  39. package/dist/tools/dashboard.d.ts +17 -0
  40. package/dist/tools/dashboard.d.ts.map +1 -0
  41. package/dist/tools/forecasting.d.ts +26 -0
  42. package/dist/tools/forecasting.d.ts.map +1 -0
  43. package/dist/tools/growth.d.ts +43 -0
  44. package/dist/tools/growth.d.ts.map +1 -0
  45. package/dist/tools/index.d.ts +20 -0
  46. package/dist/tools/index.d.ts.map +1 -0
  47. package/dist/tools/index.js +1952 -0
  48. package/dist/tools/index.js.map +7 -0
  49. package/dist/tools/reviews.d.ts +119 -0
  50. package/dist/tools/reviews.d.ts.map +1 -0
  51. package/package.json +18 -3
@@ -0,0 +1,1952 @@
1
+ // src/rate-limiter.ts
2
+ var RateLimiter = class {
3
+ constructor(maxCalls = 100, windowMs = 6e4) {
4
+ this.maxCalls = maxCalls;
5
+ this.windowMs = windowMs;
6
+ }
7
+ calls = /* @__PURE__ */ new Map();
8
+ check(key) {
9
+ const now = Date.now();
10
+ const windowStart = now - this.windowMs;
11
+ let timestamps = this.calls.get(key) || [];
12
+ timestamps = timestamps.filter((t) => t > windowStart);
13
+ if (timestamps.length >= this.maxCalls) {
14
+ const oldestInWindow = timestamps[0];
15
+ const retryAfterMs = oldestInWindow + this.windowMs - now;
16
+ this.calls.set(key, timestamps);
17
+ return { allowed: false, remaining: 0, retryAfterMs };
18
+ }
19
+ timestamps.push(now);
20
+ this.calls.set(key, timestamps);
21
+ return {
22
+ allowed: true,
23
+ remaining: this.maxCalls - timestamps.length,
24
+ retryAfterMs: 0
25
+ };
26
+ }
27
+ };
28
+
29
+ // src/tools/apps.ts
30
+ import { z } from "zod";
31
+
32
+ // src/lib/format.ts
33
+ function formatAsJson(data) {
34
+ return JSON.stringify(data, null, 2);
35
+ }
36
+ function formatError(error) {
37
+ if (error instanceof Error) {
38
+ return `Error: ${error.message}`;
39
+ }
40
+ return `Unknown error: ${String(error)}`;
41
+ }
42
+
43
+ // src/tools/apps.ts
44
+ var listAppsSchema = z.object({
45
+ platform: z.enum(["ios", "android"]).optional().describe("Filter by platform (ios or android)"),
46
+ limit: z.number().int().min(1).max(100).default(50).describe("Maximum number of apps to return")
47
+ });
48
+ var getAppDetailsSchema = z.object({
49
+ assetId: z.string().uuid().optional().describe("App UUID from Fload database"),
50
+ bundleId: z.string().optional().describe("App bundle ID (e.g., com.example.app)")
51
+ });
52
+ async function listApps(input, client) {
53
+ try {
54
+ const response = await client.get("/api/assets", {
55
+ limit: input.limit,
56
+ offset: 0
57
+ });
58
+ const apps = response.data || [];
59
+ const filtered = input.platform ? apps.filter((app) => {
60
+ if (input.platform === "ios")
61
+ return !!app.appleAppId;
62
+ if (input.platform === "android")
63
+ return !!app.googleAppId;
64
+ return true;
65
+ }) : apps;
66
+ return {
67
+ content: [
68
+ {
69
+ type: "text",
70
+ text: formatAsJson({
71
+ total: filtered.length,
72
+ apps: filtered.map((app) => ({
73
+ id: app.id,
74
+ name: app.name,
75
+ bundleId: app.bundleId,
76
+ platform: app.appleAppId ? "ios" : app.googleAppId ? "android" : "unknown",
77
+ addedAt: app.updatedAt || app.createdAt
78
+ }))
79
+ })
80
+ }
81
+ ]
82
+ };
83
+ } catch (error) {
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: formatError(error)
89
+ }
90
+ ],
91
+ isError: true
92
+ };
93
+ }
94
+ }
95
+ async function getAppDetails(input, client) {
96
+ try {
97
+ if (!input.assetId && !input.bundleId) {
98
+ throw new Error("Either assetId or bundleId must be provided");
99
+ }
100
+ let assetId = input.assetId;
101
+ if (!assetId && input.bundleId) {
102
+ const allApps = await client.get("/api/assets", { limit: 100, offset: 0 });
103
+ const match = (allApps.data || []).find((a) => a.bundleId === input.bundleId);
104
+ if (!match) {
105
+ return {
106
+ content: [
107
+ {
108
+ type: "text",
109
+ text: "App not found with that bundle ID"
110
+ }
111
+ ],
112
+ isError: true
113
+ };
114
+ }
115
+ assetId = match.id;
116
+ }
117
+ const response = await client.get(`/api/assets/${assetId}`);
118
+ const app = response.data || response;
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: formatAsJson({
124
+ id: app.id,
125
+ name: app.name,
126
+ bundleId: app.bundleId,
127
+ appleAppId: app.appleAppId,
128
+ googleAppId: app.googleAppId,
129
+ platform: app.appleAppId ? "ios" : app.googleAppId ? "android" : "unknown",
130
+ currentValuation: app.currentValuation,
131
+ metadata: app.metadata,
132
+ addedAt: app.createdAt,
133
+ updatedAt: app.updatedAt
134
+ })
135
+ }
136
+ ]
137
+ };
138
+ } catch (error) {
139
+ return {
140
+ content: [
141
+ {
142
+ type: "text",
143
+ text: formatError(error)
144
+ }
145
+ ],
146
+ isError: true
147
+ };
148
+ }
149
+ }
150
+
151
+ // src/tools/reviews.ts
152
+ import { z as z2 } from "zod";
153
+ var getReviewsSchema = z2.object({
154
+ assetId: z2.string().uuid().optional().describe("App UUID to filter reviews"),
155
+ bundleId: z2.string().optional().describe("App bundle ID to filter reviews"),
156
+ platform: z2.enum(["ios", "android"]).optional().describe("Filter by platform"),
157
+ rating: z2.number().int().min(1).max(5).optional().describe("Filter by star rating (1-5)"),
158
+ replied: z2.boolean().optional().describe("Filter by replied status (true = has reply, false = no reply)"),
159
+ startDate: z2.string().optional().describe("Filter reviews from this date (ISO format: YYYY-MM-DD)"),
160
+ endDate: z2.string().optional().describe("Filter reviews until this date (ISO format: YYYY-MM-DD)"),
161
+ limit: z2.number().int().min(1).max(200).default(50).describe("Maximum number of reviews to return"),
162
+ sortBy: z2.enum(["date", "rating"]).default("date").describe("Sort reviews by date or rating")
163
+ });
164
+ var generateReviewReplySchema = z2.object({
165
+ reviewId: z2.string().describe("The review UUID to generate a reply for"),
166
+ assetId: z2.string().uuid().describe("The app UUID the review belongs to")
167
+ });
168
+ var sendReviewReplySchema = z2.object({
169
+ reviewId: z2.string().describe("The review UUID to reply to"),
170
+ assetId: z2.string().uuid().describe("The app UUID the review belongs to"),
171
+ response: z2.string().describe("The reply text to send")
172
+ });
173
+ var translateReviewSchema = z2.object({
174
+ reviewId: z2.string().describe("The review UUID to translate"),
175
+ assetId: z2.string().uuid().describe("The app UUID the review belongs to")
176
+ });
177
+ async function getReviews(input, client) {
178
+ try {
179
+ let assetId = input.assetId;
180
+ if (!assetId && input.bundleId) {
181
+ const allApps = await client.get("/api/assets", { limit: 100, offset: 0 });
182
+ const match = (allApps.data || []).find((a) => a.bundleId === input.bundleId);
183
+ if (!match) {
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: `No app found with bundle ID: ${input.bundleId}`
189
+ }
190
+ ],
191
+ isError: true
192
+ };
193
+ }
194
+ assetId = match.id;
195
+ }
196
+ const params = {
197
+ limit: input.limit,
198
+ sortBy: input.sortBy
199
+ };
200
+ if (assetId)
201
+ params.assetId = assetId;
202
+ if (input.platform)
203
+ params.platform = input.platform;
204
+ if (input.rating !== void 0)
205
+ params.rating = input.rating;
206
+ if (input.replied !== void 0)
207
+ params.responseStatus = input.replied ? "replied" : "unreplied";
208
+ if (input.startDate)
209
+ params.startDate = input.startDate;
210
+ if (input.endDate)
211
+ params.endDate = input.endDate;
212
+ const response = await client.get("/api/reviews", params);
213
+ const reviews = response.data || response.reviews || [];
214
+ const summary = {
215
+ totalReviews: reviews.length,
216
+ averageRating: reviews.length > 0 ? (reviews.reduce((sum, r) => sum + (r.rating || 0), 0) / reviews.length).toFixed(2) : 0,
217
+ repliedCount: reviews.filter((r) => r.developerResponse || r.hasReply).length,
218
+ unrepliedCount: reviews.filter((r) => !r.developerResponse && !r.hasReply).length
219
+ };
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: formatAsJson({
225
+ summary,
226
+ reviews: reviews.map((r) => ({
227
+ id: r.id,
228
+ appId: r.appId,
229
+ platform: r.platform,
230
+ rating: r.rating,
231
+ title: r.title,
232
+ body: r.body,
233
+ author: r.nickname || r.author,
234
+ date: r.lastModified || r.date,
235
+ version: r.appVersionString || r.version,
236
+ storeFront: r.storeFront,
237
+ hasReply: !!(r.developerResponse || r.hasReply),
238
+ reply: r.developerResponse ? typeof r.developerResponse === "object" ? r.developerResponse.response : r.developerResponse : r.reply || null
239
+ }))
240
+ })
241
+ }
242
+ ]
243
+ };
244
+ } catch (error) {
245
+ return {
246
+ content: [
247
+ {
248
+ type: "text",
249
+ text: formatError(error)
250
+ }
251
+ ],
252
+ isError: true
253
+ };
254
+ }
255
+ }
256
+ async function generateReviewReply(input, client) {
257
+ try {
258
+ const response = await client.post(`/api/reviews/${input.reviewId}/generate-reply`, {
259
+ assetId: input.assetId
260
+ });
261
+ return {
262
+ content: [
263
+ {
264
+ type: "text",
265
+ text: formatAsJson(response)
266
+ }
267
+ ]
268
+ };
269
+ } catch (error) {
270
+ return {
271
+ content: [
272
+ {
273
+ type: "text",
274
+ text: formatError(error)
275
+ }
276
+ ],
277
+ isError: true
278
+ };
279
+ }
280
+ }
281
+ async function sendReviewReply(input, client) {
282
+ try {
283
+ const response = await client.post(`/api/reviews/${input.reviewId}/respond`, {
284
+ assetId: input.assetId,
285
+ response: input.response
286
+ });
287
+ return {
288
+ content: [
289
+ {
290
+ type: "text",
291
+ text: formatAsJson(response)
292
+ }
293
+ ]
294
+ };
295
+ } catch (error) {
296
+ return {
297
+ content: [
298
+ {
299
+ type: "text",
300
+ text: formatError(error)
301
+ }
302
+ ],
303
+ isError: true
304
+ };
305
+ }
306
+ }
307
+ async function translateReview(input, client) {
308
+ try {
309
+ const response = await client.post(`/api/reviews/${input.reviewId}/translate`, {
310
+ assetId: input.assetId
311
+ });
312
+ return {
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text: formatAsJson(response)
317
+ }
318
+ ]
319
+ };
320
+ } catch (error) {
321
+ return {
322
+ content: [
323
+ {
324
+ type: "text",
325
+ text: formatError(error)
326
+ }
327
+ ],
328
+ isError: true
329
+ };
330
+ }
331
+ }
332
+
333
+ // src/tools/analytics.ts
334
+ import { z as z3 } from "zod";
335
+ var discoverMetricsSchema = z3.object({
336
+ assetId: z3.string().uuid().describe("App UUID to check available metrics for")
337
+ });
338
+ var getMetricsSchema = z3.object({
339
+ assetId: z3.string().uuid().describe("App UUID"),
340
+ metrics: z3.array(z3.string()).min(1).describe('Metric names to query (e.g., ["proceeds", "totalDownloads"]). Use discover_metrics first to see available metrics.'),
341
+ startDate: z3.string().optional().describe("Start date (YYYY-MM-DD). Defaults to 30 days ago."),
342
+ endDate: z3.string().optional().describe("End date (YYYY-MM-DD). Defaults to today."),
343
+ granularity: z3.enum(["daily", "weekly", "monthly"]).default("daily").describe("Data granularity"),
344
+ dimension: z3.string().optional().describe('Optional dimension to break down by (e.g., "storefront" for country, "platform" for device). Use discover_dimensions to see options.'),
345
+ dimensionFilter: z3.string().optional().describe('Filter to a specific dimension value (e.g., "US" when dimension is "storefront")')
346
+ });
347
+ var discoverDimensionsSchema = z3.object({
348
+ assetId: z3.string().uuid().describe("App UUID"),
349
+ dimension: z3.string().optional().describe('If provided, returns the available values for this dimension (e.g., countries for "storefront")')
350
+ });
351
+ var METRIC_INFO = {
352
+ proceeds: { displayName: "Net Revenue (after store cut)", category: "Revenue", type: "currency" },
353
+ total_revenue: { displayName: "Gross Revenue", category: "Revenue", type: "currency" },
354
+ total_downloads: { displayName: "Total Downloads", category: "Downloads", type: "count" },
355
+ units: { displayName: "First-time Downloads", category: "Downloads", type: "count" },
356
+ redownloads: { displayName: "Re-downloads", category: "Downloads", type: "count" },
357
+ page_views: { displayName: "App Store Page Views", category: "Downloads", type: "count" },
358
+ impressions: { displayName: "App Store Impressions", category: "Downloads", type: "count" },
359
+ sessions: { displayName: "App Sessions", category: "Engagement", type: "count" },
360
+ active_devices: { displayName: "Active Devices", category: "Engagement", type: "count" },
361
+ crashes: { displayName: "Crashes", category: "Engagement", type: "count" },
362
+ paying_users: { displayName: "Paying Users", category: "Engagement", type: "count" },
363
+ active_subs: { displayName: "Active Subscriptions", category: "Subscriptions", type: "count" },
364
+ active_trials: { displayName: "Active Trials", category: "Subscriptions", type: "count" },
365
+ new_trials: { displayName: "New Trials", category: "Subscriptions", type: "count" },
366
+ trial_conversion_rate: { displayName: "Trial Conversion Rate", category: "Subscriptions", type: "percentage" },
367
+ subscription_retention_rate: { displayName: "Subscription Retention Rate", category: "Subscriptions", type: "percentage" },
368
+ products_sold: { displayName: "Products Sold (IAP)", category: "Revenue", type: "count" },
369
+ ad_spend: { displayName: "Ad Spend", category: "Ads", type: "currency" },
370
+ ad_impressions: { displayName: "Ad Impressions", category: "Ads", type: "count" },
371
+ ad_taps: { displayName: "Ad Taps/Clicks", category: "Ads", type: "count" },
372
+ ad_installs: { displayName: "Ad-attributed Installs", category: "Ads", type: "count" },
373
+ ad_conversions: { displayName: "Ad Conversions", category: "Ads", type: "count" }
374
+ };
375
+ var DIMENSION_INFO = {
376
+ storefront: "Country",
377
+ region: "Region",
378
+ platform: "Platform (device type)",
379
+ source: "Source",
380
+ appVersion: "App Version",
381
+ purchase: "Product",
382
+ appReferrer: "App Referrer",
383
+ domainReferrer: "Domain Referrer",
384
+ subscription_state: "Subscription Type",
385
+ duration: "Duration",
386
+ revenueShare: "Revenue Share",
387
+ campaign: "Campaign"
388
+ };
389
+ async function discoverMetrics(input, client) {
390
+ try {
391
+ const response = await client.get(`/api/assets/${input.assetId}/metrics/availability`);
392
+ const availableMetrics = response.data?.availableMetrics || [];
393
+ const metricGroups = response.data?.metricGroups || {};
394
+ const enriched = availableMetrics.map((metricName) => {
395
+ const info = METRIC_INFO[metricName];
396
+ return {
397
+ name: metricName,
398
+ displayName: info?.displayName || metricName,
399
+ category: info?.category || "Other",
400
+ type: info?.type || "count"
401
+ };
402
+ });
403
+ const byCategory = {};
404
+ for (const m of enriched) {
405
+ if (!byCategory[m.category])
406
+ byCategory[m.category] = [];
407
+ byCategory[m.category].push(m);
408
+ }
409
+ return {
410
+ content: [{
411
+ type: "text",
412
+ text: formatAsJson({
413
+ assetId: input.assetId,
414
+ totalAvailable: availableMetrics.length,
415
+ metricsByCategory: byCategory,
416
+ dataGroups: metricGroups,
417
+ tip: 'Use the metric "name" field in get_metrics. You can query multiple metrics at once.'
418
+ })
419
+ }]
420
+ };
421
+ } catch (error) {
422
+ return { content: [{ type: "text", text: formatError(error) }], isError: true };
423
+ }
424
+ }
425
+ async function getMetrics(input, client) {
426
+ try {
427
+ const endDate = input.endDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
428
+ const startDate = input.startDate || (() => {
429
+ const d = /* @__PURE__ */ new Date();
430
+ d.setDate(d.getDate() - 30);
431
+ return d.toISOString().split("T")[0];
432
+ })();
433
+ const params = {
434
+ metrics: input.metrics.join(","),
435
+ fromDate: startDate,
436
+ toDate: endDate,
437
+ granularity: input.granularity
438
+ };
439
+ if (input.dimension)
440
+ params.dimension = input.dimension;
441
+ if (input.dimensionFilter && input.dimension) {
442
+ params[`filter.${input.dimension}`] = input.dimensionFilter;
443
+ }
444
+ const response = await client.get(`/api/assets/${input.assetId}/metrics/timeseries`, params);
445
+ const data = response.data || {};
446
+ const changes = response.changes || {};
447
+ const meta = response.meta || {};
448
+ const summaries = {};
449
+ for (const [metricName, timeseries] of Object.entries(data)) {
450
+ const points = timeseries;
451
+ if (!Array.isArray(points) || points.length === 0) {
452
+ summaries[metricName] = { total: 0, average: 0, min: 0, max: 0, dataPoints: 0 };
453
+ continue;
454
+ }
455
+ const values = points.map((p) => p.value);
456
+ const total = values.reduce((s, v) => s + v, 0);
457
+ summaries[metricName] = {
458
+ total: Math.round(total * 100) / 100,
459
+ average: Math.round(total / values.length * 100) / 100,
460
+ min: Math.min(...values),
461
+ max: Math.max(...values),
462
+ dataPoints: values.length
463
+ };
464
+ }
465
+ const info = METRIC_INFO;
466
+ return {
467
+ content: [{
468
+ type: "text",
469
+ text: formatAsJson({
470
+ assetId: input.assetId,
471
+ dateRange: { start: startDate, end: endDate },
472
+ granularity: input.granularity,
473
+ dimension: input.dimension || null,
474
+ metrics: Object.keys(data).map((name) => ({
475
+ name,
476
+ displayName: info[name]?.displayName || name,
477
+ type: info[name]?.type || "count",
478
+ summary: summaries[name],
479
+ change: changes[name] || null
480
+ })),
481
+ timeseries: data,
482
+ meta
483
+ })
484
+ }]
485
+ };
486
+ } catch (error) {
487
+ return { content: [{ type: "text", text: formatError(error) }], isError: true };
488
+ }
489
+ }
490
+ async function discoverDimensions(input, client) {
491
+ try {
492
+ if (input.dimension) {
493
+ const response2 = await client.get(
494
+ `/api/assets/${input.assetId}/metrics/dimensions/${input.dimension}/values`
495
+ );
496
+ return {
497
+ content: [{
498
+ type: "text",
499
+ text: formatAsJson({
500
+ assetId: input.assetId,
501
+ dimension: input.dimension,
502
+ displayName: DIMENSION_INFO[input.dimension] || input.dimension,
503
+ values: response2.data || response2
504
+ })
505
+ }]
506
+ };
507
+ }
508
+ const response = await client.get(`/api/assets/${input.assetId}/metrics/dimensions`);
509
+ const dimensions = response.data || response || [];
510
+ const enriched = (Array.isArray(dimensions) ? dimensions : Object.keys(dimensions)).map((dim) => ({
511
+ name: dim,
512
+ displayName: DIMENSION_INFO[dim] || dim
513
+ }));
514
+ return {
515
+ content: [{
516
+ type: "text",
517
+ text: formatAsJson({
518
+ assetId: input.assetId,
519
+ dimensions: enriched,
520
+ tip: 'Use dimension name in get_metrics "dimension" param. Use discover_dimensions with a specific dimension to see available values.'
521
+ })
522
+ }]
523
+ };
524
+ } catch (error) {
525
+ return { content: [{ type: "text", text: formatError(error) }], isError: true };
526
+ }
527
+ }
528
+
529
+ // src/tools/agents.ts
530
+ import { z as z4 } from "zod";
531
+ var AGENT_TYPES = [
532
+ "review",
533
+ "monitoring",
534
+ "forecasting",
535
+ "growth",
536
+ "aso",
537
+ "ads",
538
+ "product",
539
+ "submission_review"
540
+ ];
541
+ var AGENT_DESCRIPTIONS = {
542
+ review: "AI-powered review management \u2014 drafts and sends replies to app store reviews",
543
+ monitoring: "Anomaly detection \u2014 monitors metrics for unusual changes in downloads, revenue, etc.",
544
+ forecasting: "Revenue and metric forecasting \u2014 generates forward-looking projections",
545
+ growth: "Growth audit \u2014 analyzes app performance and provides growth scoring",
546
+ aso: "App Store Optimization \u2014 listing optimization suggestions for better visibility",
547
+ ads: "Ad campaign analysis \u2014 performance tracking and optimization recommendations",
548
+ product: "Product agent \u2014 automated app installation testing via BrowserStack",
549
+ submission_review: "Submission review \u2014 GitHub-based review workflow for app submissions"
550
+ };
551
+ var listAgentsSchema = z4.object({});
552
+ var getAgentDetailsSchema = z4.object({
553
+ agentType: z4.enum(AGENT_TYPES).describe("The type of agent to get details for"),
554
+ assetId: z4.string().uuid().optional().describe("App UUID to get agent config for (when agent is per-asset)")
555
+ });
556
+ var getAgentRunHistorySchema = z4.object({
557
+ agentType: z4.enum(AGENT_TYPES).describe("The type of agent to get run history for"),
558
+ assetId: z4.string().uuid().optional().describe("App UUID to filter runs"),
559
+ limit: z4.number().int().min(1).max(100).default(20).describe("Maximum number of runs to return")
560
+ });
561
+ var triggerAgentRunSchema = z4.object({
562
+ agentId: z4.string().describe("The agent ID to trigger a run for"),
563
+ assetId: z4.string().uuid().optional().describe("Optional app UUID to run the agent against")
564
+ });
565
+ var pauseAgentSchema = z4.object({
566
+ agentId: z4.string().describe("The agent ID to pause")
567
+ });
568
+ var resumeAgentSchema = z4.object({
569
+ agentId: z4.string().describe("The agent ID to resume")
570
+ });
571
+ var getAgentActivitySchema = z4.object({
572
+ agentId: z4.string().describe("The agent ID to get activity for")
573
+ });
574
+ async function listAgents(_input, client) {
575
+ try {
576
+ let apiAgents = null;
577
+ try {
578
+ const response = await client.get("/api/agents");
579
+ apiAgents = response.data || response.agents || response;
580
+ } catch {
581
+ }
582
+ if (apiAgents && Array.isArray(apiAgents)) {
583
+ return {
584
+ content: [
585
+ {
586
+ type: "text",
587
+ text: formatAsJson({
588
+ totalAgents: apiAgents.length,
589
+ agents: apiAgents
590
+ })
591
+ }
592
+ ]
593
+ };
594
+ }
595
+ const agents = AGENT_TYPES.map((type) => ({
596
+ type,
597
+ description: AGENT_DESCRIPTIONS[type],
598
+ available: true
599
+ }));
600
+ return {
601
+ content: [
602
+ {
603
+ type: "text",
604
+ text: formatAsJson({
605
+ totalAgents: agents.length,
606
+ agents
607
+ })
608
+ }
609
+ ]
610
+ };
611
+ } catch (error) {
612
+ return {
613
+ content: [
614
+ {
615
+ type: "text",
616
+ text: formatError(error)
617
+ }
618
+ ],
619
+ isError: true
620
+ };
621
+ }
622
+ }
623
+ async function getAgentDetails(input, client) {
624
+ try {
625
+ try {
626
+ const params = {};
627
+ if (input.assetId)
628
+ params.assetId = input.assetId;
629
+ const response2 = await client.get(`/api/agents/${input.agentType}`, params);
630
+ return {
631
+ content: [
632
+ {
633
+ type: "text",
634
+ text: formatAsJson(response2.data || response2)
635
+ }
636
+ ]
637
+ };
638
+ } catch {
639
+ }
640
+ const response = {
641
+ type: input.agentType,
642
+ description: AGENT_DESCRIPTIONS[input.agentType],
643
+ message: "Detailed agent configuration is available through the platform dashboard."
644
+ };
645
+ return {
646
+ content: [
647
+ {
648
+ type: "text",
649
+ text: formatAsJson(response)
650
+ }
651
+ ]
652
+ };
653
+ } catch (error) {
654
+ return {
655
+ content: [
656
+ {
657
+ type: "text",
658
+ text: formatError(error)
659
+ }
660
+ ],
661
+ isError: true
662
+ };
663
+ }
664
+ }
665
+ async function getAgentRunHistory(input, client) {
666
+ try {
667
+ try {
668
+ const params = {
669
+ limit: input.limit
670
+ };
671
+ if (input.assetId)
672
+ params.assetId = input.assetId;
673
+ const response = await client.get(`/api/agents/${input.agentType}/runs`, params);
674
+ return {
675
+ content: [
676
+ {
677
+ type: "text",
678
+ text: formatAsJson({
679
+ agentType: input.agentType,
680
+ ...response.data || response
681
+ })
682
+ }
683
+ ]
684
+ };
685
+ } catch {
686
+ }
687
+ return {
688
+ content: [
689
+ {
690
+ type: "text",
691
+ text: formatAsJson({
692
+ agentType: input.agentType,
693
+ message: `Run history for ${input.agentType} agent is available through the platform dashboard. This agent type does not have a dedicated run log table yet.`
694
+ })
695
+ }
696
+ ]
697
+ };
698
+ } catch (error) {
699
+ return {
700
+ content: [
701
+ {
702
+ type: "text",
703
+ text: formatError(error)
704
+ }
705
+ ],
706
+ isError: true
707
+ };
708
+ }
709
+ }
710
+ async function triggerAgentRun(input, client) {
711
+ try {
712
+ const body = {};
713
+ if (input.assetId)
714
+ body.assetId = input.assetId;
715
+ const response = await client.post(`/api/agents/${input.agentId}/run`, body);
716
+ return {
717
+ content: [
718
+ {
719
+ type: "text",
720
+ text: formatAsJson(response.data || response)
721
+ }
722
+ ]
723
+ };
724
+ } catch (error) {
725
+ return {
726
+ content: [
727
+ {
728
+ type: "text",
729
+ text: formatError(error)
730
+ }
731
+ ],
732
+ isError: true
733
+ };
734
+ }
735
+ }
736
+ async function pauseAgent(input, client) {
737
+ try {
738
+ const response = await client.post(`/api/agents/${input.agentId}/pause`);
739
+ return {
740
+ content: [
741
+ {
742
+ type: "text",
743
+ text: formatAsJson(response.data || response)
744
+ }
745
+ ]
746
+ };
747
+ } catch (error) {
748
+ return {
749
+ content: [
750
+ {
751
+ type: "text",
752
+ text: formatError(error)
753
+ }
754
+ ],
755
+ isError: true
756
+ };
757
+ }
758
+ }
759
+ async function resumeAgent(input, client) {
760
+ try {
761
+ const response = await client.post(`/api/agents/${input.agentId}/resume`);
762
+ return {
763
+ content: [
764
+ {
765
+ type: "text",
766
+ text: formatAsJson(response.data || response)
767
+ }
768
+ ]
769
+ };
770
+ } catch (error) {
771
+ return {
772
+ content: [
773
+ {
774
+ type: "text",
775
+ text: formatError(error)
776
+ }
777
+ ],
778
+ isError: true
779
+ };
780
+ }
781
+ }
782
+ async function getAgentActivity(input, client) {
783
+ try {
784
+ const response = await client.get(`/api/agents/${input.agentId}/activity`);
785
+ return {
786
+ content: [
787
+ {
788
+ type: "text",
789
+ text: formatAsJson(response.data || response)
790
+ }
791
+ ]
792
+ };
793
+ } catch (error) {
794
+ return {
795
+ content: [
796
+ {
797
+ type: "text",
798
+ text: formatError(error)
799
+ }
800
+ ],
801
+ isError: true
802
+ };
803
+ }
804
+ }
805
+
806
+ // src/tools/anomalies.ts
807
+ import { z as z5 } from "zod";
808
+ var getAnomaliesSchema = z5.object({
809
+ assetId: z5.string().uuid().optional().describe("Filter anomalies by app UUID"),
810
+ severity: z5.enum(["low", "medium", "high", "critical"]).optional().describe("Filter by severity level"),
811
+ type: z5.enum(["surge", "decline"]).optional().describe("Filter by anomaly type"),
812
+ status: z5.enum(["new", "viewed", "acknowledged", "dismissed"]).optional().describe("Filter by status"),
813
+ metricName: z5.string().optional().describe('Filter by metric name (e.g., "proceeds", "units")'),
814
+ fromDate: z5.string().optional().describe("Filter anomalies from this date (YYYY-MM-DD)"),
815
+ toDate: z5.string().optional().describe("Filter anomalies until this date (YYYY-MM-DD)"),
816
+ excludeDismissed: z5.boolean().default(true).describe("Exclude dismissed anomalies (default: true)"),
817
+ limit: z5.number().int().min(1).max(100).default(50).describe("Maximum number of anomalies to return")
818
+ });
819
+ var getAnomalyDetailSchema = z5.object({
820
+ id: z5.string().uuid().describe("The anomaly UUID to get details for")
821
+ });
822
+ var acknowledgeAnomalySchema = z5.object({
823
+ id: z5.string().uuid().describe("The anomaly UUID to acknowledge")
824
+ });
825
+ var dismissAnomalySchema = z5.object({
826
+ id: z5.string().uuid().describe("The anomaly UUID to dismiss")
827
+ });
828
+ async function getAnomalies(input, client) {
829
+ try {
830
+ const params = {
831
+ limit: input.limit
832
+ };
833
+ if (input.assetId)
834
+ params.assetId = input.assetId;
835
+ if (input.severity)
836
+ params.severity = input.severity;
837
+ if (input.type)
838
+ params.type = input.type;
839
+ if (input.status)
840
+ params.status = input.status;
841
+ if (input.metricName)
842
+ params.metricName = input.metricName;
843
+ if (input.fromDate)
844
+ params.fromDate = input.fromDate;
845
+ if (input.toDate)
846
+ params.toDate = input.toDate;
847
+ if (input.excludeDismissed)
848
+ params.excludeDismissed = input.excludeDismissed;
849
+ const response = await client.get("/api/anomalies", params);
850
+ const anomalies = response.data || response.anomalies || [];
851
+ return {
852
+ content: [
853
+ {
854
+ type: "text",
855
+ text: formatAsJson({
856
+ total: anomalies.length,
857
+ anomalies: anomalies.map((a) => ({
858
+ id: a.id,
859
+ assetId: a.assetId,
860
+ assetName: a.assetName,
861
+ assetIcon: a.assetIcon,
862
+ anomalyDate: a.anomalyDate,
863
+ metricName: a.metricName,
864
+ sourceType: a.sourceType,
865
+ type: a.type,
866
+ severity: a.severity,
867
+ actualValue: parseFloat(a.actualValue || "0"),
868
+ expectedValue: parseFloat(a.expectedValue || "0"),
869
+ deviationPercent: parseFloat(a.deviationPercent || "0"),
870
+ confidence: parseFloat(a.confidence || "0"),
871
+ explanation: a.explanation,
872
+ suggestedActions: a.suggestedActions,
873
+ status: a.status,
874
+ detectedAt: a.detectedAt
875
+ }))
876
+ })
877
+ }
878
+ ]
879
+ };
880
+ } catch (error) {
881
+ return {
882
+ content: [
883
+ {
884
+ type: "text",
885
+ text: formatError(error)
886
+ }
887
+ ],
888
+ isError: true
889
+ };
890
+ }
891
+ }
892
+ async function getAnomalyDetail(input, client) {
893
+ try {
894
+ const response = await client.get(`/api/anomalies/${input.id}`);
895
+ return {
896
+ content: [
897
+ {
898
+ type: "text",
899
+ text: formatAsJson(response)
900
+ }
901
+ ]
902
+ };
903
+ } catch (error) {
904
+ return {
905
+ content: [
906
+ {
907
+ type: "text",
908
+ text: formatError(error)
909
+ }
910
+ ],
911
+ isError: true
912
+ };
913
+ }
914
+ }
915
+ async function acknowledgeAnomaly(input, client) {
916
+ try {
917
+ const response = await client.patch(`/api/anomalies/${input.id}/acknowledge`);
918
+ return {
919
+ content: [
920
+ {
921
+ type: "text",
922
+ text: formatAsJson(response)
923
+ }
924
+ ]
925
+ };
926
+ } catch (error) {
927
+ return {
928
+ content: [
929
+ {
930
+ type: "text",
931
+ text: formatError(error)
932
+ }
933
+ ],
934
+ isError: true
935
+ };
936
+ }
937
+ }
938
+ async function dismissAnomaly(input, client) {
939
+ try {
940
+ const response = await client.patch(`/api/anomalies/${input.id}/dismiss`);
941
+ return {
942
+ content: [
943
+ {
944
+ type: "text",
945
+ text: formatAsJson(response)
946
+ }
947
+ ]
948
+ };
949
+ } catch (error) {
950
+ return {
951
+ content: [
952
+ {
953
+ type: "text",
954
+ text: formatError(error)
955
+ }
956
+ ],
957
+ isError: true
958
+ };
959
+ }
960
+ }
961
+
962
+ // src/tools/ads.ts
963
+ import { z as z6 } from "zod";
964
+ var getAdsPerformanceSchema = z6.object({
965
+ assetId: z6.string().uuid().optional().describe("Filter by app UUID"),
966
+ platform: z6.enum(["apple_search_ads", "google_ads", "meta_ads", "tiktok_ads"]).optional().describe("Filter by ad platform"),
967
+ fromDate: z6.string().optional().describe("Start date for performance data (YYYY-MM-DD)"),
968
+ toDate: z6.string().optional().describe("End date for performance data (YYYY-MM-DD)"),
969
+ limit: z6.number().int().min(1).max(100).default(50).describe("Maximum number of campaigns to return")
970
+ });
971
+ async function getAdsPerformance(input, client) {
972
+ try {
973
+ const params = {
974
+ limit: input.limit
975
+ };
976
+ if (input.assetId)
977
+ params.assetId = input.assetId;
978
+ if (input.platform)
979
+ params.platform = input.platform;
980
+ if (input.fromDate)
981
+ params.fromDate = input.fromDate;
982
+ if (input.toDate)
983
+ params.toDate = input.toDate;
984
+ const response = await client.get("/api/ads/campaigns", params);
985
+ const campaigns = response.data || response.campaigns || [];
986
+ return {
987
+ content: [
988
+ {
989
+ type: "text",
990
+ text: formatAsJson({
991
+ total: campaigns.length,
992
+ campaigns: campaigns.map((c) => ({
993
+ id: c.id,
994
+ assetId: c.assetId,
995
+ assetName: c.assetName,
996
+ platformCampaignId: c.platformCampaignId,
997
+ name: c.name,
998
+ status: c.status,
999
+ objective: c.objective,
1000
+ platform: c.platform,
1001
+ linkSource: c.linkSource,
1002
+ linkedAt: c.linkedAt,
1003
+ recentPerformance: c.recentPerformance || null
1004
+ }))
1005
+ })
1006
+ }
1007
+ ]
1008
+ };
1009
+ } catch (error) {
1010
+ return {
1011
+ content: [
1012
+ {
1013
+ type: "text",
1014
+ text: formatError(error)
1015
+ }
1016
+ ],
1017
+ isError: true
1018
+ };
1019
+ }
1020
+ }
1021
+
1022
+ // src/tools/growth.ts
1023
+ import { z as z7 } from "zod";
1024
+ var getGrowthAuditSchema = z7.object({
1025
+ assetId: z7.string().uuid().describe("App UUID to audit")
1026
+ });
1027
+ var getGrowthScoreSchema = z7.object({
1028
+ assetId: z7.string().uuid().describe("App UUID to score")
1029
+ });
1030
+ async function getGrowthAudit(input, client) {
1031
+ try {
1032
+ const response = await client.get(`/api/growth/audit/${input.assetId}`);
1033
+ return {
1034
+ content: [
1035
+ {
1036
+ type: "text",
1037
+ text: formatAsJson(response.data || response)
1038
+ }
1039
+ ]
1040
+ };
1041
+ } catch (error) {
1042
+ return {
1043
+ content: [
1044
+ {
1045
+ type: "text",
1046
+ text: formatError(error)
1047
+ }
1048
+ ],
1049
+ isError: true
1050
+ };
1051
+ }
1052
+ }
1053
+ async function getGrowthScore(input, client) {
1054
+ try {
1055
+ const response = await client.get(`/api/growth/score/${input.assetId}`);
1056
+ return {
1057
+ content: [
1058
+ {
1059
+ type: "text",
1060
+ text: formatAsJson(response.data || response)
1061
+ }
1062
+ ]
1063
+ };
1064
+ } catch (error) {
1065
+ return {
1066
+ content: [
1067
+ {
1068
+ type: "text",
1069
+ text: formatError(error)
1070
+ }
1071
+ ],
1072
+ isError: true
1073
+ };
1074
+ }
1075
+ }
1076
+
1077
+ // src/tools/forecasting.ts
1078
+ import { z as z8 } from "zod";
1079
+ var getForecastsSchema = z8.object({
1080
+ assetId: z8.string().uuid().describe("App UUID to get forecasts for"),
1081
+ dataPoints: z8.number().int().min(4).max(52).default(12).describe("Number of historical data points to include")
1082
+ });
1083
+ async function getForecasts(input, client) {
1084
+ try {
1085
+ const response = await client.get("/api/forecasting/forecast", {
1086
+ assetId: input.assetId,
1087
+ dataPoints: input.dataPoints
1088
+ });
1089
+ return {
1090
+ content: [
1091
+ {
1092
+ type: "text",
1093
+ text: formatAsJson(response.data || response)
1094
+ }
1095
+ ]
1096
+ };
1097
+ } catch (error) {
1098
+ return {
1099
+ content: [
1100
+ {
1101
+ type: "text",
1102
+ text: formatError(error)
1103
+ }
1104
+ ],
1105
+ isError: true
1106
+ };
1107
+ }
1108
+ }
1109
+
1110
+ // src/tools/dashboard.ts
1111
+ import { z as z9 } from "zod";
1112
+ var getDashboardOverviewSchema = z9.object({});
1113
+ async function getDashboardOverview(_input, client) {
1114
+ try {
1115
+ const [overviewResponse, sidebarResponse] = await Promise.all([
1116
+ client.get("/api/dashboard/overview-metrics").catch(() => null),
1117
+ client.get("/api/dashboard/sidebar-assets").catch(() => null)
1118
+ ]);
1119
+ const overview = overviewResponse?.data || overviewResponse || {};
1120
+ const sidebarAssets = sidebarResponse?.data || sidebarResponse || [];
1121
+ const apps = Array.isArray(sidebarAssets) ? sidebarAssets : [];
1122
+ const totalValuation = apps.reduce((sum, a) => {
1123
+ return sum + parseFloat(a.currentValuation || "0");
1124
+ }, 0);
1125
+ return {
1126
+ content: [
1127
+ {
1128
+ type: "text",
1129
+ text: formatAsJson({
1130
+ portfolio: {
1131
+ totalApps: apps.length,
1132
+ totalValuation: totalValuation > 0 ? totalValuation : null,
1133
+ apps: apps.map((a) => ({
1134
+ id: a.id,
1135
+ name: a.name,
1136
+ bundleId: a.bundleId,
1137
+ platform: a.appleAppId ? "ios" : a.googleAppId ? "android" : "unknown",
1138
+ currentValuation: a.currentValuation ? parseFloat(a.currentValuation) : null,
1139
+ rating: a.rating ? parseFloat(a.rating) : null,
1140
+ ratingCount: a.ratingCount || null,
1141
+ category: a.category || null,
1142
+ iconUrl: a.iconUrl || null
1143
+ }))
1144
+ },
1145
+ overview
1146
+ })
1147
+ }
1148
+ ]
1149
+ };
1150
+ } catch (error) {
1151
+ return {
1152
+ content: [
1153
+ {
1154
+ type: "text",
1155
+ text: formatError(error)
1156
+ }
1157
+ ],
1158
+ isError: true
1159
+ };
1160
+ }
1161
+ }
1162
+
1163
+ // src/tools/actions.ts
1164
+ import { z as z10 } from "zod";
1165
+ var listPendingActionsSchema = z10.object({
1166
+ assetId: z10.string().uuid().optional().describe("Filter by app UUID"),
1167
+ limit: z10.number().int().min(1).max(100).default(50).describe("Maximum number of actions to return")
1168
+ });
1169
+ var approveActionSchema = z10.object({
1170
+ actionId: z10.string().describe("The draft reply ID to approve"),
1171
+ editedReply: z10.string().optional().describe("Optionally modify the reply text before approving")
1172
+ });
1173
+ var rejectActionSchema = z10.object({
1174
+ actionId: z10.string().describe("The draft reply ID to reject/delete")
1175
+ });
1176
+ async function listPendingActions(input, client) {
1177
+ try {
1178
+ const params = {
1179
+ limit: input.limit
1180
+ };
1181
+ if (input.assetId)
1182
+ params.assetId = input.assetId;
1183
+ const response = await client.get("/api/pending-actions", params);
1184
+ const actions = response.data || response.actions || [];
1185
+ return {
1186
+ content: [
1187
+ {
1188
+ type: "text",
1189
+ text: formatAsJson({
1190
+ total: actions.length,
1191
+ actions
1192
+ })
1193
+ }
1194
+ ]
1195
+ };
1196
+ } catch (error) {
1197
+ return {
1198
+ content: [
1199
+ {
1200
+ type: "text",
1201
+ text: formatError(error)
1202
+ }
1203
+ ],
1204
+ isError: true
1205
+ };
1206
+ }
1207
+ }
1208
+ async function approveAction(input, client) {
1209
+ try {
1210
+ const body = {};
1211
+ if (input.editedReply)
1212
+ body.editedReply = input.editedReply;
1213
+ const response = await client.post(`/api/pending-actions/${input.actionId}/approve`, body);
1214
+ return {
1215
+ content: [
1216
+ {
1217
+ type: "text",
1218
+ text: formatAsJson({
1219
+ success: true,
1220
+ actionId: input.actionId,
1221
+ status: "approved",
1222
+ message: "Reply has been approved and queued for sending",
1223
+ ...response.data || {}
1224
+ })
1225
+ }
1226
+ ]
1227
+ };
1228
+ } catch (error) {
1229
+ return {
1230
+ content: [
1231
+ {
1232
+ type: "text",
1233
+ text: formatError(error)
1234
+ }
1235
+ ],
1236
+ isError: true
1237
+ };
1238
+ }
1239
+ }
1240
+ async function rejectAction(input, client) {
1241
+ try {
1242
+ const response = await client.post(`/api/pending-actions/${input.actionId}/reject`);
1243
+ return {
1244
+ content: [
1245
+ {
1246
+ type: "text",
1247
+ text: formatAsJson({
1248
+ success: true,
1249
+ actionId: input.actionId,
1250
+ status: "rejected",
1251
+ message: "Draft reply has been rejected and removed",
1252
+ ...response.data || {}
1253
+ })
1254
+ }
1255
+ ]
1256
+ };
1257
+ } catch (error) {
1258
+ return {
1259
+ content: [
1260
+ {
1261
+ type: "text",
1262
+ text: formatError(error)
1263
+ }
1264
+ ],
1265
+ isError: true
1266
+ };
1267
+ }
1268
+ }
1269
+
1270
+ // src/tools/aso.ts
1271
+ import { z as z11 } from "zod";
1272
+ var getAsoSummarySchema = z11.object({
1273
+ assetId: z11.string().uuid().describe("App UUID to get ASO summary for")
1274
+ });
1275
+ var getAsoRecommendationsSchema = z11.object({
1276
+ assetId: z11.string().uuid().describe("App UUID to get ASO recommendations for")
1277
+ });
1278
+ var getAsoKeywordsSchema = z11.object({
1279
+ assetId: z11.string().uuid().describe("App UUID to get keyword intelligence for"),
1280
+ locale: z11.string().optional().describe('Locale code to filter keywords (e.g., "en-US", "de-DE"). Returns all locales if omitted.')
1281
+ });
1282
+ var getAsoExperimentsSchema = z11.object({
1283
+ assetId: z11.string().uuid().describe("App UUID to list ASO experiments for"),
1284
+ status: z11.enum(["proposed", "approved", "applied", "measuring", "completed", "reverted"]).optional().describe("Filter experiments by status"),
1285
+ limit: z11.number().int().min(1).max(100).default(20).describe("Maximum number of experiments to return"),
1286
+ offset: z11.number().int().min(0).default(0).describe("Number of experiments to skip for pagination")
1287
+ });
1288
+ var getAsoLocaleSnapshotsSchema = z11.object({
1289
+ assetId: z11.string().uuid().describe("App UUID to get locale snapshots for")
1290
+ });
1291
+ var triggerAsoAnalysisSchema = z11.object({
1292
+ assetId: z11.string().uuid().describe("App UUID to trigger ASO analysis for")
1293
+ });
1294
+ async function getAsoSummary(input, client) {
1295
+ try {
1296
+ const response = await client.get(`/api/assets/${input.assetId}/aso/summary`);
1297
+ return {
1298
+ content: [
1299
+ {
1300
+ type: "text",
1301
+ text: formatAsJson(response)
1302
+ }
1303
+ ]
1304
+ };
1305
+ } catch (error) {
1306
+ return {
1307
+ content: [
1308
+ {
1309
+ type: "text",
1310
+ text: formatError(error)
1311
+ }
1312
+ ],
1313
+ isError: true
1314
+ };
1315
+ }
1316
+ }
1317
+ async function getAsoRecommendations(input, client) {
1318
+ try {
1319
+ const response = await client.get(`/api/assets/${input.assetId}/aso/recommendations`);
1320
+ return {
1321
+ content: [
1322
+ {
1323
+ type: "text",
1324
+ text: formatAsJson(response)
1325
+ }
1326
+ ]
1327
+ };
1328
+ } catch (error) {
1329
+ return {
1330
+ content: [
1331
+ {
1332
+ type: "text",
1333
+ text: formatError(error)
1334
+ }
1335
+ ],
1336
+ isError: true
1337
+ };
1338
+ }
1339
+ }
1340
+ async function getAsoKeywords(input, client) {
1341
+ try {
1342
+ const params = {};
1343
+ if (input.locale)
1344
+ params.locale = input.locale;
1345
+ const response = await client.get(`/api/assets/${input.assetId}/aso/keyword-intelligence`, params);
1346
+ return {
1347
+ content: [
1348
+ {
1349
+ type: "text",
1350
+ text: formatAsJson(response)
1351
+ }
1352
+ ]
1353
+ };
1354
+ } catch (error) {
1355
+ return {
1356
+ content: [
1357
+ {
1358
+ type: "text",
1359
+ text: formatError(error)
1360
+ }
1361
+ ],
1362
+ isError: true
1363
+ };
1364
+ }
1365
+ }
1366
+ async function getAsoExperiments(input, client) {
1367
+ try {
1368
+ const params = {
1369
+ limit: input.limit,
1370
+ offset: input.offset
1371
+ };
1372
+ if (input.status)
1373
+ params.status = input.status;
1374
+ const response = await client.get(`/api/assets/${input.assetId}/aso/experiments`, params);
1375
+ return {
1376
+ content: [
1377
+ {
1378
+ type: "text",
1379
+ text: formatAsJson(response)
1380
+ }
1381
+ ]
1382
+ };
1383
+ } catch (error) {
1384
+ return {
1385
+ content: [
1386
+ {
1387
+ type: "text",
1388
+ text: formatError(error)
1389
+ }
1390
+ ],
1391
+ isError: true
1392
+ };
1393
+ }
1394
+ }
1395
+ async function getAsoLocaleSnapshots(input, client) {
1396
+ try {
1397
+ const response = await client.get(`/api/assets/${input.assetId}/aso/locale-snapshots`);
1398
+ return {
1399
+ content: [
1400
+ {
1401
+ type: "text",
1402
+ text: formatAsJson(response)
1403
+ }
1404
+ ]
1405
+ };
1406
+ } catch (error) {
1407
+ return {
1408
+ content: [
1409
+ {
1410
+ type: "text",
1411
+ text: formatError(error)
1412
+ }
1413
+ ],
1414
+ isError: true
1415
+ };
1416
+ }
1417
+ }
1418
+ async function triggerAsoAnalysis(input, client) {
1419
+ try {
1420
+ const response = await client.post(`/api/assets/${input.assetId}/aso/analyze`);
1421
+ return {
1422
+ content: [
1423
+ {
1424
+ type: "text",
1425
+ text: formatAsJson(response)
1426
+ }
1427
+ ]
1428
+ };
1429
+ } catch (error) {
1430
+ return {
1431
+ content: [
1432
+ {
1433
+ type: "text",
1434
+ text: formatError(error)
1435
+ }
1436
+ ],
1437
+ isError: true
1438
+ };
1439
+ }
1440
+ }
1441
+
1442
+ // src/tools/chat.ts
1443
+ import { z as z12 } from "zod";
1444
+ var listConversationsSchema = z12.object({
1445
+ limit: z12.number().int().min(1).max(100).default(20).describe("Maximum number of conversations to return")
1446
+ });
1447
+ var getConversationMessagesSchema = z12.object({
1448
+ conversationId: z12.string().uuid().describe("The conversation UUID to get messages for")
1449
+ });
1450
+ var sendChatMessageSchema = z12.object({
1451
+ message: z12.string().describe("The message to send to the AI chat"),
1452
+ conversationId: z12.string().uuid().optional().describe("Existing conversation UUID to continue. Starts a new conversation if omitted."),
1453
+ agentType: z12.string().optional().describe("Agent type to use for the conversation (e.g., review, monitoring, forecasting)")
1454
+ });
1455
+ async function listConversations(input, client) {
1456
+ try {
1457
+ const response = await client.get("/api/chat/conversations", {
1458
+ limit: input.limit
1459
+ });
1460
+ const conversations = response.data || response.conversations || response;
1461
+ return {
1462
+ content: [
1463
+ {
1464
+ type: "text",
1465
+ text: formatAsJson({
1466
+ totalConversations: Array.isArray(conversations) ? conversations.length : 0,
1467
+ conversations
1468
+ })
1469
+ }
1470
+ ]
1471
+ };
1472
+ } catch (error) {
1473
+ return {
1474
+ content: [
1475
+ {
1476
+ type: "text",
1477
+ text: formatError(error)
1478
+ }
1479
+ ],
1480
+ isError: true
1481
+ };
1482
+ }
1483
+ }
1484
+ async function getConversationMessages(input, client) {
1485
+ try {
1486
+ const response = await client.get(`/api/chat/messages/${input.conversationId}`);
1487
+ const messages = response.data || response.messages || response;
1488
+ return {
1489
+ content: [
1490
+ {
1491
+ type: "text",
1492
+ text: formatAsJson({
1493
+ conversationId: input.conversationId,
1494
+ totalMessages: Array.isArray(messages) ? messages.length : 0,
1495
+ messages
1496
+ })
1497
+ }
1498
+ ]
1499
+ };
1500
+ } catch (error) {
1501
+ return {
1502
+ content: [
1503
+ {
1504
+ type: "text",
1505
+ text: formatError(error)
1506
+ }
1507
+ ],
1508
+ isError: true
1509
+ };
1510
+ }
1511
+ }
1512
+ async function sendChatMessage(input, client) {
1513
+ try {
1514
+ const body = {
1515
+ message: input.message
1516
+ };
1517
+ if (input.conversationId)
1518
+ body.conversationId = input.conversationId;
1519
+ if (input.agentType)
1520
+ body.agentType = input.agentType;
1521
+ const response = await client.post("/api/chat", body);
1522
+ return {
1523
+ content: [
1524
+ {
1525
+ type: "text",
1526
+ text: formatAsJson(response.data || response)
1527
+ }
1528
+ ]
1529
+ };
1530
+ } catch (error) {
1531
+ return {
1532
+ content: [
1533
+ {
1534
+ type: "text",
1535
+ text: formatError(error)
1536
+ }
1537
+ ],
1538
+ isError: true
1539
+ };
1540
+ }
1541
+ }
1542
+
1543
+ // src/tools/index.ts
1544
+ function registerTools(server, client, options = {}) {
1545
+ const rateLimiter = options.rateLimiter ?? new RateLimiter(100, 6e4);
1546
+ function wrapTool(_toolName, handler) {
1547
+ return async (input) => {
1548
+ const rateCheck = rateLimiter.check("default");
1549
+ if (!rateCheck.allowed) {
1550
+ return {
1551
+ content: [
1552
+ {
1553
+ type: "text",
1554
+ text: `Rate limit exceeded. Try again in ${Math.ceil(
1555
+ rateCheck.retryAfterMs / 1e3
1556
+ )}s. Limit: 100 requests/minute.`
1557
+ }
1558
+ ],
1559
+ isError: true
1560
+ };
1561
+ }
1562
+ return handler(input, client);
1563
+ };
1564
+ }
1565
+ function tool(name, title, description, schema, handler, annotations) {
1566
+ server.registerTool(
1567
+ name,
1568
+ {
1569
+ title,
1570
+ description,
1571
+ inputSchema: schema.shape,
1572
+ annotations: { title, ...annotations }
1573
+ },
1574
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1575
+ wrapTool(name, handler)
1576
+ );
1577
+ }
1578
+ tool(
1579
+ "list_apps",
1580
+ "List apps",
1581
+ "List all mobile apps in your Fload organization. Returns app metadata including name, bundle ID, platform (iOS/Android), icon URL, and category.",
1582
+ listAppsSchema,
1583
+ listApps,
1584
+ { readOnlyHint: true, openWorldHint: false }
1585
+ );
1586
+ tool(
1587
+ "get_app_details",
1588
+ "Get app details",
1589
+ "Get detailed information about a specific app, including metadata, connected data sources (App Store Connect, Google Play Console), and sync status. Provide either assetId (UUID) or bundleId.",
1590
+ getAppDetailsSchema,
1591
+ getAppDetails,
1592
+ { readOnlyHint: true, openWorldHint: false }
1593
+ );
1594
+ tool(
1595
+ "get_reviews",
1596
+ "Get reviews",
1597
+ "Get app reviews with flexible filtering. Filter by app (assetId or bundleId), platform, rating (1-5 stars), replied status, and date range. Returns reviews with metadata, author, body text, and reply status. Useful for sentiment analysis, support workflows, and review management.",
1598
+ getReviewsSchema,
1599
+ getReviews,
1600
+ { readOnlyHint: true, openWorldHint: false }
1601
+ );
1602
+ tool(
1603
+ "generate_review_reply",
1604
+ "Generate review reply (AI draft)",
1605
+ "Generate an AI draft reply for an app review. The AI uses the review context and any configured agent settings (tone, custom instructions) to craft a response. Returns the generated draft text. Does not publish \u2014 call send_review_reply or approve_action to publish the draft.",
1606
+ generateReviewReplySchema,
1607
+ generateReviewReply,
1608
+ { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
1609
+ );
1610
+ tool(
1611
+ "send_review_reply",
1612
+ "Send review reply",
1613
+ "Send a reply to an app review on the App Store or Google Play. The response text will be submitted as the developer response. This is a write operation that publishes the reply publicly \u2014 treat as destructive (cannot be silently undone).",
1614
+ sendReviewReplySchema,
1615
+ sendReviewReply,
1616
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
1617
+ );
1618
+ tool(
1619
+ "translate_review",
1620
+ "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.",
1622
+ translateReviewSchema,
1623
+ translateReview,
1624
+ { readOnlyHint: true, openWorldHint: true }
1625
+ );
1626
+ tool(
1627
+ "discover_metrics",
1628
+ "Discover available metrics",
1629
+ "Discover what metrics are available for an app. Returns all available metrics organized by category (revenue, downloads, subscriptions, engagement, ads). Always call this first before querying metrics to know what data exists.",
1630
+ discoverMetricsSchema,
1631
+ discoverMetrics,
1632
+ { readOnlyHint: true, openWorldHint: false }
1633
+ );
1634
+ tool(
1635
+ "get_metrics",
1636
+ "Get metrics",
1637
+ "Query metric timeseries data for an app. Supports 30+ metrics (proceeds, totalDownloads, activeSubs, sessions, crashes, adSpend, etc.). Can query multiple metrics at once. Supports dimensional breakdowns (by country, platform, campaign). Use discover_metrics first to see available metrics.",
1638
+ getMetricsSchema,
1639
+ getMetrics,
1640
+ { readOnlyHint: true, openWorldHint: false }
1641
+ );
1642
+ tool(
1643
+ "discover_dimensions",
1644
+ "Discover available dimensions",
1645
+ "Discover available dimensions for breaking down metrics (e.g., country, platform, app version, campaign). Optionally get the available values for a specific dimension.",
1646
+ discoverDimensionsSchema,
1647
+ discoverDimensions,
1648
+ { readOnlyHint: true, openWorldHint: false }
1649
+ );
1650
+ tool(
1651
+ "list_agents",
1652
+ "List agents",
1653
+ "List all available AI agents in the Fload platform with their current status. Returns agent types (review, monitoring, forecasting, growth, aso, ads, product, submission_review) and configuration status.",
1654
+ listAgentsSchema,
1655
+ listAgents,
1656
+ { readOnlyHint: true, openWorldHint: false }
1657
+ );
1658
+ tool(
1659
+ "get_agent_details",
1660
+ "Get agent details",
1661
+ "Get detailed configuration and status for a specific agent type. For the review agent, returns per-asset settings (mode, tone, custom instructions). For the product agent, returns latest run details.",
1662
+ getAgentDetailsSchema,
1663
+ getAgentDetails,
1664
+ { readOnlyHint: true, openWorldHint: false }
1665
+ );
1666
+ tool(
1667
+ "get_agent_run_history",
1668
+ "Get agent run history",
1669
+ "Get run history for a specific agent type. Currently available for the product agent (BrowserStack app installation runs). Returns run status, timing, and error details.",
1670
+ getAgentRunHistorySchema,
1671
+ getAgentRunHistory,
1672
+ { readOnlyHint: true, openWorldHint: false }
1673
+ );
1674
+ tool(
1675
+ "trigger_agent_run",
1676
+ "Trigger agent run",
1677
+ "Trigger a manual run for an agent. Optionally specify an asset (app) to run the agent against. Returns the triggered run details. Reversible in the sense that agent runs can be paused or dismissed.",
1678
+ triggerAgentRunSchema,
1679
+ triggerAgentRun,
1680
+ { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
1681
+ );
1682
+ tool(
1683
+ "pause_agent",
1684
+ "Pause agent",
1685
+ "Pause a running agent. The agent will stop processing until resumed. Reversible via resume_agent.",
1686
+ pauseAgentSchema,
1687
+ pauseAgent,
1688
+ { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1689
+ );
1690
+ tool(
1691
+ "resume_agent",
1692
+ "Resume agent",
1693
+ "Resume a paused agent. The agent will continue processing from where it left off. Reversible via pause_agent.",
1694
+ resumeAgentSchema,
1695
+ resumeAgent,
1696
+ { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1697
+ );
1698
+ tool(
1699
+ "get_agent_activity",
1700
+ "Get agent activity",
1701
+ "Get the recent activity log for an agent. Returns a chronological list of actions the agent has taken, including timestamps, event types, and details.",
1702
+ getAgentActivitySchema,
1703
+ getAgentActivity,
1704
+ { readOnlyHint: true, openWorldHint: false }
1705
+ );
1706
+ tool(
1707
+ "get_anomalies",
1708
+ "Get anomalies",
1709
+ "Get detected anomalies (unusual metric changes) for your apps. Filter by app, severity (low/medium/high/critical), type (surge/decline), status, metric name, and date range. Returns actual vs expected values, deviation percentage, confidence, and suggested actions.",
1710
+ getAnomaliesSchema,
1711
+ getAnomalies,
1712
+ { readOnlyHint: true, openWorldHint: false }
1713
+ );
1714
+ tool(
1715
+ "get_anomaly_detail",
1716
+ "Get anomaly detail",
1717
+ "Get full detail for a single anomaly including chart data. Returns the anomaly metadata, actual vs expected values, and historical metric data points for visualization.",
1718
+ getAnomalyDetailSchema,
1719
+ getAnomalyDetail,
1720
+ { readOnlyHint: true, openWorldHint: false }
1721
+ );
1722
+ tool(
1723
+ "acknowledge_anomaly",
1724
+ "Acknowledge anomaly",
1725
+ 'Mark an anomaly as acknowledged. This updates the anomaly status from "new" to "acknowledged", indicating it has been reviewed but not dismissed. Reversible \u2014 anomaly can be reset or dismissed later.',
1726
+ acknowledgeAnomalySchema,
1727
+ acknowledgeAnomaly,
1728
+ { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1729
+ );
1730
+ tool(
1731
+ "dismiss_anomaly",
1732
+ "Dismiss anomaly",
1733
+ 'Dismiss an anomaly. This updates the anomaly status to "dismissed", removing it from active alerts. Dismissed anomalies are excluded from queries by default. Soft state change \u2014 the anomaly row is preserved and can be un-dismissed later.',
1734
+ dismissAnomalySchema,
1735
+ dismissAnomaly,
1736
+ { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1737
+ );
1738
+ tool(
1739
+ "get_ads_performance",
1740
+ "Get ads performance",
1741
+ "Get ad campaign performance data across platforms (Apple Search Ads, Google Ads, Meta Ads, TikTok Ads). Returns campaign metadata, status, and for Apple Search Ads includes daily performance snapshots (spend, impressions, taps, installs, CPI, TTR, conversion rate).",
1742
+ getAdsPerformanceSchema,
1743
+ getAdsPerformance,
1744
+ { readOnlyHint: true, openWorldHint: false }
1745
+ );
1746
+ tool(
1747
+ "get_growth_audit",
1748
+ "Get growth audit",
1749
+ "Get a comprehensive growth audit for an app. Synthesizes data from review sentiment analysis, recent anomalies, valuation trends, and connector health into an actionable growth assessment.",
1750
+ getGrowthAuditSchema,
1751
+ getGrowthAudit,
1752
+ { readOnlyHint: true, openWorldHint: false }
1753
+ );
1754
+ tool(
1755
+ "get_growth_score",
1756
+ "Get growth score",
1757
+ "Get a calculated growth score (0-100) and grade (A-F) for an app. The score is based on app store rating, valuation trend, recent anomalies, review sentiment, and data connector health. Includes a breakdown of scoring factors.",
1758
+ getGrowthScoreSchema,
1759
+ getGrowthScore,
1760
+ { readOnlyHint: true, openWorldHint: false }
1761
+ );
1762
+ tool(
1763
+ "get_forecasts",
1764
+ "Get forecasts",
1765
+ "Get valuation-based forecasts and trend analysis for an app. Returns historical valuation data points, trend statistics (direction, volatility), and simple linear projections. For detailed metric forecasting with statistical models, use the platform dashboard.",
1766
+ getForecastsSchema,
1767
+ getForecasts,
1768
+ { readOnlyHint: true, openWorldHint: false }
1769
+ );
1770
+ tool(
1771
+ "get_dashboard_overview",
1772
+ "Get dashboard overview",
1773
+ "Get an aggregated dashboard overview for the organization. Returns portfolio summary (apps, valuations, ratings), data connector health status, and alerts (recent anomalies, pending review drafts).",
1774
+ getDashboardOverviewSchema,
1775
+ getDashboardOverview,
1776
+ { readOnlyHint: true, openWorldHint: false }
1777
+ );
1778
+ tool(
1779
+ "list_pending_actions",
1780
+ "List pending actions",
1781
+ "List pending actions awaiting approval. Currently shows AI-generated review draft replies that have not been sent yet. Includes the original review context and the drafted reply. Filter by app.",
1782
+ listPendingActionsSchema,
1783
+ listPendingActions,
1784
+ { readOnlyHint: true, openWorldHint: false }
1785
+ );
1786
+ tool(
1787
+ "approve_action",
1788
+ "Approve pending action",
1789
+ "Approve a pending action (e.g., a review draft reply). Approving a review reply publishes it to the store \u2014 treat as destructive (publicly visible, cannot be silently undone). Optionally edit the reply text before approving.",
1790
+ approveActionSchema,
1791
+ approveAction,
1792
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
1793
+ );
1794
+ tool(
1795
+ "reject_action",
1796
+ "Reject pending action",
1797
+ "Reject a pending action (e.g., delete a review draft reply). The draft will be permanently removed. Destructive \u2014 not recoverable without regenerating the draft.",
1798
+ rejectActionSchema,
1799
+ rejectAction,
1800
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
1801
+ );
1802
+ tool(
1803
+ "get_aso_summary",
1804
+ "Get ASO summary",
1805
+ "Get the ASO (App Store Optimization) score, health status, and overview for an app. Returns an overall optimization score and key ASO health indicators. Use this for a quick snapshot of how well an app is optimized for store search and discovery.",
1806
+ getAsoSummarySchema,
1807
+ getAsoSummary,
1808
+ { readOnlyHint: true, openWorldHint: false }
1809
+ );
1810
+ tool(
1811
+ "get_aso_recommendations",
1812
+ "Get ASO recommendations",
1813
+ "Get actionable ASO recommendations for an app, including suggested improvements to the title, subtitle, keywords, and description. Each recommendation explains the rationale and expected impact on search visibility.",
1814
+ getAsoRecommendationsSchema,
1815
+ getAsoRecommendations,
1816
+ { readOnlyHint: true, openWorldHint: false }
1817
+ );
1818
+ tool(
1819
+ "get_aso_keywords",
1820
+ "Get ASO keywords",
1821
+ 'Get keyword intelligence for an app \u2014 current keyword rankings, search volume estimates, difficulty scores, and competitor keyword data. Optionally filter by locale (e.g., "en-US"). Useful for identifying keyword opportunities and tracking ranking changes.',
1822
+ getAsoKeywordsSchema,
1823
+ getAsoKeywords,
1824
+ { readOnlyHint: true, openWorldHint: false }
1825
+ );
1826
+ tool(
1827
+ "get_aso_experiments",
1828
+ "Get ASO experiments",
1829
+ "List ASO experiments (A/B tests and metadata changes) for an app. Filter by status: proposed, approved, applied, measuring, completed, or reverted. Returns experiment details, variants, and results when available. Supports pagination.",
1830
+ getAsoExperimentsSchema,
1831
+ getAsoExperiments,
1832
+ { readOnlyHint: true, openWorldHint: false }
1833
+ );
1834
+ tool(
1835
+ "get_aso_locale_snapshots",
1836
+ "Get ASO locale snapshots",
1837
+ "Get current App Store and Google Play listing snapshots across all locales for an app. Returns the live title, subtitle, keywords, description, and promotional text for each locale. Useful for auditing localized metadata consistency.",
1838
+ getAsoLocaleSnapshotsSchema,
1839
+ getAsoLocaleSnapshots,
1840
+ { readOnlyHint: true, openWorldHint: false }
1841
+ );
1842
+ tool(
1843
+ "trigger_aso_analysis",
1844
+ "Trigger ASO analysis",
1845
+ "Trigger a new ASO analysis run for an app. This kicks off a fresh evaluation of the app's store listing metadata, keyword rankings, and competitive positioning. Results will be reflected in subsequent calls to get_aso_summary and get_aso_recommendations.",
1846
+ triggerAsoAnalysisSchema,
1847
+ triggerAsoAnalysis,
1848
+ { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
1849
+ );
1850
+ tool(
1851
+ "list_conversations",
1852
+ "List conversations",
1853
+ "List chat conversations in the Fload AI chat. Returns conversation metadata including title, creation date, and last message preview. Use limit to control how many are returned.",
1854
+ listConversationsSchema,
1855
+ listConversations,
1856
+ { readOnlyHint: true, openWorldHint: false }
1857
+ );
1858
+ tool(
1859
+ "get_conversation_messages",
1860
+ "Get conversation messages",
1861
+ "Get all messages in a specific chat conversation. Returns the full message history including user messages and AI responses with timestamps and roles.",
1862
+ getConversationMessagesSchema,
1863
+ getConversationMessages,
1864
+ { readOnlyHint: true, openWorldHint: false }
1865
+ );
1866
+ tool(
1867
+ "send_chat_message",
1868
+ "Send chat message",
1869
+ "Send a message to the Fload AI chat assistant. Starts a new conversation if no conversationId is provided, or continues an existing one. Optionally specify an agentType to route to a specialized agent (review, monitoring, forecasting, etc.). Adds a message to your chat history \u2014 not publicly visible.",
1870
+ sendChatMessageSchema,
1871
+ sendChatMessage,
1872
+ { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
1873
+ );
1874
+ }
1875
+ export {
1876
+ acknowledgeAnomaly,
1877
+ acknowledgeAnomalySchema,
1878
+ approveAction,
1879
+ approveActionSchema,
1880
+ discoverDimensions,
1881
+ discoverDimensionsSchema,
1882
+ discoverMetrics,
1883
+ discoverMetricsSchema,
1884
+ dismissAnomaly,
1885
+ dismissAnomalySchema,
1886
+ generateReviewReply,
1887
+ generateReviewReplySchema,
1888
+ getAdsPerformance,
1889
+ getAdsPerformanceSchema,
1890
+ getAgentActivity,
1891
+ getAgentActivitySchema,
1892
+ getAgentDetails,
1893
+ getAgentDetailsSchema,
1894
+ getAgentRunHistory,
1895
+ getAgentRunHistorySchema,
1896
+ getAnomalies,
1897
+ getAnomaliesSchema,
1898
+ getAnomalyDetail,
1899
+ getAnomalyDetailSchema,
1900
+ getAppDetails,
1901
+ getAppDetailsSchema,
1902
+ getAsoExperiments,
1903
+ getAsoExperimentsSchema,
1904
+ getAsoKeywords,
1905
+ getAsoKeywordsSchema,
1906
+ getAsoLocaleSnapshots,
1907
+ getAsoLocaleSnapshotsSchema,
1908
+ getAsoRecommendations,
1909
+ getAsoRecommendationsSchema,
1910
+ getAsoSummary,
1911
+ getAsoSummarySchema,
1912
+ getConversationMessages,
1913
+ getConversationMessagesSchema,
1914
+ getDashboardOverview,
1915
+ getDashboardOverviewSchema,
1916
+ getForecasts,
1917
+ getForecastsSchema,
1918
+ getGrowthAudit,
1919
+ getGrowthAuditSchema,
1920
+ getGrowthScore,
1921
+ getGrowthScoreSchema,
1922
+ getMetrics,
1923
+ getMetricsSchema,
1924
+ getReviews,
1925
+ getReviewsSchema,
1926
+ listAgents,
1927
+ listAgentsSchema,
1928
+ listApps,
1929
+ listAppsSchema,
1930
+ listConversations,
1931
+ listConversationsSchema,
1932
+ listPendingActions,
1933
+ listPendingActionsSchema,
1934
+ pauseAgent,
1935
+ pauseAgentSchema,
1936
+ registerTools,
1937
+ rejectAction,
1938
+ rejectActionSchema,
1939
+ resumeAgent,
1940
+ resumeAgentSchema,
1941
+ sendChatMessage,
1942
+ sendChatMessageSchema,
1943
+ sendReviewReply,
1944
+ sendReviewReplySchema,
1945
+ translateReview,
1946
+ translateReviewSchema,
1947
+ triggerAgentRun,
1948
+ triggerAgentRunSchema,
1949
+ triggerAsoAnalysis,
1950
+ triggerAsoAnalysisSchema
1951
+ };
1952
+ //# sourceMappingURL=index.js.map