@layers/amba-mcp 4.0.2 → 4.0.3
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/dist/expo-build-prompt.js +2 -2
- package/dist/index.js +1024 -76
- package/dist/lib/annotations.d.ts +113 -0
- package/dist/resources/amba-setup-economy.d.ts +1 -1
- package/dist/resources/amba-setup-infrastructure.d.ts +1 -1
- package/dist/resources/amba-setup-social.d.ts +1 -1
- package/dist/resources/amba-setup.d.ts +1 -1
- package/dist/resources/index.d.ts +3 -3
- package/dist/tools/domains.d.ts +33 -0
- package/dist/tools/email.d.ts +26 -0
- package/dist/tools/funnels.d.ts +3 -0
- package/dist/tools/invites.d.ts +5 -4
- package/dist/tools/secrets.d.ts +8 -8
- package/dist/tools/webhooks.d.ts +27 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -286,6 +286,234 @@ function warnDeprecatedAlias(alias, canonical) {
|
|
|
286
286
|
console.warn(`[amba-mcp] tool "${alias}" is deprecated; use "${canonical}" instead. The alias still works but will be removed in a future release.`);
|
|
287
287
|
}
|
|
288
288
|
//#endregion
|
|
289
|
+
//#region src/lib/annotations.ts
|
|
290
|
+
/**
|
|
291
|
+
* Read-only verbs. A tool whose name contains one of these (and no
|
|
292
|
+
* destructive verb) does not mutate project state — it reads/derives/
|
|
293
|
+
* exports data. Maps to `readOnlyHint: true`.
|
|
294
|
+
*/
|
|
295
|
+
const READ_VERBS = new Set([
|
|
296
|
+
"get",
|
|
297
|
+
"list",
|
|
298
|
+
"find",
|
|
299
|
+
"describe",
|
|
300
|
+
"stats",
|
|
301
|
+
"export",
|
|
302
|
+
"analytics",
|
|
303
|
+
"me",
|
|
304
|
+
"status",
|
|
305
|
+
"count",
|
|
306
|
+
"search",
|
|
307
|
+
"check",
|
|
308
|
+
"tiers",
|
|
309
|
+
"verify",
|
|
310
|
+
"logs",
|
|
311
|
+
"query"
|
|
312
|
+
]);
|
|
313
|
+
/**
|
|
314
|
+
* Destructive verbs. A tool whose name contains one of these MAY perform
|
|
315
|
+
* a destructive update (irreversible removal of existing state). Maps to
|
|
316
|
+
* `destructiveHint: true`. `reset` is included because `*_reset_sandbox`
|
|
317
|
+
* wipes end-user data.
|
|
318
|
+
*/
|
|
319
|
+
const DESTRUCTIVE_VERBS = new Set([
|
|
320
|
+
"delete",
|
|
321
|
+
"remove",
|
|
322
|
+
"revoke",
|
|
323
|
+
"drop",
|
|
324
|
+
"reset"
|
|
325
|
+
]);
|
|
326
|
+
/**
|
|
327
|
+
* Write verbs — mutations that are additive / non-destructive (create a
|
|
328
|
+
* row, update fields, grant currency, deploy a function, send a push, …).
|
|
329
|
+
* Maps to `destructiveHint: false`. This set exists so a write tool that
|
|
330
|
+
* matches NONE of the read/destructive verbs is still positively
|
|
331
|
+
* classified (rather than falling through unannotated). Kept explicit so
|
|
332
|
+
* an unrecognised verb surfaces as a test failure instead of silently
|
|
333
|
+
* defaulting.
|
|
334
|
+
*/
|
|
335
|
+
const WRITE_VERBS = new Set([
|
|
336
|
+
"create",
|
|
337
|
+
"update",
|
|
338
|
+
"set",
|
|
339
|
+
"patch",
|
|
340
|
+
"grant",
|
|
341
|
+
"spend",
|
|
342
|
+
"deduct",
|
|
343
|
+
"deploy",
|
|
344
|
+
"send",
|
|
345
|
+
"add",
|
|
346
|
+
"configure",
|
|
347
|
+
"invoke",
|
|
348
|
+
"schedule",
|
|
349
|
+
"assign",
|
|
350
|
+
"upload",
|
|
351
|
+
"rotate",
|
|
352
|
+
"reprovision",
|
|
353
|
+
"transfer",
|
|
354
|
+
"pause",
|
|
355
|
+
"resume",
|
|
356
|
+
"trigger",
|
|
357
|
+
"evaluate",
|
|
358
|
+
"test",
|
|
359
|
+
"approve",
|
|
360
|
+
"reject",
|
|
361
|
+
"escalate",
|
|
362
|
+
"replay",
|
|
363
|
+
"import",
|
|
364
|
+
"alter",
|
|
365
|
+
"insert",
|
|
366
|
+
"invite",
|
|
367
|
+
"register",
|
|
368
|
+
"purchase",
|
|
369
|
+
"signup",
|
|
370
|
+
"login",
|
|
371
|
+
"refresh"
|
|
372
|
+
]);
|
|
373
|
+
/**
|
|
374
|
+
* Split a tool name into lowercase verb-candidate tokens, dropping the
|
|
375
|
+
* `amba_` prefix. Exported for the test suite.
|
|
376
|
+
*/
|
|
377
|
+
function toolNameTokens(name) {
|
|
378
|
+
return name.replace(/^amba_/, "").split("_").filter(Boolean);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Classify a tool name into its behavioural class by PRECEDENCE
|
|
382
|
+
* (destructive > read > write). Returns `null` if no token matches any
|
|
383
|
+
* known verb — that should never happen for a real Amba tool and the
|
|
384
|
+
* test suite asserts it doesn't, so a `null` at runtime means a new tool
|
|
385
|
+
* used an unrecognised verb and needs a verb-set entry here.
|
|
386
|
+
*/
|
|
387
|
+
function classifyToolVerb(name) {
|
|
388
|
+
const tokens = toolNameTokens(name);
|
|
389
|
+
let sawRead = false;
|
|
390
|
+
let sawWrite = false;
|
|
391
|
+
for (const token of tokens) {
|
|
392
|
+
if (DESTRUCTIVE_VERBS.has(token)) return "destructive";
|
|
393
|
+
if (READ_VERBS.has(token)) sawRead = true;
|
|
394
|
+
else if (WRITE_VERBS.has(token)) sawWrite = true;
|
|
395
|
+
}
|
|
396
|
+
if (sawRead) return "read";
|
|
397
|
+
if (sawWrite) return "write";
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
/** Title-case a single token (`currencies` → `Currencies`). */
|
|
401
|
+
function capitalize(token) {
|
|
402
|
+
return token.length === 0 ? token : token[0].toUpperCase() + token.slice(1);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Verb-set tokens that read as nouns/objects far more often than as the
|
|
406
|
+
* operation verb, so they must NOT be chosen as the title's leading verb.
|
|
407
|
+
* They stay in the verb sets above for CLASSIFICATION (e.g. `*_status`,
|
|
408
|
+
* `*_stats` are read-only), but for TITLE purposes they're objects:
|
|
409
|
+
*
|
|
410
|
+
* amba_get_friendship_stats → "Get Friendship Stats" (not "Stats …")
|
|
411
|
+
* amba_pause_function_schedule→ "Pause Function Schedule" (not "Schedule …")
|
|
412
|
+
* amba_functions_get_logs → "Get Functions Logs"
|
|
413
|
+
* amba_billing_status → "Billing Status" (no action verb at all)
|
|
414
|
+
* amba_sessions_analytics → "Sessions Analytics"
|
|
415
|
+
*/
|
|
416
|
+
const NOUN_LIKE_VERBS = new Set([
|
|
417
|
+
"stats",
|
|
418
|
+
"status",
|
|
419
|
+
"logs",
|
|
420
|
+
"tiers",
|
|
421
|
+
"analytics",
|
|
422
|
+
"schedule",
|
|
423
|
+
"me"
|
|
424
|
+
]);
|
|
425
|
+
/**
|
|
426
|
+
* The verbs eligible to be a title's leading word: every classification
|
|
427
|
+
* verb that isn't a noun-like token. These are unambiguous action verbs
|
|
428
|
+
* (`create`, `get`, `delete`, `set`, `pause`, …).
|
|
429
|
+
*/
|
|
430
|
+
const STRONG_TITLE_VERBS = new Set([
|
|
431
|
+
...READ_VERBS,
|
|
432
|
+
...WRITE_VERBS,
|
|
433
|
+
...DESTRUCTIVE_VERBS
|
|
434
|
+
].filter((v) => !NOUN_LIKE_VERBS.has(v)));
|
|
435
|
+
/**
|
|
436
|
+
* Pick the index of the OPERATIVE action verb for the title, or -1 if the
|
|
437
|
+
* name has no action verb (e.g. `billing_status`, `sessions_analytics`).
|
|
438
|
+
*
|
|
439
|
+
* A naive `tokens.find()` is wrong two different ways:
|
|
440
|
+
* - `amba_currency_grant_rules_create` has `grant` (a noun-modifier in
|
|
441
|
+
* `grant_rules`) AND `create`; `find` picks `grant` → "Grant … Create".
|
|
442
|
+
* - `amba_get_friendship_stats` has `get` AND `stats`; preferring the
|
|
443
|
+
* trailing match picks `stats` → "Stats Get Friendship".
|
|
444
|
+
*
|
|
445
|
+
* Amba's naming puts the operation verb at a token-list BOUNDARY: leading
|
|
446
|
+
* for legacy `amba_<verb>_<resource>`, trailing/mid for canonical
|
|
447
|
+
* `amba_<resource>_<verb>[_<object>]`. So:
|
|
448
|
+
* 1. If the FIRST token is a strong verb → it's the operative verb
|
|
449
|
+
* (legacy alias: `get_friendship_stats`, `create_currency`).
|
|
450
|
+
* 2. Else take the LAST strong verb — skips trailing object nouns like
|
|
451
|
+
* `stats`/`schedule` (excluded from `STRONG_TITLE_VERBS`) and skips
|
|
452
|
+
* mid-name noun-modifiers like `grant` in `grant_rules_create`
|
|
453
|
+
* (`get_stats`/`set_price`/`grant_rules_create` all resolve right).
|
|
454
|
+
* 3. No strong verb → -1 (caller title-cases in order).
|
|
455
|
+
*/
|
|
456
|
+
function operativeVerbIndex(tokens) {
|
|
457
|
+
if (tokens.length > 0 && STRONG_TITLE_VERBS.has(tokens[0])) return 0;
|
|
458
|
+
let lastStrong = -1;
|
|
459
|
+
for (let i = 0; i < tokens.length; i += 1) if (STRONG_TITLE_VERBS.has(tokens[i])) lastStrong = i;
|
|
460
|
+
return lastStrong;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Derive a human-readable Title-Case title from the tool name. The
|
|
464
|
+
* operative action verb (see `operativeVerbIndex`) is moved to the front
|
|
465
|
+
* so the title reads as an imperative; remaining tokens keep their order:
|
|
466
|
+
*
|
|
467
|
+
* amba_currencies_create → "Create Currencies"
|
|
468
|
+
* amba_content_delete_schedule → "Delete Content Schedule"
|
|
469
|
+
* amba_currency_grant_rules_create → "Create Currency Grant Rules"
|
|
470
|
+
* amba_get_friendship_stats → "Get Friendship Stats"
|
|
471
|
+
* amba_pause_function_schedule → "Pause Function Schedule"
|
|
472
|
+
* amba_catalog_items_set_price → "Set Catalog Items Price"
|
|
473
|
+
* amba_users_get → "Get Users"
|
|
474
|
+
*
|
|
475
|
+
* When there is no action verb (`billing_status`, `sessions_analytics`)
|
|
476
|
+
* the tokens are title-cased in order — a readable noun-phrase fallback.
|
|
477
|
+
*/
|
|
478
|
+
function deriveToolTitle(name) {
|
|
479
|
+
const tokens = toolNameTokens(name);
|
|
480
|
+
const idx = operativeVerbIndex(tokens);
|
|
481
|
+
if (idx >= 0) {
|
|
482
|
+
const rest = tokens.filter((_, i) => i !== idx);
|
|
483
|
+
return [tokens[idx], ...rest].map(capitalize).join(" ");
|
|
484
|
+
}
|
|
485
|
+
return tokens.map(capitalize).join(" ");
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Derive the full `ToolAnnotations` (title + read-only/destructive hints)
|
|
489
|
+
* for a tool name. This is the single function `with-pat.ts` calls for
|
|
490
|
+
* every tool it registers, so annotations stay uniform and automatic.
|
|
491
|
+
*/
|
|
492
|
+
function deriveToolAnnotations(name) {
|
|
493
|
+
const title = deriveToolTitle(name);
|
|
494
|
+
switch (classifyToolVerb(name)) {
|
|
495
|
+
case "read": return {
|
|
496
|
+
title,
|
|
497
|
+
readOnlyHint: true
|
|
498
|
+
};
|
|
499
|
+
case "destructive": return {
|
|
500
|
+
title,
|
|
501
|
+
readOnlyHint: false,
|
|
502
|
+
destructiveHint: true
|
|
503
|
+
};
|
|
504
|
+
case "write": return {
|
|
505
|
+
title,
|
|
506
|
+
readOnlyHint: false,
|
|
507
|
+
destructiveHint: false
|
|
508
|
+
};
|
|
509
|
+
default: return {
|
|
510
|
+
title,
|
|
511
|
+
readOnlyHint: false,
|
|
512
|
+
destructiveHint: true
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
289
517
|
//#region src/lib/with-pat.ts
|
|
290
518
|
/**
|
|
291
519
|
* The `pat` schema entry injected into every tool that goes through
|
|
@@ -362,8 +590,8 @@ function registerTool(server, apiClient, name, description, schema, handler, ali
|
|
|
362
590
|
client
|
|
363
591
|
});
|
|
364
592
|
};
|
|
365
|
-
server.tool(name, description, extendedSchema, wrappedHandler(name));
|
|
366
|
-
for (const alias of aliases) server.tool(alias, description, extendedSchema, (async (rawArgs) => {
|
|
593
|
+
server.tool(name, description, extendedSchema, deriveToolAnnotations(name), wrappedHandler(name));
|
|
594
|
+
for (const alias of aliases) server.tool(alias, description, extendedSchema, deriveToolAnnotations(alias), (async (rawArgs) => {
|
|
367
595
|
warnDeprecatedAlias(alias, name);
|
|
368
596
|
return wrappedHandler(name)(rawArgs);
|
|
369
597
|
}));
|
|
@@ -375,15 +603,15 @@ function registerTool(server, apiClient, name, description, schema, handler, ali
|
|
|
375
603
|
* should go through `registerTool`.
|
|
376
604
|
*/
|
|
377
605
|
function registerPublicTool(server, name, description, schema, handler, aliases = []) {
|
|
378
|
-
server.tool(name, description, schema, handler);
|
|
379
|
-
for (const alias of aliases) server.tool(alias, description, schema, (async (args) => {
|
|
606
|
+
server.tool(name, description, schema, deriveToolAnnotations(name), handler);
|
|
607
|
+
for (const alias of aliases) server.tool(alias, description, schema, deriveToolAnnotations(alias), (async (args) => {
|
|
380
608
|
warnDeprecatedAlias(alias, name);
|
|
381
609
|
return handler(args);
|
|
382
610
|
}));
|
|
383
611
|
}
|
|
384
612
|
//#endregion
|
|
385
613
|
//#region src/tools/projects.ts
|
|
386
|
-
function registerTools$
|
|
614
|
+
function registerTools$29(server, apiClient) {
|
|
387
615
|
registerTool(server, apiClient, "amba_projects_list", "List all Amba projects owned by the authenticated developer. Returns project id, name, bundle_id, platform, and environment.", {}, async (_, { client }) => {
|
|
388
616
|
const result = await client.get("/projects");
|
|
389
617
|
return { content: [{
|
|
@@ -490,7 +718,7 @@ function registerTools$25(server, apiClient) {
|
|
|
490
718
|
}
|
|
491
719
|
//#endregion
|
|
492
720
|
//#region src/tools/push.ts
|
|
493
|
-
function registerTools$
|
|
721
|
+
function registerTools$28(server, apiClient) {
|
|
494
722
|
registerTool(server, apiClient, "amba_push_campaigns_create", "Create a new push notification campaign for a project. The campaign starts in \"draft\" status. You can optionally target a segment and schedule delivery.", {
|
|
495
723
|
project_id: z.string().describe("The project ID"),
|
|
496
724
|
title: z.string().describe("Push notification title shown to the user"),
|
|
@@ -620,7 +848,7 @@ const segmentRulesSchema = z.object({
|
|
|
620
848
|
operator: z.enum(["AND", "OR"]).describe("Logical operator combining conditions"),
|
|
621
849
|
conditions: z.array(segmentConditionSchema).describe("Array of filter conditions")
|
|
622
850
|
});
|
|
623
|
-
function registerTools$
|
|
851
|
+
function registerTools$27(server, apiClient) {
|
|
624
852
|
registerTool(server, apiClient, "amba_segments_list", "List user segments for a project. Segments define groups of users based on rules (e.g. \"active in last 7 days\", \"premium users\"). Returns only project-custom segments by default. Pass include_system: true to also include the built-in system segments (e.g. \"all\", \"active_7d\", \"premium\").", {
|
|
625
853
|
project_id: z.string().describe("The project ID"),
|
|
626
854
|
include_system: z.boolean().optional().describe("Include built-in system segments in the result. Defaults to false.")
|
|
@@ -705,8 +933,8 @@ const configConditionSchema = z.object({
|
|
|
705
933
|
percentage: z.number().optional().describe("Percentage rollout (0-100)"),
|
|
706
934
|
value: z.unknown().describe("Override value for this condition")
|
|
707
935
|
});
|
|
708
|
-
function registerTools$
|
|
709
|
-
registerTool(server, apiClient, "
|
|
936
|
+
function registerTools$26(server, apiClient) {
|
|
937
|
+
registerTool(server, apiClient, "amba_remote_configs_list", "List remote-config keys for a project. Returns key, value, value_type, description, conditions, and version per active row. Remote config lets you change app behavior without deploying an update. Returns only project-created config keys by default. Pass include_system: true to also include platform-provided defaults (e.g. app_version_min, maintenance_mode).", {
|
|
710
938
|
project_id: z.string().describe("The project ID"),
|
|
711
939
|
include_system: z.boolean().optional().describe("Include platform-provided default config keys in the result. Defaults to false.")
|
|
712
940
|
}, async ({ project_id, include_system }, { client }) => {
|
|
@@ -717,8 +945,8 @@ function registerTools$22(server, apiClient) {
|
|
|
717
945
|
type: "text",
|
|
718
946
|
text: JSON.stringify(result, null, 2)
|
|
719
947
|
}] };
|
|
720
|
-
}, ["amba_list_configs"]);
|
|
721
|
-
registerTool(server, apiClient, "
|
|
948
|
+
}, ["amba_configs_list", "amba_list_configs"]);
|
|
949
|
+
registerTool(server, apiClient, "amba_remote_configs_create", "Create or upsert a remote-config key. Supports string, number, boolean, and JSON value types. Can include segment-based or percentage-based conditions for targeted rollouts. The handler is upsert-on-key — calling this for an existing key replaces it.", {
|
|
722
950
|
project_id: z.string().describe("The project ID"),
|
|
723
951
|
key: z.string().describe("Config key name (e.g. \"feature_paywall_v2\", \"daily_limit\")"),
|
|
724
952
|
value: z.unknown().describe("The default config value"),
|
|
@@ -743,8 +971,8 @@ function registerTools$22(server, apiClient) {
|
|
|
743
971
|
type: "text",
|
|
744
972
|
text: JSON.stringify(result, null, 2)
|
|
745
973
|
}] };
|
|
746
|
-
}, ["amba_create_config"]);
|
|
747
|
-
registerTool(server, apiClient, "
|
|
974
|
+
}, ["amba_configs_create", "amba_create_config"]);
|
|
975
|
+
registerTool(server, apiClient, "amba_remote_configs_update", "Partially update a single remote-config key. Only fields provided are touched — other fields are preserved. Updating `value` bumps the per-row version counter; any successful mutation also recomputes the singleton config_versions hash so cached clients see the change.", {
|
|
748
976
|
project_id: z.string().describe("The project ID"),
|
|
749
977
|
key: z.string().describe("The config key to update"),
|
|
750
978
|
value: z.unknown().optional().describe("New default value (omit to leave unchanged)"),
|
|
@@ -760,8 +988,8 @@ function registerTools$22(server, apiClient) {
|
|
|
760
988
|
type: "text",
|
|
761
989
|
text: JSON.stringify(result, null, 2)
|
|
762
990
|
}] };
|
|
763
|
-
}, ["amba_update_config"]);
|
|
764
|
-
registerTool(server, apiClient, "
|
|
991
|
+
}, ["amba_configs_update", "amba_update_config"]);
|
|
992
|
+
registerTool(server, apiClient, "amba_remote_configs_delete", "Delete a remote-config key. Idempotent — deleting a non-existent key returns success. Recomputes the config_versions hash so clients refresh cached config on next fetch.", {
|
|
765
993
|
project_id: z.string().describe("The project ID"),
|
|
766
994
|
key: z.string().describe("The config key to delete")
|
|
767
995
|
}, async ({ project_id, key }, { client }) => {
|
|
@@ -770,7 +998,7 @@ function registerTools$22(server, apiClient) {
|
|
|
770
998
|
type: "text",
|
|
771
999
|
text: JSON.stringify(result, null, 2)
|
|
772
1000
|
}] };
|
|
773
|
-
}, ["amba_delete_config"]);
|
|
1001
|
+
}, ["amba_configs_delete", "amba_delete_config"]);
|
|
774
1002
|
}
|
|
775
1003
|
//#endregion
|
|
776
1004
|
//#region src/tools/content.ts
|
|
@@ -783,7 +1011,7 @@ const contentItemSchema = z.object({
|
|
|
783
1011
|
metadata: z.record(z.unknown()).optional().describe("Arbitrary metadata key-value pairs"),
|
|
784
1012
|
is_premium: z.boolean().optional().describe("Whether this content requires a premium entitlement")
|
|
785
1013
|
});
|
|
786
|
-
function registerTools$
|
|
1014
|
+
function registerTools$25(server, apiClient) {
|
|
787
1015
|
registerTool(server, apiClient, "amba_content_libraries_create", "Create a content library for a project. A content library is a collection of content items (tips, quotes, articles) that can be delivered on a schedule to keep users engaged.", {
|
|
788
1016
|
project_id: z.string().describe("The project ID"),
|
|
789
1017
|
name: z.string().describe("Library name (e.g. \"Daily Motivation\", \"Workout Tips\")"),
|
|
@@ -947,7 +1175,7 @@ function registerTools$21(server, apiClient) {
|
|
|
947
1175
|
}
|
|
948
1176
|
//#endregion
|
|
949
1177
|
//#region src/tools/streaks.ts
|
|
950
|
-
function registerTools$
|
|
1178
|
+
function registerTools$24(server, apiClient) {
|
|
951
1179
|
registerTool(server, apiClient, "amba_streaks_create", "Create a streak definition for a project. Streaks track consecutive user engagement (e.g. daily logins, workout completions). Supports configurable periods, grace periods, and freeze mechanics.", {
|
|
952
1180
|
project_id: z.string().describe("The project ID"),
|
|
953
1181
|
key: z.string().regex(/^[a-z0-9_-]{1,64}$/).optional().describe("Stable identifier used by the SDK to qualify a streak without knowing its UUID: `Amba.streaks.qualify(\"daily_login\")`. Lowercase letters, digits, underscore, hyphen, 1-64 characters. Immutable after creation — changing it breaks live SDK calls. When omitted the server derives one from `name`."),
|
|
@@ -1021,11 +1249,25 @@ function registerTools$20(server, apiClient) {
|
|
|
1021
1249
|
text: JSON.stringify(result, null, 2)
|
|
1022
1250
|
}] };
|
|
1023
1251
|
});
|
|
1252
|
+
registerTool(server, apiClient, "amba_streaks_reset", "Reset or correct a specific user's streak — for support fixing a wrongly-broken streak (e.g. after an outage). Sets the user's current count and marks the streak active again. Omit set_count to zero the streak (clean restart); pass set_count to restore it to a specific value (longest_count is bumped if exceeded). Returns 404 if the user has no streak row for this definition yet.", {
|
|
1253
|
+
project_id: z.string().describe("The project ID"),
|
|
1254
|
+
streak_id: z.string().describe("The streak definition ID"),
|
|
1255
|
+
app_user_id: z.string().describe("The user whose streak to reset"),
|
|
1256
|
+
set_count: z.number().int().nonnegative().optional().describe("Restore the streak to this count. Omit to zero the streak (clean restart).")
|
|
1257
|
+
}, async ({ project_id, streak_id, app_user_id, set_count }, { client }) => {
|
|
1258
|
+
const payload = { app_user_id };
|
|
1259
|
+
if (set_count !== void 0) payload.set_count = set_count;
|
|
1260
|
+
const result = await client.post(`/projects/${project_id}/streaks/${streak_id}/reset`, payload);
|
|
1261
|
+
return { content: [{
|
|
1262
|
+
type: "text",
|
|
1263
|
+
text: JSON.stringify(result, null, 2)
|
|
1264
|
+
}] };
|
|
1265
|
+
});
|
|
1024
1266
|
}
|
|
1025
1267
|
//#endregion
|
|
1026
1268
|
//#region src/tools/integrations.ts
|
|
1027
1269
|
const providerEnum = z.enum(INTEGRATION_PROVIDERS);
|
|
1028
|
-
function registerTools$
|
|
1270
|
+
function registerTools$23(server, apiClient) {
|
|
1029
1271
|
registerTool(server, apiClient, "amba_integrations_configure", "Configure (create or replace) a third-party integration for a project. Supported providers: \"apns\" (Apple Push), \"fcm\" (Firebase Cloud Messaging), \"revenuecat\" (subscription management), \"superwall\" (paywall management). Each provider requires specific config fields.", {
|
|
1030
1272
|
project_id: z.string().describe("The project ID"),
|
|
1031
1273
|
provider: providerEnum.describe("Integration provider name"),
|
|
@@ -1095,7 +1337,7 @@ const PERIOD_DAYS = {
|
|
|
1095
1337
|
"30d": "30",
|
|
1096
1338
|
"90d": "90"
|
|
1097
1339
|
};
|
|
1098
|
-
function registerTools$
|
|
1340
|
+
function registerTools$22(server, apiClient) {
|
|
1099
1341
|
registerTool(server, apiClient, "amba_analytics_get", "Get daily analytics for a project. Returns per-day event counts and unique-user counts for the requested window, plus totals. Use this to verify that end-to-end event tracking is working after seeding and testing.", {
|
|
1100
1342
|
project_id: z.string().describe("The project ID"),
|
|
1101
1343
|
period: z.enum([
|
|
@@ -1118,7 +1360,7 @@ function registerTools$18(server, apiClient) {
|
|
|
1118
1360
|
}
|
|
1119
1361
|
//#endregion
|
|
1120
1362
|
//#region src/tools/users.ts
|
|
1121
|
-
function registerTools$
|
|
1363
|
+
function registerTools$21(server, apiClient) {
|
|
1122
1364
|
registerTool(server, apiClient, "amba_users_list", "List app users for a project. Supports pagination with offset and limit, plus optional search (matches email / external_id / display_name with ILIKE) and segment filter. Returns user profiles including external_id, email, display_name, properties, and activity timestamps.", {
|
|
1123
1365
|
project_id: z.string().describe("The project ID"),
|
|
1124
1366
|
limit: z.number().optional().describe("Maximum number of users to return (default 50)"),
|
|
@@ -1491,7 +1733,7 @@ final dailyLimit = await Amba.config.get<int>('daily_limit', defaultValue: 10);
|
|
|
1491
1733
|
final streak = await Amba.streaks.get('daily_login');
|
|
1492
1734
|
print('Current streak: \${streak.currentCount}');`
|
|
1493
1735
|
};
|
|
1494
|
-
function registerTools$
|
|
1736
|
+
function registerTools$20(server, apiClient) {
|
|
1495
1737
|
registerTool(server, apiClient, "amba_sdk_get_setup_instructions", "Get platform-specific code snippets and setup instructions for integrating the Amba client SDK into a mobile app. Supports iOS (Swift), Android (Kotlin), React Native, Expo, and Flutter.", { platform: z.enum([
|
|
1496
1738
|
"ios",
|
|
1497
1739
|
"android",
|
|
@@ -1547,7 +1789,7 @@ function registerTools$16(server, apiClient) {
|
|
|
1547
1789
|
"1. Replace YOUR_CLIENT_API_KEY with your project's client API key (find it with amba_projects_get)",
|
|
1548
1790
|
"2. Configure push notification integration (APNs for iOS, FCM for Android) using amba_integrations_configure",
|
|
1549
1791
|
"3. Create segments to target user groups with amba_segments_create",
|
|
1550
|
-
"4. Set up remote config values with
|
|
1792
|
+
"4. Set up remote config values with amba_remote_configs_create",
|
|
1551
1793
|
"5. Create streaks to track user engagement with amba_streaks_create",
|
|
1552
1794
|
"",
|
|
1553
1795
|
"## Content delivery loop (worked example — DX-12)",
|
|
@@ -1576,7 +1818,7 @@ const criteriaSchema = z.object({
|
|
|
1576
1818
|
property_key: z.string().optional().describe("User property key (required for property_value type)"),
|
|
1577
1819
|
target_value: z.number().describe("Target value to unlock the achievement")
|
|
1578
1820
|
});
|
|
1579
|
-
function registerTools$
|
|
1821
|
+
function registerTools$19(server, apiClient) {
|
|
1580
1822
|
registerTool(server, apiClient, "amba_achievements_create", "Create an achievement/badge definition. Achievements unlock automatically when users meet the criteria (e.g. track 5 workouts, reach a 7-day streak, earn 5000 XP). Can optionally award bonus XP on unlock.", {
|
|
1581
1823
|
project_id: z.string().describe("The project ID"),
|
|
1582
1824
|
key: z.string().describe("Unique key for this achievement (e.g. \"first_workout\", \"streak_master_7\")"),
|
|
@@ -1656,10 +1898,32 @@ function registerTools$15(server, apiClient) {
|
|
|
1656
1898
|
text: JSON.stringify(result, null, 2)
|
|
1657
1899
|
}] };
|
|
1658
1900
|
}, ["amba_delete_achievement"]);
|
|
1901
|
+
registerTool(server, apiClient, "amba_achievements_grant", "Manually award an achievement to a specific user (marks it unlocked now). For support, promos, or corrections — the rest of the achievement surface only defines criteria the system evaluates automatically. Idempotent: re-granting an already-unlocked achievement keeps the original unlock time. Note: this does NOT auto-award the achievement's xp_reward; award XP separately with amba_xp_grant if intended.", {
|
|
1902
|
+
project_id: z.string().describe("The project ID"),
|
|
1903
|
+
achievement_id: z.string().describe("The achievement definition ID"),
|
|
1904
|
+
app_user_id: z.string().describe("The user to award the achievement to")
|
|
1905
|
+
}, async ({ project_id, achievement_id, app_user_id }, { client }) => {
|
|
1906
|
+
const result = await client.post(`/projects/${project_id}/achievements/${achievement_id}/grant`, { app_user_id });
|
|
1907
|
+
return { content: [{
|
|
1908
|
+
type: "text",
|
|
1909
|
+
text: JSON.stringify(result, null, 2)
|
|
1910
|
+
}] };
|
|
1911
|
+
});
|
|
1912
|
+
registerTool(server, apiClient, "amba_achievements_revoke", "Claw back an achievement from a specific user — the inverse of grant. Removes the user's unlock entirely. Returns 404 if the user does not currently hold the achievement.", {
|
|
1913
|
+
project_id: z.string().describe("The project ID"),
|
|
1914
|
+
achievement_id: z.string().describe("The achievement definition ID"),
|
|
1915
|
+
app_user_id: z.string().describe("The user to revoke the achievement from")
|
|
1916
|
+
}, async ({ project_id, achievement_id, app_user_id }, { client }) => {
|
|
1917
|
+
const result = await client.post(`/projects/${project_id}/achievements/${achievement_id}/revoke`, { app_user_id });
|
|
1918
|
+
return { content: [{
|
|
1919
|
+
type: "text",
|
|
1920
|
+
text: JSON.stringify(result, null, 2)
|
|
1921
|
+
}] };
|
|
1922
|
+
});
|
|
1659
1923
|
}
|
|
1660
1924
|
//#endregion
|
|
1661
1925
|
//#region src/tools/challenges.ts
|
|
1662
|
-
function registerTools$
|
|
1926
|
+
function registerTools$18(server, apiClient) {
|
|
1663
1927
|
registerTool(server, apiClient, "amba_challenges_create", "Create a time-limited challenge. Challenges are events that run between start_at and end_at, where users try to reach a goal (e.g. track 10 workouts this week, earn 500 XP in 3 days). Can reward XP and/or unlock an achievement on completion.", {
|
|
1664
1928
|
project_id: z.string().describe("The project ID"),
|
|
1665
1929
|
name: z.string().describe("Challenge name (e.g. \"7-Day Fitness Sprint\", \"XP Weekend Blitz\")"),
|
|
@@ -1770,7 +2034,7 @@ function registerTools$14(server, apiClient) {
|
|
|
1770
2034
|
}
|
|
1771
2035
|
//#endregion
|
|
1772
2036
|
//#region src/tools/economy.ts
|
|
1773
|
-
function registerTools$
|
|
2037
|
+
function registerTools$17(server, apiClient) {
|
|
1774
2038
|
registerTool(server, apiClient, "amba_currencies_create", "Create a virtual currency for a project. Currencies can be soft (earned through gameplay) or premium (purchased with real money). Supports auto-recharge for time-gated mechanics like hearts/energy.", {
|
|
1775
2039
|
project_id: z.string().describe("The project ID"),
|
|
1776
2040
|
code: z.string().describe("Unique currency code (e.g. \"gold\", \"gems\", \"hearts\")"),
|
|
@@ -1824,6 +2088,25 @@ function registerTools$13(server, apiClient) {
|
|
|
1824
2088
|
text: JSON.stringify(result, null, 2)
|
|
1825
2089
|
}] };
|
|
1826
2090
|
}, ["amba_grant_currency"]);
|
|
2091
|
+
registerTool(server, apiClient, "amba_currencies_spend", "Debit virtual currency from a specific user. Atomic — returns 400 INSUFFICIENT_FUNDS if the balance is too low (no partial debit). Use for in-app purchases priced in soft currency, gameplay consumes, or admin adjustments. The amount parameter is positive; the ledger records the spend as a negative delta automatically.", {
|
|
2092
|
+
project_id: z.string().describe("The project ID"),
|
|
2093
|
+
app_user_id: z.string().describe("The user to debit currency from"),
|
|
2094
|
+
currency_code: z.string().describe("Currency code (e.g. \"gold\")"),
|
|
2095
|
+
amount: z.number().describe("Positive amount to debit. The endpoint records this as a negative delta on the ledger."),
|
|
2096
|
+
reason: z.string().optional().describe("Reason for the spend (stored as reference_id, useful for the audit trail)")
|
|
2097
|
+
}, async ({ project_id, app_user_id, currency_code, amount, reason }, { client }) => {
|
|
2098
|
+
const payload = {
|
|
2099
|
+
app_user_id,
|
|
2100
|
+
currency_code,
|
|
2101
|
+
amount
|
|
2102
|
+
};
|
|
2103
|
+
if (reason !== void 0) payload.reason = reason;
|
|
2104
|
+
const result = await client.post(`/projects/${project_id}/currencies/spend`, payload);
|
|
2105
|
+
return { content: [{
|
|
2106
|
+
type: "text",
|
|
2107
|
+
text: JSON.stringify(result, null, 2)
|
|
2108
|
+
}] };
|
|
2109
|
+
});
|
|
1827
2110
|
registerTool(server, apiClient, "amba_currency_grant_rules_create", "Create a rule that automatically grants virtual currency when a specific event is tracked. Supports flat-amount grants and property-scaled grants (e.g. points per minute). Optional daily cap prevents farming.", {
|
|
1828
2111
|
project_id: z.string().describe("The project ID"),
|
|
1829
2112
|
currency_key: z.string().describe("Currency code to grant (e.g. \"calm_points\")"),
|
|
@@ -1978,6 +2261,23 @@ function registerTools$13(server, apiClient) {
|
|
|
1978
2261
|
text: JSON.stringify(result, null, 2)
|
|
1979
2262
|
}] };
|
|
1980
2263
|
}, ["amba_grant_item"]);
|
|
2264
|
+
registerTool(server, apiClient, "amba_inventory_revoke_item", "Remove a catalog item from a user's inventory — the inverse of grant. Decrements the held quantity; returns 400 INSUFFICIENT_QUANTITY if you try to revoke more than the user holds, and 404 if they don't hold the item at all. Revoking down to zero removes the holding entirely.", {
|
|
2265
|
+
project_id: z.string().describe("The project ID"),
|
|
2266
|
+
app_user_id: z.string().describe("The user to remove the item from"),
|
|
2267
|
+
catalog_item_id: z.string().describe("The catalog item ID to revoke"),
|
|
2268
|
+
quantity: z.number().optional().describe("Quantity to revoke (default 1)")
|
|
2269
|
+
}, async ({ project_id, app_user_id, catalog_item_id, quantity }, { client }) => {
|
|
2270
|
+
const payload = {
|
|
2271
|
+
app_user_id,
|
|
2272
|
+
catalog_item_id
|
|
2273
|
+
};
|
|
2274
|
+
if (quantity !== void 0) payload.quantity = quantity;
|
|
2275
|
+
const result = await client.post(`/projects/${project_id}/inventory/revoke`, payload);
|
|
2276
|
+
return { content: [{
|
|
2277
|
+
type: "text",
|
|
2278
|
+
text: JSON.stringify(result, null, 2)
|
|
2279
|
+
}] };
|
|
2280
|
+
});
|
|
1981
2281
|
registerTool(server, apiClient, "amba_users_get_inventory", "View a user's inventory including all owned items and quantities.", {
|
|
1982
2282
|
project_id: z.string().describe("The project ID"),
|
|
1983
2283
|
app_user_id: z.string().describe("The user ID to look up")
|
|
@@ -2266,7 +2566,7 @@ function registerTools$13(server, apiClient) {
|
|
|
2266
2566
|
}
|
|
2267
2567
|
//#endregion
|
|
2268
2568
|
//#region src/tools/leaderboards.ts
|
|
2269
|
-
function registerTools$
|
|
2569
|
+
function registerTools$16(server, apiClient) {
|
|
2270
2570
|
registerTool(server, apiClient, "amba_leaderboards_create", "Create a leaderboard definition. Leaderboards rank users by XP, streak length, or a custom metric. Supports all-time, daily, weekly, and monthly periods with configurable max entries.", {
|
|
2271
2571
|
project_id: z.string().describe("The project ID"),
|
|
2272
2572
|
name: z.string().describe("Leaderboard name (e.g. \"Top XP Earners\", \"Weekly Streak Leaders\")"),
|
|
@@ -2372,7 +2672,7 @@ function registerTools$12(server, apiClient) {
|
|
|
2372
2672
|
}
|
|
2373
2673
|
//#endregion
|
|
2374
2674
|
//#region src/tools/platform.ts
|
|
2375
|
-
function registerTools$
|
|
2675
|
+
function registerTools$15(server, apiClient) {
|
|
2376
2676
|
registerTool(server, apiClient, "amba_onboarding_create", "Create an onboarding flow for a project. Onboarding flows define step-by-step experiences for new users (e.g. welcome screens, permission prompts, feature tours).", {
|
|
2377
2677
|
project_id: z.string().describe("The project ID"),
|
|
2378
2678
|
name: z.string().describe("Flow name (e.g. \"Welcome Flow\", \"Premium Onboarding\")"),
|
|
@@ -2791,6 +3091,44 @@ function registerTools$11(server, apiClient) {
|
|
|
2791
3091
|
text: JSON.stringify(result, null, 2)
|
|
2792
3092
|
}] };
|
|
2793
3093
|
});
|
|
3094
|
+
registerTool(server, apiClient, "amba_messaging_create_conversation", "Create a conversation server-side. Unlike the client path, this allows ZERO or more initial participants — a matchmaker can create an empty circle then populate it over time. Returns the new conversation row.", {
|
|
3095
|
+
project_id: z.string().describe("The project ID"),
|
|
3096
|
+
type: z.enum(["direct", "group"]).optional().describe("Conversation type. Defaults to \"group\"."),
|
|
3097
|
+
name: z.string().optional().describe("Optional conversation name (group conversations)."),
|
|
3098
|
+
participant_ids: z.array(z.string()).optional().describe("Initial participant app_user IDs. May be empty or omitted.")
|
|
3099
|
+
}, async ({ project_id, type, name, participant_ids }, { client }) => {
|
|
3100
|
+
const payload = {};
|
|
3101
|
+
if (type !== void 0) payload.type = type;
|
|
3102
|
+
if (name !== void 0) payload.name = name;
|
|
3103
|
+
if (participant_ids !== void 0) payload.participant_ids = participant_ids;
|
|
3104
|
+
const result = await client.post(`/projects/${project_id}/messaging/conversations`, payload);
|
|
3105
|
+
return { content: [{
|
|
3106
|
+
type: "text",
|
|
3107
|
+
text: JSON.stringify(result, null, 2)
|
|
3108
|
+
}] };
|
|
3109
|
+
});
|
|
3110
|
+
registerTool(server, apiClient, "amba_messaging_add_participant", "Add a user to an existing conversation (idempotent). The matchmaker primitive: as each user qualifies for a circle, add them to its conversation so they can read, send, and receive messages. Re-adding an existing member is a no-op.", {
|
|
3111
|
+
project_id: z.string().describe("The project ID"),
|
|
3112
|
+
conversation_id: z.string().describe("The conversation ID"),
|
|
3113
|
+
user_id: z.string().describe("The app_user ID to add")
|
|
3114
|
+
}, async ({ project_id, conversation_id, user_id }, { client }) => {
|
|
3115
|
+
const result = await client.post(`/projects/${project_id}/messaging/conversations/${conversation_id}/participants`, { user_id });
|
|
3116
|
+
return { content: [{
|
|
3117
|
+
type: "text",
|
|
3118
|
+
text: JSON.stringify(result, null, 2)
|
|
3119
|
+
}] };
|
|
3120
|
+
});
|
|
3121
|
+
registerTool(server, apiClient, "amba_messaging_remove_participant", "Remove a user from a conversation (idempotent inverse of add). Removing a non-member returns removed: false.", {
|
|
3122
|
+
project_id: z.string().describe("The project ID"),
|
|
3123
|
+
conversation_id: z.string().describe("The conversation ID"),
|
|
3124
|
+
user_id: z.string().describe("The app_user ID to remove")
|
|
3125
|
+
}, async ({ project_id, conversation_id, user_id }, { client }) => {
|
|
3126
|
+
const result = await client.delete(`/projects/${project_id}/messaging/conversations/${conversation_id}/participants/${user_id}`);
|
|
3127
|
+
return { content: [{
|
|
3128
|
+
type: "text",
|
|
3129
|
+
text: JSON.stringify(result, null, 2)
|
|
3130
|
+
}] };
|
|
3131
|
+
});
|
|
2794
3132
|
registerTool(server, apiClient, "amba_messaging_list_conversations", "List message conversations for a project, newest first. Pagination via limit+offset.", {
|
|
2795
3133
|
project_id: z.string().describe("The project ID"),
|
|
2796
3134
|
limit: z.number().optional().describe("Max results to return (default 50)"),
|
|
@@ -2910,7 +3248,7 @@ function registerTools$11(server, apiClient) {
|
|
|
2910
3248
|
}
|
|
2911
3249
|
//#endregion
|
|
2912
3250
|
//#region src/tools/social.ts
|
|
2913
|
-
function registerTools$
|
|
3251
|
+
function registerTools$14(server, apiClient) {
|
|
2914
3252
|
registerTool(server, apiClient, "amba_friendships_get_stats", "Get friendship statistics for a project. Returns total friendships, pending requests, accepted friendships, and blocked count.", { project_id: z.string().describe("The project ID") }, async ({ project_id }, { client }) => {
|
|
2915
3253
|
const result = await client.get(`/projects/${project_id}/friends/stats`);
|
|
2916
3254
|
return { content: [{
|
|
@@ -3235,7 +3573,7 @@ function registerTools$10(server, apiClient) {
|
|
|
3235
3573
|
}
|
|
3236
3574
|
//#endregion
|
|
3237
3575
|
//#region src/tools/xp.ts
|
|
3238
|
-
function registerTools$
|
|
3576
|
+
function registerTools$13(server, apiClient) {
|
|
3239
3577
|
registerTool(server, apiClient, "amba_xp_rules_create", "Create an XP rule that auto-awards XP when a matching engagement event is tracked. For example, award 50 XP every time a user completes a workout, with optional daily caps and cooldowns.", {
|
|
3240
3578
|
project_id: z.string().describe("The project ID"),
|
|
3241
3579
|
name: z.string().describe("Rule name (e.g. \"Workout Completed\", \"Daily Login Bonus\")"),
|
|
@@ -3307,6 +3645,40 @@ function registerTools$9(server, apiClient) {
|
|
|
3307
3645
|
text: JSON.stringify(result, null, 2)
|
|
3308
3646
|
}] };
|
|
3309
3647
|
});
|
|
3648
|
+
registerTool(server, apiClient, "amba_xp_grant", "Directly add XP to a user's total — for rewards, promos, or support corrections. This is a manual adjustment: it does NOT replay rule side-effects (level-up events, achievement re-evaluation) the way event-driven XP does. The user's displayed level is still recomputed from the new total. The amount is a positive magnitude; the ledger records it as a positive delta.", {
|
|
3649
|
+
project_id: z.string().describe("The project ID"),
|
|
3650
|
+
app_user_id: z.string().describe("The user to grant XP to"),
|
|
3651
|
+
amount: z.number().describe("Positive amount of XP to add"),
|
|
3652
|
+
reason: z.string().optional().describe("Reason for the grant (stored on the ledger row for the audit trail)")
|
|
3653
|
+
}, async ({ project_id, app_user_id, amount, reason }, { client }) => {
|
|
3654
|
+
const payload = {
|
|
3655
|
+
app_user_id,
|
|
3656
|
+
amount
|
|
3657
|
+
};
|
|
3658
|
+
if (reason !== void 0) payload.reason = reason;
|
|
3659
|
+
const result = await client.post(`/projects/${project_id}/xp/grant`, payload);
|
|
3660
|
+
return { content: [{
|
|
3661
|
+
type: "text",
|
|
3662
|
+
text: JSON.stringify(result, null, 2)
|
|
3663
|
+
}] };
|
|
3664
|
+
});
|
|
3665
|
+
registerTool(server, apiClient, "amba_xp_deduct", "Directly subtract XP from a user's total — the inverse of grant. Atomic: returns 400 INSUFFICIENT_XP if the deduction would drive the total below zero (no partial deduct). Like grant, this is a manual adjustment that does NOT replay rule side-effects. The amount is a positive magnitude; the ledger records it as a negative delta.", {
|
|
3666
|
+
project_id: z.string().describe("The project ID"),
|
|
3667
|
+
app_user_id: z.string().describe("The user to deduct XP from"),
|
|
3668
|
+
amount: z.number().describe("Positive amount of XP to subtract"),
|
|
3669
|
+
reason: z.string().optional().describe("Reason for the deduction (stored on the ledger row for the audit trail)")
|
|
3670
|
+
}, async ({ project_id, app_user_id, amount, reason }, { client }) => {
|
|
3671
|
+
const payload = {
|
|
3672
|
+
app_user_id,
|
|
3673
|
+
amount
|
|
3674
|
+
};
|
|
3675
|
+
if (reason !== void 0) payload.reason = reason;
|
|
3676
|
+
const result = await client.post(`/projects/${project_id}/xp/deduct`, payload);
|
|
3677
|
+
return { content: [{
|
|
3678
|
+
type: "text",
|
|
3679
|
+
text: JSON.stringify(result, null, 2)
|
|
3680
|
+
}] };
|
|
3681
|
+
});
|
|
3310
3682
|
registerTool(server, apiClient, "amba_xp_list_users", "List users by total XP (leaderboard view). Ordered by total_xp descending with pagination.", {
|
|
3311
3683
|
project_id: z.string().describe("The project ID"),
|
|
3312
3684
|
limit: z.number().optional().describe("Max results to return (default 50)"),
|
|
@@ -3324,7 +3696,7 @@ function registerTools$9(server, apiClient) {
|
|
|
3324
3696
|
}
|
|
3325
3697
|
//#endregion
|
|
3326
3698
|
//#region src/tools/events.ts
|
|
3327
|
-
function registerTools$
|
|
3699
|
+
function registerTools$12(server, apiClient) {
|
|
3328
3700
|
registerTool(server, apiClient, "amba_events_list", "List engagement events for a project (most recent first) with cursor pagination on (occurred_at, id). Defaults to the last 24 hours when `since` is omitted. Pass `next_cursor` from a previous page back as `cursor` to continue.", {
|
|
3329
3701
|
project_id: z.string().describe("The project ID"),
|
|
3330
3702
|
since: z.string().optional().describe("ISO-8601 lower bound for occurred_at. Defaults to 24h ago."),
|
|
@@ -3366,6 +3738,125 @@ function registerTools$8(server, apiClient) {
|
|
|
3366
3738
|
});
|
|
3367
3739
|
}
|
|
3368
3740
|
//#endregion
|
|
3741
|
+
//#region src/tools/funnels.ts
|
|
3742
|
+
const funnelFilterSchema = z.object({
|
|
3743
|
+
property: z.string().describe("Dot-path into the event `properties` JSON (e.g. \"source\" or \"plan.tier\")."),
|
|
3744
|
+
op: z.enum([
|
|
3745
|
+
"eq",
|
|
3746
|
+
"neq",
|
|
3747
|
+
"gt",
|
|
3748
|
+
"gte",
|
|
3749
|
+
"lt",
|
|
3750
|
+
"lte",
|
|
3751
|
+
"contains",
|
|
3752
|
+
"not_contains",
|
|
3753
|
+
"exists",
|
|
3754
|
+
"not_exists"
|
|
3755
|
+
]).describe("Comparison operator. exists/not_exists ignore `value`."),
|
|
3756
|
+
value: z.union([
|
|
3757
|
+
z.string(),
|
|
3758
|
+
z.number(),
|
|
3759
|
+
z.boolean()
|
|
3760
|
+
]).optional().describe("Scalar to compare against. Required for every op except exists/not_exists.")
|
|
3761
|
+
});
|
|
3762
|
+
const funnelStepSchema = z.object({
|
|
3763
|
+
event: z.string().describe("Event name to match (engagement event_name)."),
|
|
3764
|
+
filters: z.array(funnelFilterSchema).optional().describe("Optional property predicates, ANDed together, that an event must satisfy.")
|
|
3765
|
+
});
|
|
3766
|
+
function registerTools$11(server, apiClient) {
|
|
3767
|
+
registerTool(server, apiClient, "amba_funnels_list", "List conversion funnels for a project. A funnel is a saved, ordered sequence of steps (event + optional property filters) plus a conversion window. Returns the definitions only — use amba_funnels_query to compute per-step counts.", { project_id: z.string().describe("The project ID") }, async ({ project_id }, { client }) => {
|
|
3768
|
+
const result = await client.get(`/projects/${project_id}/funnels`);
|
|
3769
|
+
return { content: [{
|
|
3770
|
+
type: "text",
|
|
3771
|
+
text: JSON.stringify(result, null, 2)
|
|
3772
|
+
}] };
|
|
3773
|
+
});
|
|
3774
|
+
registerTool(server, apiClient, "amba_funnels_create", [
|
|
3775
|
+
"Create a conversion funnel definition. Steps are ORDERED: a user is credited with",
|
|
3776
|
+
"step N only if they emitted step N after their matched step N-1, all within the",
|
|
3777
|
+
"conversion window measured from their first step-0 event. conversion_window_seconds",
|
|
3778
|
+
"defaults to 604800 (7 days). Needs at least 2 steps."
|
|
3779
|
+
].join(" "), {
|
|
3780
|
+
project_id: z.string().describe("The project ID"),
|
|
3781
|
+
name: z.string().describe("Funnel name (unique per project)."),
|
|
3782
|
+
description: z.string().optional().describe("Human-readable description."),
|
|
3783
|
+
steps: z.array(funnelStepSchema).describe("Ordered steps (2-10). Each step is an event + optional property filters."),
|
|
3784
|
+
conversion_window_seconds: z.number().int().optional().describe("Conversion window in seconds (60 .. 7776000). Defaults to 604800 (7 days).")
|
|
3785
|
+
}, async ({ project_id, name, description, steps, conversion_window_seconds }, { client }) => {
|
|
3786
|
+
const payload = {
|
|
3787
|
+
name,
|
|
3788
|
+
steps
|
|
3789
|
+
};
|
|
3790
|
+
if (description !== void 0) payload.description = description;
|
|
3791
|
+
if (conversion_window_seconds !== void 0) payload.conversion_window_seconds = conversion_window_seconds;
|
|
3792
|
+
const result = await client.post(`/projects/${project_id}/funnels`, payload);
|
|
3793
|
+
return { content: [{
|
|
3794
|
+
type: "text",
|
|
3795
|
+
text: JSON.stringify(result, null, 2)
|
|
3796
|
+
}] };
|
|
3797
|
+
});
|
|
3798
|
+
registerTool(server, apiClient, "amba_funnels_get", "Get a single funnel definition by id (name, ordered steps, conversion window).", {
|
|
3799
|
+
project_id: z.string().describe("The project ID"),
|
|
3800
|
+
funnel_id: z.string().describe("The funnel ID")
|
|
3801
|
+
}, async ({ project_id, funnel_id }, { client }) => {
|
|
3802
|
+
const result = await client.get(`/projects/${project_id}/funnels/${funnel_id}`);
|
|
3803
|
+
return { content: [{
|
|
3804
|
+
type: "text",
|
|
3805
|
+
text: JSON.stringify(result, null, 2)
|
|
3806
|
+
}] };
|
|
3807
|
+
});
|
|
3808
|
+
registerTool(server, apiClient, "amba_funnels_update", "Update a funnel's name, description, steps, and/or conversion window. Only the provided fields change; steps (when supplied) replace the whole ordered list.", {
|
|
3809
|
+
project_id: z.string().describe("The project ID"),
|
|
3810
|
+
funnel_id: z.string().describe("The funnel ID"),
|
|
3811
|
+
name: z.string().optional().describe("New funnel name."),
|
|
3812
|
+
description: z.string().nullable().optional().describe("New description; pass null to clear."),
|
|
3813
|
+
steps: z.array(funnelStepSchema).optional().describe("Replacement ordered step list (2-10)."),
|
|
3814
|
+
conversion_window_seconds: z.number().int().optional().describe("New conversion window in seconds (60 .. 7776000).")
|
|
3815
|
+
}, async ({ project_id, funnel_id, name, description, steps, conversion_window_seconds }, { client }) => {
|
|
3816
|
+
const payload = {};
|
|
3817
|
+
if (name !== void 0) payload.name = name;
|
|
3818
|
+
if (description !== void 0) payload.description = description;
|
|
3819
|
+
if (steps !== void 0) payload.steps = steps;
|
|
3820
|
+
if (conversion_window_seconds !== void 0) payload.conversion_window_seconds = conversion_window_seconds;
|
|
3821
|
+
const result = await client.patch(`/projects/${project_id}/funnels/${funnel_id}`, payload);
|
|
3822
|
+
return { content: [{
|
|
3823
|
+
type: "text",
|
|
3824
|
+
text: JSON.stringify(result, null, 2)
|
|
3825
|
+
}] };
|
|
3826
|
+
});
|
|
3827
|
+
registerTool(server, apiClient, "amba_funnels_delete", "Delete a funnel definition by id.", {
|
|
3828
|
+
project_id: z.string().describe("The project ID"),
|
|
3829
|
+
funnel_id: z.string().describe("The funnel ID")
|
|
3830
|
+
}, async ({ project_id, funnel_id }, { client }) => {
|
|
3831
|
+
const result = await client.delete(`/projects/${project_id}/funnels/${funnel_id}`);
|
|
3832
|
+
return { content: [{
|
|
3833
|
+
type: "text",
|
|
3834
|
+
text: JSON.stringify(result, null, 2)
|
|
3835
|
+
}] };
|
|
3836
|
+
});
|
|
3837
|
+
registerTool(server, apiClient, "amba_funnels_query", [
|
|
3838
|
+
"Compute a funnel: per-step distinct-user counts, conversion rates, and drop-off over",
|
|
3839
|
+
"an entry date range. `from`/`to` (ISO-8601) bound which step-0 events count as funnel",
|
|
3840
|
+
"entries (default: last 30 days). Each entrant then has their own conversion window in",
|
|
3841
|
+
"which later steps must occur (it may extend past `to`). Returns total_entered,",
|
|
3842
|
+
"total_converted, overall_conversion_rate, and a per-step breakdown."
|
|
3843
|
+
].join(" "), {
|
|
3844
|
+
project_id: z.string().describe("The project ID"),
|
|
3845
|
+
funnel_id: z.string().describe("The funnel ID"),
|
|
3846
|
+
from: z.string().optional().describe("ISO-8601 lower bound for funnel entries. Defaults to 30 days ago."),
|
|
3847
|
+
to: z.string().optional().describe("ISO-8601 upper bound for funnel entries. Defaults to now.")
|
|
3848
|
+
}, async ({ project_id, funnel_id, from, to }, { client }) => {
|
|
3849
|
+
const query = {};
|
|
3850
|
+
if (from !== void 0) query.from = from;
|
|
3851
|
+
if (to !== void 0) query.to = to;
|
|
3852
|
+
const result = await client.get(`/projects/${project_id}/funnels/${funnel_id}/query`, query);
|
|
3853
|
+
return { content: [{
|
|
3854
|
+
type: "text",
|
|
3855
|
+
text: JSON.stringify(result, null, 2)
|
|
3856
|
+
}] };
|
|
3857
|
+
});
|
|
3858
|
+
}
|
|
3859
|
+
//#endregion
|
|
3369
3860
|
//#region src/lib/tool-result.ts
|
|
3370
3861
|
/**
|
|
3371
3862
|
* Shared MCP tool result helpers.
|
|
@@ -3538,7 +4029,7 @@ function enrichedAuthResult(result, agentInstructions) {
|
|
|
3538
4029
|
status: result.status
|
|
3539
4030
|
});
|
|
3540
4031
|
}
|
|
3541
|
-
function registerTools$
|
|
4032
|
+
function registerTools$10(server, apiClient) {
|
|
3542
4033
|
registerPublicTool(server, "amba_developer_signup", [
|
|
3543
4034
|
"Create a new Amba developer account. Returns a long-lived Personal Access Token (PAT)",
|
|
3544
4035
|
"plus a real isolated Amba project (provisioning asynchronously), plus ready-to-paste",
|
|
@@ -3735,7 +4226,7 @@ const sdkOrderSchema = z.array(z.object({
|
|
|
3735
4226
|
})).describe("Ordering. `[{column: \"created_at\", direction: \"desc\"}, ...]`.");
|
|
3736
4227
|
const adminFindWhereSchema = z.unknown().describe("Server WhereClause shape: column-keyed FieldOps, e.g. {tag: {eq: \"x\"}, count: {gte: 5}} plus optional `and|or|not` combinators.");
|
|
3737
4228
|
const setSchema = z.record(z.unknown()).describe("Column-value map. Server-managed columns (id, created_at, etc.) are rejected.");
|
|
3738
|
-
function registerTools$
|
|
4229
|
+
function registerTools$9(server, apiClient) {
|
|
3739
4230
|
registerTool(server, apiClient, "amba_collections_create", [
|
|
3740
4231
|
"Create a new collection (schema-first Postgres table) in a project.",
|
|
3741
4232
|
"The DDL is emitted server-side and applied via a Temporal saga.",
|
|
@@ -3746,13 +4237,15 @@ function registerTools$6(server, apiClient) {
|
|
|
3746
4237
|
project_id: z.string().describe("The project ID owning the collection."),
|
|
3747
4238
|
name: z.string().describe("Collection name. Must match /^[a-z][a-z0-9_]*$/. Reserved prefixes (amba_, coll_amba_) are rejected."),
|
|
3748
4239
|
columns: z.array(columnSchema).describe("Column definitions. Server adds id/user_id/created_at/updated_at/deleted_at."),
|
|
3749
|
-
indexes: z.array(indexSchema).optional().describe("Optional extra indexes (server creates a primary key + user_id index by default).")
|
|
3750
|
-
|
|
4240
|
+
indexes: z.array(indexSchema).optional().describe("Optional extra indexes (server creates a primary key + user_id index by default)."),
|
|
4241
|
+
shared: z.boolean().optional().describe("Set true for developer-seeded GLOBAL content not owned by any single user (question banks, content libraries, lookup tables). Makes user_id NULLABLE so rows can be inserted without a user_id. Defaults to false (every row requires a user_id).")
|
|
4242
|
+
}, async ({ project_id, name, columns, indexes, shared }, { pat }) => {
|
|
3751
4243
|
const body = {
|
|
3752
4244
|
name,
|
|
3753
4245
|
columns
|
|
3754
4246
|
};
|
|
3755
4247
|
if (indexes !== void 0) body.indexes = indexes;
|
|
4248
|
+
if (shared !== void 0) body.shared = shared;
|
|
3756
4249
|
return passthroughResult(await adminFetch(apiClient, {
|
|
3757
4250
|
method: "POST",
|
|
3758
4251
|
path: `/projects/${project_id}/collections`,
|
|
@@ -3810,22 +4303,39 @@ function registerTools$6(server, apiClient) {
|
|
|
3810
4303
|
}));
|
|
3811
4304
|
}, ["amba_delete_collection"]);
|
|
3812
4305
|
registerTool(server, apiClient, "amba_collections_alter", [
|
|
3813
|
-
"Alter a collection: exactly one of `add_column`, `add_index`,
|
|
4306
|
+
"Alter a collection: exactly one of `add_column`, `add_index`, `change_type`, `rename_column`, `drop_column`, or `relax_user_id` per call (the underlying saga is built around one SQL blob per workflow).",
|
|
3814
4307
|
"Drop-column is destructive — requires `confirm` to equal the dropped column name.",
|
|
4308
|
+
"change_type changes an existing column to a new type. It REWRITES THE WHOLE TABLE under an exclusive lock — the collection is briefly unreadable/unwritable while it runs, so prefer low-traffic windows for large collections. Casts that are not trivially safe (e.g. text→integer) require a `using` cast expression; known-safe casts (anything→text, integer→bigint, integer/bigint→numeric) do not. Without a `using` for an unsafe cast you get a 400 up front rather than a mid-migration failure.",
|
|
4309
|
+
"rename_column renames a column (metadata-only, fast). It BREAKS any app code referencing the old name — re-run `amba types generate` and update call sites afterward. The new name can’t collide with a server-managed column (id, user_id, created_at, updated_at, deleted_at) or a reserved query keyword (and, or, not).",
|
|
4310
|
+
"relax_user_id converts an EXISTING collection to shared (drops the user_id NOT NULL so global rows can be inserted) — the post-hoc equivalent of creating it with shared:true.",
|
|
3815
4311
|
"Authenticates as the developer/agent — pass `pat` or send as inbound Bearer.",
|
|
3816
|
-
"Errors: 400 INVALID_PATCH if not exactly one operation, 400 CONFIRMATION_REQUIRED on drop_column without matching confirm."
|
|
4312
|
+
"Errors: 400 INVALID_PATCH if not exactly one operation, 400 CONFIRMATION_REQUIRED on drop_column without matching confirm, 400 INVALID_COLLECTION_SCHEMA on an unsafe change_type cast or a reserved rename target, 400 COLLECTION_MIGRATION_FAILED if the cast fails at apply time."
|
|
3817
4313
|
].join(" "), {
|
|
3818
4314
|
project_id: z.string().describe("The project ID."),
|
|
3819
4315
|
name: z.string().describe("Collection name to alter."),
|
|
3820
|
-
add_column: columnSchema.optional().describe("Column to add. Mutually exclusive with
|
|
3821
|
-
add_index: indexSchema.optional().describe("Index to add. Mutually exclusive with
|
|
4316
|
+
add_column: columnSchema.optional().describe("Column to add. Mutually exclusive with the other alter operations."),
|
|
4317
|
+
add_index: indexSchema.optional().describe("Index to add. Mutually exclusive with the other alter operations."),
|
|
4318
|
+
change_type: z.object({
|
|
4319
|
+
column: z.string().describe("Existing column to change."),
|
|
4320
|
+
to_type: z.string().describe("Target type (text, integer, bigint, numeric, boolean, timestamptz, date, jsonb, uuid)."),
|
|
4321
|
+
from_type: z.string().optional().describe("Optional current type. When provided, known-safe widening casts (integer→bigint, integer/bigint→numeric) skip the `using` requirement."),
|
|
4322
|
+
using: z.string().optional().describe("SQL cast expression for the USING clause, e.g. \"score::integer\". Required for casts that are not trivially safe; omit only for anything→text or a known-safe widening cast.")
|
|
4323
|
+
}).optional().describe("Change a column’s type. REWRITES THE WHOLE TABLE under an exclusive lock (briefly unreadable/unwritable). Mutually exclusive with the other alter operations."),
|
|
4324
|
+
rename_column: z.object({
|
|
4325
|
+
from: z.string().describe("Current column name."),
|
|
4326
|
+
to: z.string().describe("New column name. Must match /^[a-z][a-z0-9_]*$/.")
|
|
4327
|
+
}).optional().describe("Rename a column (metadata-only, fast). BREAKS code referencing the old name — re-run `amba types generate` afterward. Mutually exclusive with the other alter operations."),
|
|
3822
4328
|
drop_column: z.string().optional().describe("Column name to drop. Requires `confirm` query param to match."),
|
|
4329
|
+
relax_user_id: z.boolean().optional().describe("Set true to drop the user_id NOT NULL constraint so this collection can hold shared/global rows (NULL user_id). Mutually exclusive with the other operations."),
|
|
3823
4330
|
confirm: z.string().optional().describe("Required when drop_column is set; must equal the column name.")
|
|
3824
|
-
}, async ({ project_id, name, add_column, add_index, drop_column, confirm }, { pat }) => {
|
|
4331
|
+
}, async ({ project_id, name, add_column, add_index, change_type, rename_column, drop_column, relax_user_id, confirm }, { pat }) => {
|
|
3825
4332
|
const body = {};
|
|
3826
4333
|
if (add_column !== void 0) body.add_column = add_column;
|
|
3827
4334
|
if (add_index !== void 0) body.add_index = add_index;
|
|
4335
|
+
if (change_type !== void 0) body.change_type = change_type;
|
|
4336
|
+
if (rename_column !== void 0) body.rename_column = rename_column;
|
|
3828
4337
|
if (drop_column !== void 0) body.drop_column = drop_column;
|
|
4338
|
+
if (relax_user_id !== void 0) body.relax_user_id = relax_user_id;
|
|
3829
4339
|
const query = {};
|
|
3830
4340
|
if (drop_column !== void 0 && confirm !== void 0) query.confirm = confirm;
|
|
3831
4341
|
return passthroughResult(await adminFetch(apiClient, {
|
|
@@ -3853,6 +4363,28 @@ function registerTools$6(server, apiClient) {
|
|
|
3853
4363
|
bearer: pat
|
|
3854
4364
|
}));
|
|
3855
4365
|
});
|
|
4366
|
+
registerTool(server, apiClient, "amba_admin_insert_rows", [
|
|
4367
|
+
"Bulk-insert up to 500 rows into a collection in a single atomic statement — BYPASSES auto-RLS.",
|
|
4368
|
+
"Use for developer-seeded data (question banks, content libraries, lookup tables); far faster than calling amba_admin_insert_row in a loop.",
|
|
4369
|
+
"Server-managed columns (id, created_at, updated_at, deleted_at) are stripped per row; `user_id` is honored if present. Rows may be sparse — a column absent from a row is inserted as NULL.",
|
|
4370
|
+
"on_conflict \"error\" (default) aborts the whole batch on any conflict; \"skip\" emits ON CONFLICT DO NOTHING and reports how many rows were skipped.",
|
|
4371
|
+
"Authenticates as the developer/agent — pass `pat` or send as inbound Bearer.",
|
|
4372
|
+
"Errors: 400 BATCH_TOO_LARGE (>500) / INVALID_COLUMN / NOT_NULL_VIOLATION, 404 COLLECTION_NOT_FOUND, 409 CONFLICT (on_conflict:\"error\")."
|
|
4373
|
+
].join(" "), {
|
|
4374
|
+
project_id: z.string().describe("The project ID."),
|
|
4375
|
+
name: z.string().describe("Collection name."),
|
|
4376
|
+
rows: z.array(z.record(z.unknown())).min(1).max(500).describe("1–500 column-value maps. Server-managed columns are stripped per row."),
|
|
4377
|
+
on_conflict: z.enum(["error", "skip"]).optional().describe("Conflict policy. \"error\" (default) fails the batch; \"skip\" ignores conflicts.")
|
|
4378
|
+
}, async ({ project_id, name, rows, on_conflict }, { pat }) => {
|
|
4379
|
+
const body = { rows };
|
|
4380
|
+
if (on_conflict !== void 0) body.on_conflict = on_conflict;
|
|
4381
|
+
return passthroughResult(await adminFetch(apiClient, {
|
|
4382
|
+
method: "POST",
|
|
4383
|
+
path: `/projects/${project_id}/collections/${encodeURIComponent(name)}/rows:batch`,
|
|
4384
|
+
body,
|
|
4385
|
+
bearer: pat
|
|
4386
|
+
}));
|
|
4387
|
+
});
|
|
3856
4388
|
registerTool(server, apiClient, "amba_admin_list_rows", [
|
|
3857
4389
|
"List rows in a collection from the admin side. BYPASSES auto-RLS — every row in the collection is visible.",
|
|
3858
4390
|
"Default behavior includes soft-deleted rows (devs debugging \"where did the row go\" need them); pass `include_deleted=false` to hide them.",
|
|
@@ -4232,7 +4764,7 @@ function jsonResult(payload) {
|
|
|
4232
4764
|
* of a generic upstream 400.
|
|
4233
4765
|
*/
|
|
4234
4766
|
const MAX_BUNDLE_BYTES = 10 * 1024 * 1024;
|
|
4235
|
-
function registerTools$
|
|
4767
|
+
function registerTools$8(server, apiClient) {
|
|
4236
4768
|
registerTool(server, apiClient, "amba_functions_deploy", [
|
|
4237
4769
|
"Deploy a function to an Amba project. The supplied `code` is a fully bundled JavaScript module (one entry file) — the deploy endpoint uploads it server-side and records a new active deployment row, superseding any prior active version for the same `name`.",
|
|
4238
4770
|
"Function name must match /^[a-z][a-z0-9_-]*$/ and be ≤58 chars. Bundle size cap: 10 MiB (enforced client-side too — oversize uploads are rejected before the round-trip).",
|
|
@@ -4357,7 +4889,7 @@ function registerTools$5(server, apiClient) {
|
|
|
4357
4889
|
}
|
|
4358
4890
|
//#endregion
|
|
4359
4891
|
//#region src/tools/sites.ts
|
|
4360
|
-
function registerTools$
|
|
4892
|
+
function registerTools$7(server, apiClient) {
|
|
4361
4893
|
registerTool(server, apiClient, "amba_sites_deploy", [
|
|
4362
4894
|
"Deploy a static site to an Amba project. Creates the site if it does not yet exist, then uploads the supplied files as a new deployment.",
|
|
4363
4895
|
"The deployment is immutable — its files live forever under the site's storage prefix — and the site flips to serve the new deployment atomically. Older deployments are kept for rollback via `amba_sites_list`.",
|
|
@@ -4448,22 +4980,81 @@ function registerTools$4(server, apiClient) {
|
|
|
4448
4980
|
}, ["amba_remove_site_domain"]);
|
|
4449
4981
|
}
|
|
4450
4982
|
//#endregion
|
|
4983
|
+
//#region src/tools/domains.ts
|
|
4984
|
+
function registerTools$6(server, apiClient) {
|
|
4985
|
+
registerTool(server, apiClient, "amba_domains_search", ["Search for available domains to buy through Amba. Pass a keyword or a full domain; returns candidate domains with availability and first-year price (USD).", "Free — searching never costs anything. Use this to help a user pick a name, then `amba_domains_check` to price specific picks and `amba_domains_purchase` to buy."].join(" "), {
|
|
4986
|
+
project_id: z.string().describe("The Amba project ID."),
|
|
4987
|
+
query: z.string().describe("A keyword (e.g. \"getunbury\") or a full domain (e.g. \"unbury.com\")."),
|
|
4988
|
+
limit: z.number().int().min(1).max(50).optional().describe("Max suggestions (1-50, default 20).")
|
|
4989
|
+
}, async ({ project_id, query, limit }, { pat }) => {
|
|
4990
|
+
const q = { q: query };
|
|
4991
|
+
if (limit !== void 0) q.limit = String(limit);
|
|
4992
|
+
return jsonResult(await callWithPat(apiClient, pat, "GET", `/projects/${encodeURIComponent(project_id)}/domains/search`, { query: q }));
|
|
4993
|
+
});
|
|
4994
|
+
registerTool(server, apiClient, "amba_domains_check", ["Check authoritative availability + price for specific domains (up to 20 at once). Free.", "Returns, per domain: whether it is available, the first-year price (USD), whether it is a premium name, and whether Amba can register that extension."].join(" "), {
|
|
4995
|
+
project_id: z.string().describe("The Amba project ID."),
|
|
4996
|
+
domains: z.array(z.string()).min(1).max(20).describe("Domains to check, e.g. [\"unbury.com\", \"unbury.app\"]. Lowercased at the boundary.")
|
|
4997
|
+
}, async ({ project_id, domains }, { pat }) => {
|
|
4998
|
+
const normalised = domains.map((d) => d.toLowerCase());
|
|
4999
|
+
return jsonResult(await callWithPat(apiClient, pat, "POST", `/projects/${encodeURIComponent(project_id)}/domains/check`, { body: { domains: normalised } }));
|
|
5000
|
+
});
|
|
5001
|
+
registerTool(server, apiClient, "amba_domains_purchase", [
|
|
5002
|
+
"Buy a domain through Amba and connect it to one of the project's sites as a live custom domain — the customer never touches DNS.",
|
|
5003
|
+
"TWO-STEP / MONEY SAFETY: the first call (without `confirm`) returns a QUOTE with the price and `confirmation_required: true` and charges nothing. Surface the price to the user and get their go-ahead, then call again with `confirm: true` and `accept_price_usd` set to the quoted price to execute the purchase.",
|
|
5004
|
+
"A real domain registration spends money and is permanent. If purchasing is not enabled on the deployment, the call returns the quote with `gated: true` and still charges nothing.",
|
|
5005
|
+
"On success: the domain is registered, DNS is auto-configured, and the site begins serving on the new domain (the TLS certificate issues automatically)."
|
|
5006
|
+
].join(" "), {
|
|
5007
|
+
project_id: z.string().describe("The Amba project ID."),
|
|
5008
|
+
domain: z.string().describe("The domain to buy, e.g. \"unbury.com\". Must be available (check first)."),
|
|
5009
|
+
site: z.string().describe("The site name to connect the domain to (from amba_sites_list)."),
|
|
5010
|
+
accept_price_usd: z.number().optional().describe("Required to execute: the price (USD) you were quoted. Omit on the first (quote) call."),
|
|
5011
|
+
confirm: z.boolean().optional().describe("Set true ONLY after the user has approved the cost. Omit on the first (quote) call."),
|
|
5012
|
+
auto_renew: z.boolean().optional().describe("Auto-renew the domain each year (default true)."),
|
|
5013
|
+
privacy: z.boolean().optional().describe("Enable WHOIS privacy (default true)."),
|
|
5014
|
+
years: z.number().int().min(1).max(10).optional().describe("Registration length in years (default 1).")
|
|
5015
|
+
}, async ({ project_id, domain, site, accept_price_usd, confirm, auto_renew, privacy, years }, { pat }) => {
|
|
5016
|
+
const body = {
|
|
5017
|
+
domain: domain.toLowerCase(),
|
|
5018
|
+
site
|
|
5019
|
+
};
|
|
5020
|
+
if (accept_price_usd !== void 0) body.accept_price_usd = accept_price_usd;
|
|
5021
|
+
if (confirm !== void 0) body.confirm = confirm;
|
|
5022
|
+
if (auto_renew !== void 0) body.auto_renew = auto_renew;
|
|
5023
|
+
if (privacy !== void 0) body.privacy = privacy;
|
|
5024
|
+
if (years !== void 0) body.years = years;
|
|
5025
|
+
return jsonResult(await callWithPat(apiClient, pat, "POST", `/projects/${encodeURIComponent(project_id)}/domains/purchase`, { body }));
|
|
5026
|
+
});
|
|
5027
|
+
registerTool(server, apiClient, "amba_domains_list", "List domains this project has purchased through Amba, with registration status and the site each is connected to.", {
|
|
5028
|
+
project_id: z.string().describe("The Amba project ID."),
|
|
5029
|
+
limit: z.number().int().min(1).max(200).optional().describe("Page size (1-200, default 50)."),
|
|
5030
|
+
offset: z.number().int().min(0).optional().describe("Skip rows (default 0).")
|
|
5031
|
+
}, async ({ project_id, limit, offset }, { pat }) => {
|
|
5032
|
+
const query = {};
|
|
5033
|
+
if (limit !== void 0) query.limit = String(limit);
|
|
5034
|
+
if (offset !== void 0) query.offset = String(offset);
|
|
5035
|
+
return jsonResult(await callWithPat(apiClient, pat, "GET", `/projects/${encodeURIComponent(project_id)}/domains`, { query }));
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
//#endregion
|
|
4451
5039
|
//#region src/tools/secrets.ts
|
|
4452
|
-
function registerTools$
|
|
5040
|
+
function registerTools$5(server, apiClient) {
|
|
4453
5041
|
registerTool(server, apiClient, "amba_secrets_set", [
|
|
4454
|
-
"Set or rotate a function-
|
|
4455
|
-
"Writes a new version to the backing secret store and enqueues
|
|
4456
|
-
"Secret name must match /^[A-Z][A-Z0-9_]{0,62}$/. Function name must match /^[a-z][a-z0-9_-]{0,57}$/.",
|
|
5042
|
+
"Set or rotate a secret. Omit `function` for a PROJECT-WIDE secret (one value visible to every function in the project, including ones deployed later) — set it once instead of repeating per function. Pass `function` to scope the secret to just that function.",
|
|
5043
|
+
"Writes a new version to the backing secret store and enqueues the sync. Subsequent function invocations see the new value once sync_status flips to \"synced\" (typically <30s).",
|
|
5044
|
+
"Secret name must match /^[A-Z][A-Z0-9_]{0,62}$/. Function name (when given) must match /^[a-z][a-z0-9_-]{0,57}$/.",
|
|
4457
5045
|
"Reserved binding names (AMBA_INTERNAL_TOKEN, EDGE_HEADER_SIGNING_SECRET, STORAGE, EDGE_DB_PROXY, AMBA_API_URL, AMBA_AI_GATEWAY_URL, AMBA_PROJECT_ID) are platform-managed and rejected with 400 RESERVED_BINDING.",
|
|
4458
|
-
"
|
|
5046
|
+
"Secrets may be set BEFORE deploying the function — the sync drains once a deployment lands, so you can set every secret first and deploy once.",
|
|
4459
5047
|
"Value cap: 64 KiB. Plaintext is never returned afterwards."
|
|
4460
5048
|
].join(" "), {
|
|
4461
5049
|
project_id: z.string().describe("The Amba project ID."),
|
|
4462
5050
|
name: z.string().describe("Secret name. UPPER_SNAKE_CASE. /^[A-Z][A-Z0-9_]{0,62}$/."),
|
|
4463
5051
|
value: z.string().describe("Secret value. Stored encrypted at rest; never returned in plaintext afterwards."),
|
|
4464
|
-
function: z.string().describe("Function
|
|
5052
|
+
function: z.string().optional().describe("Function this secret binds to. OMIT for a project-wide secret (applied to every function in the project).")
|
|
4465
5053
|
}, async ({ project_id, name, value, function: functionName }, { pat }) => {
|
|
4466
|
-
return jsonResult(await callWithPat(apiClient, pat, "POST", `/projects/${encodeURIComponent(project_id)}/secrets`, { body: {
|
|
5054
|
+
return jsonResult(await callWithPat(apiClient, pat, "POST", `/projects/${encodeURIComponent(project_id)}/secrets`, { body: functionName === void 0 ? {
|
|
5055
|
+
name,
|
|
5056
|
+
value
|
|
5057
|
+
} : {
|
|
4467
5058
|
name,
|
|
4468
5059
|
value,
|
|
4469
5060
|
function: functionName
|
|
@@ -4488,25 +5079,26 @@ function registerTools$3(server, apiClient) {
|
|
|
4488
5079
|
return jsonResult(await callWithPat(apiClient, pat, "GET", `/projects/${encodeURIComponent(project_id)}/secrets`));
|
|
4489
5080
|
}, ["amba_list_secrets"]);
|
|
4490
5081
|
registerTool(server, apiClient, "amba_secrets_delete", [
|
|
4491
|
-
"Delete a
|
|
4492
|
-
"
|
|
5082
|
+
"Delete a secret. Removes the version from the backing store AND the sync-pending row. The Worker-side binding is reaped on the next deploy of the affected function(s) (the binding list is re-emitted without the secret).",
|
|
5083
|
+
"Pass `function` to delete a function-scoped secret; OMIT it to delete the project-wide secret of this name.",
|
|
4493
5084
|
"Idempotent: deleting a secret that no longer exists succeeds."
|
|
4494
5085
|
].join(" "), {
|
|
4495
5086
|
project_id: z.string().describe("The Amba project ID."),
|
|
4496
5087
|
name: z.string().describe("Secret name. /^[A-Z][A-Z0-9_]{0,62}$/."),
|
|
4497
|
-
function: z.string().describe("Function
|
|
5088
|
+
function: z.string().optional().describe("Function this secret was bound to. OMIT to delete the project-wide secret.")
|
|
4498
5089
|
}, async ({ project_id, name, function: functionName }, { pat }) => {
|
|
5090
|
+
const scope = functionName ?? null;
|
|
4499
5091
|
try {
|
|
4500
|
-
return jsonResult(await callWithPat(apiClient, pat, "DELETE", `/projects/${encodeURIComponent(project_id)}/secrets/${encodeURIComponent(name)}`, { query: { function: functionName } }) ?? {
|
|
5092
|
+
return jsonResult(await callWithPat(apiClient, pat, "DELETE", `/projects/${encodeURIComponent(project_id)}/secrets/${encodeURIComponent(name)}`, functionName === void 0 ? {} : { query: { function: functionName } }) ?? {
|
|
4501
5093
|
deleted: true,
|
|
4502
5094
|
name,
|
|
4503
|
-
function:
|
|
5095
|
+
function: scope
|
|
4504
5096
|
});
|
|
4505
5097
|
} catch (err) {
|
|
4506
5098
|
if (err instanceof AmbaApiError && err.status === 404) return jsonResult({
|
|
4507
5099
|
deleted: true,
|
|
4508
5100
|
name,
|
|
4509
|
-
function:
|
|
5101
|
+
function: scope,
|
|
4510
5102
|
already_absent: true
|
|
4511
5103
|
});
|
|
4512
5104
|
throw err;
|
|
@@ -4521,7 +5113,7 @@ const RATE_LIMIT_SHAPE = z.object({
|
|
|
4521
5113
|
max: z.number().int().min(1).optional().describe("Max invocations per window. Defaults to 20."),
|
|
4522
5114
|
key: z.string().optional().describe("Bucketing key. Typically \"user_id\" or \"session_id\". Defaults to \"user_id\".")
|
|
4523
5115
|
}).strict();
|
|
4524
|
-
function registerTools$
|
|
5116
|
+
function registerTools$4(server, apiClient) {
|
|
4525
5117
|
registerTool(server, apiClient, "amba_ai_prompts_create", [
|
|
4526
5118
|
"Register a new AI prompt template on a project. Returns the persisted prompt row (name, version, provider, model, client_invokable).",
|
|
4527
5119
|
"Name must match /^[a-z][a-z0-9_-]{0,127}$/. Provider must be \"anthropic\" or \"openai\" and must already be registered (POST /ai/providers — currently CLI / console). Model is the provider-native model id (e.g. \"claude-sonnet-4-5-20250929\", \"gpt-4o-2024-08-06\").",
|
|
@@ -4723,7 +5315,7 @@ const TIER_CATALOG = {
|
|
|
4723
5315
|
note: "Use Amba.track(name, props, { telemetry: true }) for high-volume telemetry. Cheaper, no fan-out to segments/workflows/push."
|
|
4724
5316
|
}
|
|
4725
5317
|
};
|
|
4726
|
-
function registerTools$
|
|
5318
|
+
function registerTools$3(server, apiClient) {
|
|
4727
5319
|
registerTool(server, apiClient, "amba_billing_status", "Get the current billing tier, headroom on each metered axis (MAU, events, push, db_storage, media_storage), projected overage for the current period, spend ceiling, and what (if any) human action is required before the agent can continue safely. Use this BEFORE provisioning new features or running data-heavy workloads so the agent can self-throttle or escalate to the human.", { project_id: z.string().describe("The project ID") }, async ({ project_id }, { client }) => {
|
|
4728
5320
|
const result = await client.get(`/projects/${project_id}/billing/status`);
|
|
4729
5321
|
return { content: [{
|
|
@@ -4750,7 +5342,7 @@ function registerTools$1(server, apiClient) {
|
|
|
4750
5342
|
}
|
|
4751
5343
|
//#endregion
|
|
4752
5344
|
//#region src/tools/invites.ts
|
|
4753
|
-
function registerTools(server, apiClient) {
|
|
5345
|
+
function registerTools$2(server, apiClient) {
|
|
4754
5346
|
registerTool(server, apiClient, "amba_projects_invite_member", "Invite a teammate by email to collaborate on this project. Returns the invite URL the human should send to their teammate. Roles: admin (can manage members + integrations), member (can write data), viewer (read-only).", {
|
|
4755
5347
|
project_id: z.string().describe("The project ID"),
|
|
4756
5348
|
email: z.string().describe("Teammate's email address"),
|
|
@@ -4777,7 +5369,7 @@ function registerTools(server, apiClient) {
|
|
|
4777
5369
|
text: JSON.stringify(result, null, 2)
|
|
4778
5370
|
}] };
|
|
4779
5371
|
});
|
|
4780
|
-
registerTool(server, apiClient, "amba_projects_remove_member", "Remove a member from a project. The owner cannot be removed
|
|
5372
|
+
registerTool(server, apiClient, "amba_projects_remove_member", "Remove a member from a project. The owner cannot be removed — transfer ownership first via amba_projects_transfer_owner, then remove the demoted ex-owner like any admin. Members can remove themselves.", {
|
|
4781
5373
|
project_id: z.string().describe("The project ID"),
|
|
4782
5374
|
developer_id: z.string().describe("The developer ID to remove")
|
|
4783
5375
|
}, async ({ project_id, developer_id }, { client }) => {
|
|
@@ -4787,6 +5379,304 @@ function registerTools(server, apiClient) {
|
|
|
4787
5379
|
text: JSON.stringify(result, null, 2)
|
|
4788
5380
|
}] };
|
|
4789
5381
|
});
|
|
5382
|
+
registerTool(server, apiClient, "amba_projects_transfer_owner", "Transfer project ownership to an existing member. Only the current owner can do this. The new owner must already be a member (invite + accept them first if not). The previous owner is automatically demoted to admin, retaining member and integration management. Use this before removing the current owner — owners cannot be removed directly.", {
|
|
5383
|
+
project_id: z.string().describe("The project ID"),
|
|
5384
|
+
new_owner_developer_id: z.string().describe("Developer ID of the existing member who should become the new owner.")
|
|
5385
|
+
}, async ({ project_id, new_owner_developer_id }, { client }) => {
|
|
5386
|
+
const result = await client.post(`/projects/${project_id}/transfer-ownership`, { new_owner_developer_id });
|
|
5387
|
+
return { content: [{
|
|
5388
|
+
type: "text",
|
|
5389
|
+
text: JSON.stringify(result, null, 2)
|
|
5390
|
+
}] };
|
|
5391
|
+
});
|
|
5392
|
+
}
|
|
5393
|
+
//#endregion
|
|
5394
|
+
//#region src/tools/webhooks.ts
|
|
5395
|
+
function registerTools$1(server, apiClient) {
|
|
5396
|
+
registerTool(server, apiClient, "amba_webhooks_create", "Register an outbound webhook subscription. Amba sends an HMAC-signed POST to target_url every time an event named event_name fires. The signing secret is returned ONCE in this response — copy it into your receiver config; later reads never include it. target_url must be https:// (http:// is allowed only for localhost during development).", {
|
|
5397
|
+
project_id: z.string().describe("The project ID"),
|
|
5398
|
+
event_name: z.string().describe("Event name to subscribe to. Must match ^[a-z][a-z0-9_.-]{0,127}$ (case-insensitive)."),
|
|
5399
|
+
target_url: z.string().describe("HTTPS endpoint that receives the signed POST. http:// allowed only for localhost.")
|
|
5400
|
+
}, async ({ project_id, event_name, target_url }, { client }) => {
|
|
5401
|
+
const result = await client.post(`/projects/${project_id}/webhooks/subscriptions`, {
|
|
5402
|
+
event_name,
|
|
5403
|
+
target_url
|
|
5404
|
+
});
|
|
5405
|
+
return { content: [{
|
|
5406
|
+
type: "text",
|
|
5407
|
+
text: JSON.stringify(result, null, 2)
|
|
5408
|
+
}] };
|
|
5409
|
+
});
|
|
5410
|
+
registerTool(server, apiClient, "amba_webhooks_list", "List all webhook subscriptions for a project (most recent first). Secrets are never included — only id, event_name, target_url, status, and timestamps. status is \"active\", \"degraded\" (the delivery circuit breaker tripped on repeated failures), or \"disabled\".", { project_id: z.string().describe("The project ID") }, async ({ project_id }, { client }) => {
|
|
5411
|
+
const result = await client.get(`/projects/${project_id}/webhooks/subscriptions`);
|
|
5412
|
+
return { content: [{
|
|
5413
|
+
type: "text",
|
|
5414
|
+
text: JSON.stringify(result, null, 2)
|
|
5415
|
+
}] };
|
|
5416
|
+
});
|
|
5417
|
+
registerTool(server, apiClient, "amba_webhooks_get", "Describe a single webhook subscription by id. Returns event_name, target_url, status, attempt metadata, and timestamps (no secret).", {
|
|
5418
|
+
project_id: z.string().describe("The project ID"),
|
|
5419
|
+
subscription_id: z.string().describe("The subscription UUID")
|
|
5420
|
+
}, async ({ project_id, subscription_id }, { client }) => {
|
|
5421
|
+
const result = await client.get(`/projects/${project_id}/webhooks/subscriptions/${subscription_id}`);
|
|
5422
|
+
return { content: [{
|
|
5423
|
+
type: "text",
|
|
5424
|
+
text: JSON.stringify(result, null, 2)
|
|
5425
|
+
}] };
|
|
5426
|
+
});
|
|
5427
|
+
registerTool(server, apiClient, "amba_webhooks_update", "Update a webhook subscription. Any of event_name, target_url, or status may be changed; supply at least one. status accepts only \"active\" or \"disabled\" — \"degraded\" is reserved for the automatic failure circuit breaker and cannot be set manually.", {
|
|
5428
|
+
project_id: z.string().describe("The project ID"),
|
|
5429
|
+
subscription_id: z.string().describe("The subscription UUID"),
|
|
5430
|
+
event_name: z.string().optional().describe("New event name. Must match ^[a-z][a-z0-9_.-]{0,127}$ (case-insensitive)."),
|
|
5431
|
+
target_url: z.string().optional().describe("New HTTPS endpoint. http:// allowed only for localhost."),
|
|
5432
|
+
status: z.enum(["active", "disabled"]).optional().describe("Enable or disable delivery. \"degraded\" cannot be set manually.")
|
|
5433
|
+
}, async ({ project_id, subscription_id, event_name, target_url, status }, { client }) => {
|
|
5434
|
+
const body = {};
|
|
5435
|
+
if (event_name !== void 0) body.event_name = event_name;
|
|
5436
|
+
if (target_url !== void 0) body.target_url = target_url;
|
|
5437
|
+
if (status !== void 0) body.status = status;
|
|
5438
|
+
const result = await client.patch(`/projects/${project_id}/webhooks/subscriptions/${subscription_id}`, body);
|
|
5439
|
+
return { content: [{
|
|
5440
|
+
type: "text",
|
|
5441
|
+
text: JSON.stringify(result, null, 2)
|
|
5442
|
+
}] };
|
|
5443
|
+
});
|
|
5444
|
+
registerTool(server, apiClient, "amba_webhooks_delete", "Delete a webhook subscription. No further events are delivered to its target_url after this.", {
|
|
5445
|
+
project_id: z.string().describe("The project ID"),
|
|
5446
|
+
subscription_id: z.string().describe("The subscription UUID")
|
|
5447
|
+
}, async ({ project_id, subscription_id }, { client }) => {
|
|
5448
|
+
const result = await client.delete(`/projects/${project_id}/webhooks/subscriptions/${subscription_id}`);
|
|
5449
|
+
return { content: [{
|
|
5450
|
+
type: "text",
|
|
5451
|
+
text: JSON.stringify(result, null, 2)
|
|
5452
|
+
}] };
|
|
5453
|
+
});
|
|
5454
|
+
registerTool(server, apiClient, "amba_webhooks_rotate_secret", "Rotate the HMAC signing secret for a subscription. The new secret is returned ONCE — update your receiver config with it. To avoid dropped deliveries, have your receiver accept both the old and new secret for a brief overlap window before retiring the old one.", {
|
|
5455
|
+
project_id: z.string().describe("The project ID"),
|
|
5456
|
+
subscription_id: z.string().describe("The subscription UUID")
|
|
5457
|
+
}, async ({ project_id, subscription_id }, { client }) => {
|
|
5458
|
+
const result = await client.post(`/projects/${project_id}/webhooks/subscriptions/${subscription_id}/rotate-secret`);
|
|
5459
|
+
return { content: [{
|
|
5460
|
+
type: "text",
|
|
5461
|
+
text: JSON.stringify(result, null, 2)
|
|
5462
|
+
}] };
|
|
5463
|
+
});
|
|
5464
|
+
registerTool(server, apiClient, "amba_webhooks_test", "Send a synthetic test delivery to a subscription to verify receiver setup. The test payload is tagged so it does not affect the failure circuit breaker or pollute your real event history. Returns a workflow id; poll amba_webhooks_deliveries_list shortly after for the result. Fails with 409 if the subscription is disabled.", {
|
|
5465
|
+
project_id: z.string().describe("The project ID"),
|
|
5466
|
+
subscription_id: z.string().describe("The subscription UUID")
|
|
5467
|
+
}, async ({ project_id, subscription_id }, { client }) => {
|
|
5468
|
+
const result = await client.post(`/projects/${project_id}/webhooks/subscriptions/${subscription_id}/test`);
|
|
5469
|
+
return { content: [{
|
|
5470
|
+
type: "text",
|
|
5471
|
+
text: JSON.stringify(result, null, 2)
|
|
5472
|
+
}] };
|
|
5473
|
+
});
|
|
5474
|
+
registerTool(server, apiClient, "amba_webhooks_deliveries_list", "List recent webhook delivery attempts (most recent first). Optionally filter by subscription_id and/or status. status is \"pending\", \"delivered\", \"failed\", or \"dlq\" (dead-lettered after exhausting retries). Default page size 50, max 200.", {
|
|
5475
|
+
project_id: z.string().describe("The project ID"),
|
|
5476
|
+
subscription_id: z.string().optional().describe("Filter to deliveries for a single subscription UUID."),
|
|
5477
|
+
status: z.enum([
|
|
5478
|
+
"pending",
|
|
5479
|
+
"delivered",
|
|
5480
|
+
"failed",
|
|
5481
|
+
"dlq"
|
|
5482
|
+
]).optional().describe("Filter by delivery status."),
|
|
5483
|
+
limit: z.number().optional().describe("Page size (default 50, max 200).")
|
|
5484
|
+
}, async ({ project_id, subscription_id, status, limit }, { client }) => {
|
|
5485
|
+
const query = {};
|
|
5486
|
+
if (subscription_id !== void 0) query.subscription_id = subscription_id;
|
|
5487
|
+
if (status !== void 0) query.status = status;
|
|
5488
|
+
if (limit !== void 0) query.limit = String(limit);
|
|
5489
|
+
const result = await client.get(`/projects/${project_id}/webhooks/deliveries`, query);
|
|
5490
|
+
return { content: [{
|
|
5491
|
+
type: "text",
|
|
5492
|
+
text: JSON.stringify(result, null, 2)
|
|
5493
|
+
}] };
|
|
5494
|
+
});
|
|
5495
|
+
registerTool(server, apiClient, "amba_webhooks_deliveries_get", "Describe a single webhook delivery by id, including the full attempt log, attempt count, and last error.", {
|
|
5496
|
+
project_id: z.string().describe("The project ID"),
|
|
5497
|
+
delivery_id: z.string().describe("The delivery UUID")
|
|
5498
|
+
}, async ({ project_id, delivery_id }, { client }) => {
|
|
5499
|
+
const result = await client.get(`/projects/${project_id}/webhooks/deliveries/${delivery_id}`);
|
|
5500
|
+
return { content: [{
|
|
5501
|
+
type: "text",
|
|
5502
|
+
text: JSON.stringify(result, null, 2)
|
|
5503
|
+
}] };
|
|
5504
|
+
});
|
|
5505
|
+
registerTool(server, apiClient, "amba_webhooks_deliveries_replay", "Re-enqueue a terminated (failed or dead-lettered) delivery, rebuilding the original payload from the source event. The original idempotency key is preserved so receiver-side dedup treats it as a re-delivery, not a new event. Returns 409 if the delivery is still pending, or 410 if the source event has been purged.", {
|
|
5506
|
+
project_id: z.string().describe("The project ID"),
|
|
5507
|
+
delivery_id: z.string().describe("The delivery UUID to replay")
|
|
5508
|
+
}, async ({ project_id, delivery_id }, { client }) => {
|
|
5509
|
+
const result = await client.post(`/projects/${project_id}/webhooks/deliveries/${delivery_id}/replay`);
|
|
5510
|
+
return { content: [{
|
|
5511
|
+
type: "text",
|
|
5512
|
+
text: JSON.stringify(result, null, 2)
|
|
5513
|
+
}] };
|
|
5514
|
+
});
|
|
5515
|
+
}
|
|
5516
|
+
//#endregion
|
|
5517
|
+
//#region src/tools/email.ts
|
|
5518
|
+
function registerTools(server, apiClient) {
|
|
5519
|
+
registerTool(server, apiClient, "amba_email_templates_create", "Create or update a transactional email template (upsert keyed by name — re-creating an existing name overwrites it). Templates are referenced by name when sending. Subject and html_body are required; text_body is an optional plain-text fallback. name must match ^[a-z][a-z0-9_]*$.", {
|
|
5520
|
+
project_id: z.string().describe("The project ID"),
|
|
5521
|
+
name: z.string().describe("Template name. Must match ^[a-z][a-z0-9_]*$."),
|
|
5522
|
+
subject: z.string().describe("Email subject line. Supports template variables."),
|
|
5523
|
+
html_body: z.string().describe("HTML body. Supports template variables."),
|
|
5524
|
+
text_body: z.string().nullable().optional().describe("Optional plain-text fallback body. Pass null to leave unset.")
|
|
5525
|
+
}, async ({ project_id, name, subject, html_body, text_body }, { client }) => {
|
|
5526
|
+
const body = {
|
|
5527
|
+
name,
|
|
5528
|
+
subject,
|
|
5529
|
+
html_body
|
|
5530
|
+
};
|
|
5531
|
+
if (text_body !== void 0) body.text_body = text_body;
|
|
5532
|
+
const result = await client.post(`/projects/${project_id}/email/templates`, body);
|
|
5533
|
+
return { content: [{
|
|
5534
|
+
type: "text",
|
|
5535
|
+
text: JSON.stringify(result, null, 2)
|
|
5536
|
+
}] };
|
|
5537
|
+
});
|
|
5538
|
+
registerTool(server, apiClient, "amba_email_templates_list", "List all transactional email templates for a project, ordered by name.", { project_id: z.string().describe("The project ID") }, async ({ project_id }, { client }) => {
|
|
5539
|
+
const result = await client.get(`/projects/${project_id}/email/templates`);
|
|
5540
|
+
return { content: [{
|
|
5541
|
+
type: "text",
|
|
5542
|
+
text: JSON.stringify(result, null, 2)
|
|
5543
|
+
}] };
|
|
5544
|
+
});
|
|
5545
|
+
registerTool(server, apiClient, "amba_email_templates_get", "Get a single transactional email template by name, including its subject, html_body, and text_body.", {
|
|
5546
|
+
project_id: z.string().describe("The project ID"),
|
|
5547
|
+
name: z.string().describe("The template name")
|
|
5548
|
+
}, async ({ project_id, name }, { client }) => {
|
|
5549
|
+
const result = await client.get(`/projects/${project_id}/email/templates/${encodeURIComponent(name)}`);
|
|
5550
|
+
return { content: [{
|
|
5551
|
+
type: "text",
|
|
5552
|
+
text: JSON.stringify(result, null, 2)
|
|
5553
|
+
}] };
|
|
5554
|
+
});
|
|
5555
|
+
registerTool(server, apiClient, "amba_email_templates_update", "Update fields on an existing email template. Supply at least one of subject, html_body, or text_body. For text_body: pass a string to set it, or null to explicitly clear it; omit it to leave it unchanged.", {
|
|
5556
|
+
project_id: z.string().describe("The project ID"),
|
|
5557
|
+
name: z.string().describe("The template name to update"),
|
|
5558
|
+
subject: z.string().optional().describe("New subject line."),
|
|
5559
|
+
html_body: z.string().optional().describe("New HTML body."),
|
|
5560
|
+
text_body: z.string().nullable().optional().describe("New plain-text body; pass null to clear it, omit to leave unchanged.")
|
|
5561
|
+
}, async ({ project_id, name, subject, html_body, text_body }, { client }) => {
|
|
5562
|
+
const body = {};
|
|
5563
|
+
if (subject !== void 0) body.subject = subject;
|
|
5564
|
+
if (html_body !== void 0) body.html_body = html_body;
|
|
5565
|
+
if (text_body !== void 0) body.text_body = text_body;
|
|
5566
|
+
const result = await client.patch(`/projects/${project_id}/email/templates/${encodeURIComponent(name)}`, body);
|
|
5567
|
+
return { content: [{
|
|
5568
|
+
type: "text",
|
|
5569
|
+
text: JSON.stringify(result, null, 2)
|
|
5570
|
+
}] };
|
|
5571
|
+
});
|
|
5572
|
+
registerTool(server, apiClient, "amba_email_templates_delete", "Delete a transactional email template by name. Sends that reference a deleted template will fail with TEMPLATE_NOT_FOUND.", {
|
|
5573
|
+
project_id: z.string().describe("The project ID"),
|
|
5574
|
+
name: z.string().describe("The template name to delete")
|
|
5575
|
+
}, async ({ project_id, name }, { client }) => {
|
|
5576
|
+
const result = await client.delete(`/projects/${project_id}/email/templates/${encodeURIComponent(name)}`);
|
|
5577
|
+
return { content: [{
|
|
5578
|
+
type: "text",
|
|
5579
|
+
text: JSON.stringify(result, null, 2)
|
|
5580
|
+
}] };
|
|
5581
|
+
});
|
|
5582
|
+
registerTool(server, apiClient, "amba_email_suppressions_create", "Add an email address to the suppression list (upsert). Suppressed addresses are skipped on send. reason is \"bounce\", \"complaint\", or \"manual\" (default). Use this to honor unsubscribe or block requests.", {
|
|
5583
|
+
project_id: z.string().describe("The project ID"),
|
|
5584
|
+
email: z.string().describe("Email address to suppress (must contain @)."),
|
|
5585
|
+
reason: z.enum([
|
|
5586
|
+
"bounce",
|
|
5587
|
+
"complaint",
|
|
5588
|
+
"manual"
|
|
5589
|
+
]).optional().describe("Why the address is suppressed. Defaults to \"manual\".")
|
|
5590
|
+
}, async ({ project_id, email, reason }, { client }) => {
|
|
5591
|
+
const body = { email };
|
|
5592
|
+
if (reason !== void 0) body.reason = reason;
|
|
5593
|
+
const result = await client.post(`/projects/${project_id}/email/suppressions`, body);
|
|
5594
|
+
return { content: [{
|
|
5595
|
+
type: "text",
|
|
5596
|
+
text: JSON.stringify(result, null, 2)
|
|
5597
|
+
}] };
|
|
5598
|
+
});
|
|
5599
|
+
registerTool(server, apiClient, "amba_email_suppressions_list", "List suppressed email addresses (most recent first, up to 500). Optionally filter by reason (\"bounce\", \"complaint\", or \"manual\").", {
|
|
5600
|
+
project_id: z.string().describe("The project ID"),
|
|
5601
|
+
reason: z.enum([
|
|
5602
|
+
"bounce",
|
|
5603
|
+
"complaint",
|
|
5604
|
+
"manual"
|
|
5605
|
+
]).optional().describe("Filter to a single suppression reason.")
|
|
5606
|
+
}, async ({ project_id, reason }, { client }) => {
|
|
5607
|
+
const query = {};
|
|
5608
|
+
if (reason !== void 0) query.reason = reason;
|
|
5609
|
+
const result = await client.get(`/projects/${project_id}/email/suppressions`, query);
|
|
5610
|
+
return { content: [{
|
|
5611
|
+
type: "text",
|
|
5612
|
+
text: JSON.stringify(result, null, 2)
|
|
5613
|
+
}] };
|
|
5614
|
+
});
|
|
5615
|
+
registerTool(server, apiClient, "amba_email_suppressions_delete", "Remove an email address from the suppression list (operator unblock). Subsequent sends to this address are no longer skipped.", {
|
|
5616
|
+
project_id: z.string().describe("The project ID"),
|
|
5617
|
+
email: z.string().describe("Email address to unsuppress.")
|
|
5618
|
+
}, async ({ project_id, email }, { client }) => {
|
|
5619
|
+
const result = await client.delete(`/projects/${project_id}/email/suppressions/${encodeURIComponent(email)}`);
|
|
5620
|
+
return { content: [{
|
|
5621
|
+
type: "text",
|
|
5622
|
+
text: JSON.stringify(result, null, 2)
|
|
5623
|
+
}] };
|
|
5624
|
+
});
|
|
5625
|
+
registerTool(server, apiClient, "amba_email_send", "Send a transactional email. Recipient is either a user (to.user_id — resolves the user's email) or a raw address (to.email). Send via a template (template + optional data variables) OR ad-hoc to a raw address (subject + html/text). Suppressed recipients are skipped and returned with status \"suppressed\".", {
|
|
5626
|
+
project_id: z.string().describe("The project ID"),
|
|
5627
|
+
to: z.object({
|
|
5628
|
+
user_id: z.string().optional().describe("App user UUID; resolves the user's email server-side."),
|
|
5629
|
+
email: z.string().optional().describe("Raw recipient email address.")
|
|
5630
|
+
}).describe("Recipient. Provide exactly one of user_id or email."),
|
|
5631
|
+
template: z.string().optional().describe("Template name to render. Mutually exclusive with subject/html/text."),
|
|
5632
|
+
data: z.record(z.unknown()).optional().describe("Template variable map (used with `template`)."),
|
|
5633
|
+
subject: z.string().optional().describe("Ad-hoc subject. Requires to.email; mutually exclusive with `template`."),
|
|
5634
|
+
html: z.string().optional().describe("Ad-hoc HTML body (ad-hoc send only)."),
|
|
5635
|
+
text: z.string().optional().describe("Ad-hoc plain-text body (ad-hoc send only).")
|
|
5636
|
+
}, async ({ project_id, to, template, data, subject, html, text }, { client }) => {
|
|
5637
|
+
const body = { to };
|
|
5638
|
+
if (template !== void 0) body.template = template;
|
|
5639
|
+
if (data !== void 0) body.data = data;
|
|
5640
|
+
if (subject !== void 0) body.subject = subject;
|
|
5641
|
+
if (html !== void 0) body.html = html;
|
|
5642
|
+
if (text !== void 0) body.text = text;
|
|
5643
|
+
const result = await client.post(`/projects/${project_id}/email/send`, body);
|
|
5644
|
+
return { content: [{
|
|
5645
|
+
type: "text",
|
|
5646
|
+
text: JSON.stringify(result, null, 2)
|
|
5647
|
+
}] };
|
|
5648
|
+
});
|
|
5649
|
+
registerTool(server, apiClient, "amba_email_deliveries_list", "List recent email delivery records (most recent first). Optionally filter by template name and/or status (\"queued\", \"sent\", \"suppressed\", \"failed\"). Default page size 50, max 200.", {
|
|
5650
|
+
project_id: z.string().describe("The project ID"),
|
|
5651
|
+
template: z.string().optional().describe("Filter to a single template name."),
|
|
5652
|
+
status: z.enum([
|
|
5653
|
+
"queued",
|
|
5654
|
+
"sent",
|
|
5655
|
+
"suppressed",
|
|
5656
|
+
"failed"
|
|
5657
|
+
]).optional().describe("Filter by delivery status."),
|
|
5658
|
+
limit: z.number().optional().describe("Page size (default 50, max 200).")
|
|
5659
|
+
}, async ({ project_id, template, status, limit }, { client }) => {
|
|
5660
|
+
const query = {};
|
|
5661
|
+
if (template !== void 0) query.template = template;
|
|
5662
|
+
if (status !== void 0) query.status = status;
|
|
5663
|
+
if (limit !== void 0) query.limit = String(limit);
|
|
5664
|
+
const result = await client.get(`/projects/${project_id}/email/deliveries`, query);
|
|
5665
|
+
return { content: [{
|
|
5666
|
+
type: "text",
|
|
5667
|
+
text: JSON.stringify(result, null, 2)
|
|
5668
|
+
}] };
|
|
5669
|
+
});
|
|
5670
|
+
registerTool(server, apiClient, "amba_email_deliveries_get", "Describe a single email delivery by id, including recipient, template, status, and last error if it failed.", {
|
|
5671
|
+
project_id: z.string().describe("The project ID"),
|
|
5672
|
+
delivery_id: z.string().describe("The delivery UUID")
|
|
5673
|
+
}, async ({ project_id, delivery_id }, { client }) => {
|
|
5674
|
+
const result = await client.get(`/projects/${project_id}/email/deliveries/${delivery_id}`);
|
|
5675
|
+
return { content: [{
|
|
5676
|
+
type: "text",
|
|
5677
|
+
text: JSON.stringify(result, null, 2)
|
|
5678
|
+
}] };
|
|
5679
|
+
});
|
|
4790
5680
|
}
|
|
4791
5681
|
//#endregion
|
|
4792
5682
|
//#region src/resources/amba-setup-identity.ts
|
|
@@ -5586,6 +6476,7 @@ Real-money purchases need a billing integration configured separately — see \`
|
|
|
5586
6476
|
| \`amba_currencies_update\` | Edit (rename, change caps, change auto-recharge). | \`{ project_id, currency_id, max_balance: 10000 }\` |
|
|
5587
6477
|
| \`amba_currencies_delete\` | Delete a currency (irreversible — users lose their balance). | \`{ project_id, currency_id }\` |
|
|
5588
6478
|
| \`amba_currencies_grant\` | Grant currency to a specific user. | \`{ project_id, app_user_id, currency_code: "gems", amount: 100, reason: "welcome_bonus" }\` |
|
|
6479
|
+
| \`amba_currencies_spend\` | Debit currency from a specific user. Atomic; returns INSUFFICIENT_FUNDS if balance is too low (no partial debit). | \`{ project_id, app_user_id, currency_code: "gems", amount: 5, reason: "ai_generation" }\` |
|
|
5589
6480
|
| \`amba_currencies_get_transactions\` | Per-user transaction ledger. | \`{ project_id, user_id, currency_code: "gems", limit: 100 }\` |
|
|
5590
6481
|
|
|
5591
6482
|
#### Currency grant rules (auto-grants)
|
|
@@ -5871,6 +6762,9 @@ Mostly SDK-side — admin tools are for moderation + stats.
|
|
|
5871
6762
|
|
|
5872
6763
|
| Tool | Purpose | Example args |
|
|
5873
6764
|
| --- | --- | --- |
|
|
6765
|
+
| \`amba_messaging_create_conversation\` | Create a conversation server-side (zero or more initial participants — for matchmaker-built circles). | \`{ project_id, type: "group", name: "Circle" }\` |
|
|
6766
|
+
| \`amba_messaging_add_participant\` | Add a user to a conversation (idempotent). The matchmaker primitive for growing membership over time. | \`{ project_id, conversation_id, user_id }\` |
|
|
6767
|
+
| \`amba_messaging_remove_participant\` | Remove a user from a conversation (idempotent). | \`{ project_id, conversation_id, user_id }\` |
|
|
5874
6768
|
| \`amba_messaging_list_conversations\` | List conversations (admin / moderation view). | \`{ project_id, limit: 50 }\` |
|
|
5875
6769
|
| \`amba_messaging_list_messages\` | List messages in a conversation. | \`{ project_id, conversation_id, limit: 100 }\` |
|
|
5876
6770
|
| \`amba_messaging_delete_message\` | Hard-delete a message (moderation). | \`{ project_id, conversation_id, message_id }\` |
|
|
@@ -6092,23 +6986,26 @@ If gamification, economy, and social are the playable surface, **infrastructure
|
|
|
6092
6986
|
|
|
6093
6987
|
A collection is a schema-first table inside the project's isolated tenant database. You describe the columns, the server creates the table and any indexes. Rows are scoped to the signed-in \`app_user\` automatically (server-enforced auto row-level isolation) for SDK clients — admin tools bypass this.
|
|
6094
6988
|
|
|
6989
|
+
Admin tools authenticate the developer/agent (pass \`pat\` or send it as the inbound Bearer) and take \`project_id\`. Client tools authenticate an end-user and take \`api_key\` (+ \`session_token\`) — NOT \`project_id\` and NOT a \`pat\`. Every row tool names the collection with \`name\`, never \`collection\`.
|
|
6990
|
+
|
|
6095
6991
|
| Tool | Purpose | Example args |
|
|
6096
6992
|
| --- | --- | --- |
|
|
6097
|
-
| \`amba_collections_create\` | Create a typed collection. | \`{ project_id, name: "todos", columns: [{ name: "title", type: "text", nullable: false }, { name: "done", type: "boolean", nullable: false, default: false }, { name: "due_at", type: "timestamptz", nullable: true }] }\` |
|
|
6993
|
+
| \`amba_collections_create\` | Create a typed collection. Pass \`shared: true\` for developer-seeded GLOBAL content (question banks, lookup tables) so \`user_id\` is nullable. | \`{ project_id, name: "todos", columns: [{ name: "title", type: "text", nullable: false }, { name: "done", type: "boolean", nullable: false, default: false }, { name: "due_at", type: "timestamptz", nullable: true }], shared: false }\` |
|
|
6098
6994
|
| \`amba_collections_list\` | List collections in this project. | \`{ project_id }\` |
|
|
6099
|
-
| \`amba_collections_get\` | Read one collection's schema. | \`{ project_id,
|
|
6100
|
-
| \`amba_collections_alter\` |
|
|
6101
|
-
| \`amba_collections_delete\` | Drop the table (destructive). | \`{ project_id,
|
|
6102
|
-
| \`amba_admin_insert_row\` | Insert
|
|
6103
|
-
| \`
|
|
6104
|
-
| \`
|
|
6105
|
-
| \`
|
|
6106
|
-
| \`
|
|
6107
|
-
| \`
|
|
6108
|
-
| \`
|
|
6109
|
-
| \`
|
|
6110
|
-
| \`
|
|
6111
|
-
| \`
|
|
6995
|
+
| \`amba_collections_get\` | Read one collection's schema. | \`{ project_id, name: "todos" }\` |
|
|
6996
|
+
| \`amba_collections_alter\` | Exactly ONE of: \`add_column\`, \`add_index\`, \`drop_column\`, or \`relax_user_id\` per call. \`relax_user_id: true\` converts an existing collection to shared (drops the \`user_id\` NOT NULL). | \`{ project_id, name: "todos", add_column: { name: "priority", type: "int", nullable: true } }\` |
|
|
6997
|
+
| \`amba_collections_delete\` | Drop the table (destructive). \`confirm\` must equal the collection name. | \`{ project_id, name: "todos", confirm: "todos" }\` |
|
|
6998
|
+
| \`amba_admin_insert_row\` | Insert one row as the developer (bypasses user-scope; \`user_id\` honored if present). | \`{ project_id, name: "todos", row: { title: "Sample", done: false } }\` |
|
|
6999
|
+
| \`amba_admin_insert_rows\` | Bulk-insert up to 500 rows in one atomic statement — the canonical seeding/migration path. \`on_conflict\`: \`"error"\` (default) or \`"skip"\`. | \`{ project_id, name: "questions", rows: [{ q: "..." }, { q: "..." }], on_conflict: "skip" }\` |
|
|
7000
|
+
| \`amba_admin_list_rows\` | Read rows as the developer. | \`{ project_id, name: "todos", limit: 100 }\` |
|
|
7001
|
+
| \`amba_client_insert_row\` | Insert as an end-user. Requires \`api_key\` (+ \`session_token\`). | \`{ api_key, session_token, name: "todos", row: {...} }\` |
|
|
7002
|
+
| \`amba_client_list_rows\` | Read as an end-user (auto user-scoped). | \`{ api_key, session_token, name: "todos" }\` |
|
|
7003
|
+
| \`amba_client_get_row\` | Get one row by id (end-user). | \`{ api_key, session_token, name: "todos", id }\` |
|
|
7004
|
+
| \`amba_client_update_row\` | Update one row by id (end-user). Fields go in \`set\`. Omit \`id\` + pass \`where\` for a bulk update. | \`{ api_key, session_token, name: "todos", id, set: {...} }\` |
|
|
7005
|
+
| \`amba_client_delete_row\` | Soft-delete one row by id (end-user). | \`{ api_key, session_token, name: "todos", id }\` |
|
|
7006
|
+
| \`amba_client_count_rows\` | Count rows matching an optional \`where\`. | \`{ api_key, session_token, name: "todos", where: {...} }\` |
|
|
7007
|
+
| \`amba_client_find_rows\` | Filter / sort / paginate rows (SDK-shaped \`filter\`). | \`{ api_key, session_token, name: "todos", filter: {...}, order: ["created_at desc"], limit: 50 }\` |
|
|
7008
|
+
| \`amba_client_find_nearest_rows\` | Vector-similarity search (rows with a \`vector(<dim>)\` column). | \`{ api_key, session_token, name: "todos", column: "embedding", to_vector: [...], k: 10 }\` |
|
|
6112
7009
|
|
|
6113
7010
|
Column types: \`text\`, \`int\`, \`bigint\`, \`float\`, \`boolean\`, \`timestamptz\`, \`date\`, \`json\`, \`jsonb\`, \`uuid\`, \`vector(<dim>)\` (e.g. \`vector(1536)\` for OpenAI embeddings).
|
|
6114
7011
|
|
|
@@ -6623,6 +7520,8 @@ const TOOL_CATEGORY = {
|
|
|
6623
7520
|
amba_list_xp_rules: "gamification",
|
|
6624
7521
|
amba_xp_update_rule: "gamification",
|
|
6625
7522
|
amba_xp_delete_rule: "gamification",
|
|
7523
|
+
amba_xp_grant: "gamification",
|
|
7524
|
+
amba_xp_deduct: "gamification",
|
|
6626
7525
|
amba_xp_list_users: "gamification",
|
|
6627
7526
|
amba_users_get_xp: "gamification",
|
|
6628
7527
|
amba_get_user_xp: "gamification",
|
|
@@ -6636,11 +7535,14 @@ const TOOL_CATEGORY = {
|
|
|
6636
7535
|
amba_update_achievement: "gamification",
|
|
6637
7536
|
amba_achievements_delete: "gamification",
|
|
6638
7537
|
amba_delete_achievement: "gamification",
|
|
7538
|
+
amba_achievements_grant: "gamification",
|
|
7539
|
+
amba_achievements_revoke: "gamification",
|
|
6639
7540
|
amba_streaks_create: "gamification",
|
|
6640
7541
|
amba_create_streak: "gamification",
|
|
6641
7542
|
amba_streaks_list: "gamification",
|
|
6642
7543
|
amba_streaks_update: "gamification",
|
|
6643
7544
|
amba_streaks_delete: "gamification",
|
|
7545
|
+
amba_streaks_reset: "gamification",
|
|
6644
7546
|
amba_leaderboards_create: "gamification",
|
|
6645
7547
|
amba_create_leaderboard: "gamification",
|
|
6646
7548
|
amba_leaderboards_list: "gamification",
|
|
@@ -6675,6 +7577,7 @@ const TOOL_CATEGORY = {
|
|
|
6675
7577
|
amba_delete_currency: "economy",
|
|
6676
7578
|
amba_currencies_grant: "economy",
|
|
6677
7579
|
amba_grant_currency: "economy",
|
|
7580
|
+
amba_currencies_spend: "economy",
|
|
6678
7581
|
amba_currencies_get_transactions: "economy",
|
|
6679
7582
|
amba_get_currency_transactions: "economy",
|
|
6680
7583
|
amba_currency_grant_rules_create: "economy",
|
|
@@ -6709,6 +7612,7 @@ const TOOL_CATEGORY = {
|
|
|
6709
7612
|
amba_stores_patch_listing: "economy",
|
|
6710
7613
|
amba_stores_delete_listing: "economy",
|
|
6711
7614
|
amba_inventory_grant_item: "economy",
|
|
7615
|
+
amba_inventory_revoke_item: "economy",
|
|
6712
7616
|
amba_grant_item: "economy",
|
|
6713
7617
|
amba_users_get_inventory: "economy",
|
|
6714
7618
|
amba_get_user_inventory: "economy",
|
|
@@ -6732,6 +7636,9 @@ const TOOL_CATEGORY = {
|
|
|
6732
7636
|
amba_update_group_member: "social",
|
|
6733
7637
|
amba_groups_remove_member: "social",
|
|
6734
7638
|
amba_remove_group_member: "social",
|
|
7639
|
+
amba_messaging_create_conversation: "social",
|
|
7640
|
+
amba_messaging_add_participant: "social",
|
|
7641
|
+
amba_messaging_remove_participant: "social",
|
|
6735
7642
|
amba_messaging_list_conversations: "social",
|
|
6736
7643
|
amba_messaging_list_messages: "social",
|
|
6737
7644
|
amba_messaging_delete_message: "social",
|
|
@@ -6770,6 +7677,12 @@ const TOOL_CATEGORY = {
|
|
|
6770
7677
|
amba_get_analytics: "analytics",
|
|
6771
7678
|
amba_sessions_list: "analytics",
|
|
6772
7679
|
amba_sessions_analytics: "analytics",
|
|
7680
|
+
amba_funnels_list: "analytics",
|
|
7681
|
+
amba_funnels_create: "analytics",
|
|
7682
|
+
amba_funnels_get: "analytics",
|
|
7683
|
+
amba_funnels_update: "analytics",
|
|
7684
|
+
amba_funnels_delete: "analytics",
|
|
7685
|
+
amba_funnels_query: "analytics",
|
|
6773
7686
|
amba_projects_create: "infrastructure",
|
|
6774
7687
|
amba_create_project: "infrastructure",
|
|
6775
7688
|
amba_projects_list: "infrastructure",
|
|
@@ -6836,6 +7749,10 @@ const TOOL_CATEGORY = {
|
|
|
6836
7749
|
amba_remove_site_domain: "infrastructure",
|
|
6837
7750
|
amba_sites_list_domains: "infrastructure",
|
|
6838
7751
|
amba_list_site_domains: "infrastructure",
|
|
7752
|
+
amba_domains_search: "infrastructure",
|
|
7753
|
+
amba_domains_check: "infrastructure",
|
|
7754
|
+
amba_domains_purchase: "infrastructure",
|
|
7755
|
+
amba_domains_list: "infrastructure",
|
|
6839
7756
|
amba_media_upload: "infrastructure",
|
|
6840
7757
|
amba_upload_media: "infrastructure",
|
|
6841
7758
|
amba_media_list: "infrastructure",
|
|
@@ -6886,7 +7803,34 @@ const TOOL_CATEGORY = {
|
|
|
6886
7803
|
amba_billing_set_ceiling: "infrastructure",
|
|
6887
7804
|
amba_projects_invite_member: "infrastructure",
|
|
6888
7805
|
amba_projects_list_members: "infrastructure",
|
|
6889
|
-
amba_projects_remove_member: "infrastructure"
|
|
7806
|
+
amba_projects_remove_member: "infrastructure",
|
|
7807
|
+
amba_projects_transfer_owner: "infrastructure",
|
|
7808
|
+
amba_webhooks_create: "infrastructure",
|
|
7809
|
+
amba_webhooks_list: "infrastructure",
|
|
7810
|
+
amba_webhooks_get: "infrastructure",
|
|
7811
|
+
amba_webhooks_update: "infrastructure",
|
|
7812
|
+
amba_webhooks_delete: "infrastructure",
|
|
7813
|
+
amba_webhooks_rotate_secret: "infrastructure",
|
|
7814
|
+
amba_webhooks_test: "infrastructure",
|
|
7815
|
+
amba_webhooks_deliveries_list: "infrastructure",
|
|
7816
|
+
amba_webhooks_deliveries_get: "infrastructure",
|
|
7817
|
+
amba_webhooks_deliveries_replay: "infrastructure",
|
|
7818
|
+
amba_email_templates_create: "engagement",
|
|
7819
|
+
amba_email_templates_list: "engagement",
|
|
7820
|
+
amba_email_templates_get: "engagement",
|
|
7821
|
+
amba_email_templates_update: "engagement",
|
|
7822
|
+
amba_email_templates_delete: "engagement",
|
|
7823
|
+
amba_email_suppressions_create: "engagement",
|
|
7824
|
+
amba_email_suppressions_list: "engagement",
|
|
7825
|
+
amba_email_suppressions_delete: "engagement",
|
|
7826
|
+
amba_email_send: "engagement",
|
|
7827
|
+
amba_email_deliveries_list: "engagement",
|
|
7828
|
+
amba_email_deliveries_get: "engagement",
|
|
7829
|
+
amba_admin_insert_rows: "infrastructure",
|
|
7830
|
+
amba_remote_configs_create: "infrastructure",
|
|
7831
|
+
amba_remote_configs_list: "infrastructure",
|
|
7832
|
+
amba_remote_configs_update: "infrastructure",
|
|
7833
|
+
amba_remote_configs_delete: "infrastructure"
|
|
6890
7834
|
};
|
|
6891
7835
|
/**
|
|
6892
7836
|
* Look up the canonical category for an MCP tool name. Accepts both
|
|
@@ -6906,7 +7850,11 @@ function getToolCategory(toolName) {
|
|
|
6906
7850
|
* called against it once.
|
|
6907
7851
|
*/
|
|
6908
7852
|
function registerAllTools(server, apiClient) {
|
|
6909
|
-
registerTools$
|
|
7853
|
+
registerTools$10(server, apiClient);
|
|
7854
|
+
registerTools$29(server, apiClient);
|
|
7855
|
+
registerTools$28(server, apiClient);
|
|
7856
|
+
registerTools$27(server, apiClient);
|
|
7857
|
+
registerTools$26(server, apiClient);
|
|
6910
7858
|
registerTools$25(server, apiClient);
|
|
6911
7859
|
registerTools$24(server, apiClient);
|
|
6912
7860
|
registerTools$23(server, apiClient);
|
|
@@ -6922,9 +7870,9 @@ function registerAllTools(server, apiClient) {
|
|
|
6922
7870
|
registerTools$13(server, apiClient);
|
|
6923
7871
|
registerTools$12(server, apiClient);
|
|
6924
7872
|
registerTools$11(server, apiClient);
|
|
6925
|
-
registerTools$10(server, apiClient);
|
|
6926
7873
|
registerTools$9(server, apiClient);
|
|
6927
7874
|
registerTools$8(server, apiClient);
|
|
7875
|
+
registerTools$7(server, apiClient);
|
|
6928
7876
|
registerTools$6(server, apiClient);
|
|
6929
7877
|
registerTools$5(server, apiClient);
|
|
6930
7878
|
registerTools$4(server, apiClient);
|