@layers/amba-mcp 4.0.8 → 4.0.10
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/auto/collection-tools.d.ts +2 -0
- package/dist/auto/function-tools.d.ts +2 -0
- package/dist/index.js +43 -18
- package/dist/lib/tool-result.d.ts +12 -0
- package/dist/lib/with-pat.d.ts +6 -1
- package/dist/resources/amba-setup-identity.d.ts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/package.json +2 -2
|
@@ -51,6 +51,8 @@ export interface CollectionPolicy {
|
|
|
51
51
|
export interface AppMcpClientAuth {
|
|
52
52
|
apiKey: string;
|
|
53
53
|
sessionToken?: string;
|
|
54
|
+
/** Verified by the client API on every request; never trusted locally. */
|
|
55
|
+
groupContext?: string;
|
|
54
56
|
baseUrl: string;
|
|
55
57
|
}
|
|
56
58
|
export interface RegisterCollectionToolsOptions {
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
* named `recipes`.
|
|
27
27
|
*/
|
|
28
28
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
29
|
+
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
29
30
|
import { type AppMcpClientAuth } from './collection-tools.js';
|
|
30
31
|
/** One exposed function from the app-MCP manifest. */
|
|
31
32
|
export interface AppMcpManifestFunction {
|
|
32
33
|
name: string;
|
|
33
34
|
description?: string | null;
|
|
34
35
|
input_schema?: Record<string, unknown> | null;
|
|
36
|
+
annotations?: ToolAnnotations | null;
|
|
35
37
|
public?: boolean;
|
|
36
38
|
invoke_url: string;
|
|
37
39
|
}
|
package/dist/index.js
CHANGED
|
@@ -641,9 +641,9 @@ function registerTool(server, apiClient, name, description, schema, handler, ali
|
|
|
641
641
|
* `amba_developer_login`, `amba_developer_refresh`) — anything else
|
|
642
642
|
* should go through `registerTool`.
|
|
643
643
|
*/
|
|
644
|
-
function registerPublicTool(server, name, description, schema, handler, aliases = []) {
|
|
645
|
-
server.tool(name, description, schema, deriveToolAnnotations(name), handler);
|
|
646
|
-
for (const alias of aliases) server.tool(alias, description, schema, deriveToolAnnotations(alias), (async (args) => {
|
|
644
|
+
function registerPublicTool(server, name, description, schema, handler, aliases = [], annotations) {
|
|
645
|
+
server.tool(name, description, schema, annotations ?? deriveToolAnnotations(name), handler);
|
|
646
|
+
for (const alias of aliases) server.tool(alias, description, schema, annotations ?? deriveToolAnnotations(alias), (async (args) => {
|
|
647
647
|
warnDeprecatedAlias(alias, name);
|
|
648
648
|
return handler(args);
|
|
649
649
|
}));
|
|
@@ -685,7 +685,7 @@ function registerTools$42(server, apiClient) {
|
|
|
685
685
|
text: JSON.stringify(result, null, 2)
|
|
686
686
|
}] };
|
|
687
687
|
}, ["amba_create_project"]);
|
|
688
|
-
registerTool(server, apiClient, "amba_projects_update", "Update a project
|
|
688
|
+
registerTool(server, apiClient, "amba_projects_update", "Update a project, including passwordless-auth routing. `magic_link_redirect_url` is the exact HTTPS callback that receives the one-time `token` query parameter. `auth_email_brand_name` controls auth-email copy and sender attribution (`<brand> via Amba`) while the mailbox remains on Amba’s verified sending domain. Pass null to either auth field to restore its fallback.", {
|
|
689
689
|
project_id: z.string().describe("The project ID"),
|
|
690
690
|
name: z.string().optional().describe("Human-readable project name."),
|
|
691
691
|
bundle_id: z.string().optional().describe("App bundle identifier (e.g. \"com.example.myapp\"). Audience for Apple Sign In identity tokens."),
|
|
@@ -695,14 +695,18 @@ function registerTools$42(server, apiClient) {
|
|
|
695
695
|
"android",
|
|
696
696
|
"all"
|
|
697
697
|
]).optional().describe("Target platform."),
|
|
698
|
-
environment: z.enum(["development", "production"]).optional().describe("Project environment.")
|
|
699
|
-
|
|
698
|
+
environment: z.enum(["development", "production"]).optional().describe("Project environment."),
|
|
699
|
+
magic_link_redirect_url: z.string().url().nullable().optional().describe("Exact HTTPS callback URL for magic-link sign-in. Amba appends the one-time token query parameter. Pass null to restore the legacy fallback."),
|
|
700
|
+
auth_email_brand_name: z.string().max(80).nullable().optional().describe("Brand shown in auth-email copy and `<brand> via Amba` sender attribution. Pass null to fall back to the project name.")
|
|
701
|
+
}, async ({ project_id, name, bundle_id, google_oauth_client_id, platform, environment, magic_link_redirect_url, auth_email_brand_name }, { client }) => {
|
|
700
702
|
const payload = {};
|
|
701
703
|
if (name !== void 0) payload.name = name;
|
|
702
704
|
if (bundle_id !== void 0) payload.bundle_id = bundle_id;
|
|
703
705
|
if (google_oauth_client_id !== void 0) payload.google_oauth_client_id = google_oauth_client_id;
|
|
704
706
|
if (platform !== void 0) payload.platform = platform;
|
|
705
707
|
if (environment !== void 0) payload.environment = environment;
|
|
708
|
+
if (magic_link_redirect_url !== void 0) payload.magic_link_redirect_url = magic_link_redirect_url;
|
|
709
|
+
if (auth_email_brand_name !== void 0) payload.auth_email_brand_name = auth_email_brand_name;
|
|
706
710
|
const result = await client.patch(`/projects/${project_id}`, payload);
|
|
707
711
|
return { content: [{
|
|
708
712
|
type: "text",
|
|
@@ -1489,6 +1493,13 @@ function jsonResult$1(payload) {
|
|
|
1489
1493
|
text: JSON.stringify(payload, null, 2)
|
|
1490
1494
|
}] };
|
|
1491
1495
|
}
|
|
1496
|
+
/** Preserve an HTTP response wrapper while mapping failures to MCP isError. */
|
|
1497
|
+
function httpResult(result) {
|
|
1498
|
+
return {
|
|
1499
|
+
...jsonResult$1(result),
|
|
1500
|
+
...result.status >= 400 ? { isError: true } : {}
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1492
1503
|
/**
|
|
1493
1504
|
* Flatten an upstream HTTP response into the agent-facing tool payload.
|
|
1494
1505
|
* Body fields are spread FIRST so a future top-level `status` key in
|
|
@@ -1496,10 +1507,13 @@ function jsonResult$1(payload) {
|
|
|
1496
1507
|
* `parsed.status` to distinguish 2xx from 4xx/5xx.
|
|
1497
1508
|
*/
|
|
1498
1509
|
function passthroughResult(result) {
|
|
1499
|
-
return
|
|
1500
|
-
...
|
|
1501
|
-
|
|
1502
|
-
|
|
1510
|
+
return {
|
|
1511
|
+
...jsonResult$1({
|
|
1512
|
+
...result.body !== null && typeof result.body === "object" ? result.body : {},
|
|
1513
|
+
status: result.status
|
|
1514
|
+
}),
|
|
1515
|
+
...result.status >= 400 ? { isError: true } : {}
|
|
1516
|
+
};
|
|
1503
1517
|
}
|
|
1504
1518
|
//#endregion
|
|
1505
1519
|
//#region src/tools/users.ts
|
|
@@ -5755,14 +5769,17 @@ function registerTools$18(server, apiClient) {
|
|
|
5755
5769
|
"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).",
|
|
5756
5770
|
"Optional `rate_limit` declares a per-function rate-limit config (validated server-side). Pass null/omit for no rate limit.",
|
|
5757
5771
|
"Optional `public` (default false) deploys the function WITHOUT the X-Api-Key gate, so third-party webhook senders (RevenueCat, Stripe, GitHub, Slack) that cannot attach a custom header can reach it. SECURITY: when public, YOU must verify the webhook signature (HMAC) inside the function body — the runtime no longer requires a caller credential. `public: true` only skips the key requirement; rate-limiting and the per-request signed identity context still apply. Leave it false for functions only your own app calls.",
|
|
5772
|
+
"Optional `forward_authorization` (default false) forwards a verified client Bearer to customer code. X-Api-Key and platform internal-trigger credentials are always stripped. Enable only when the function must validate or proxy the end-user session.",
|
|
5758
5773
|
"Returns the function deployment row + the public URL (`fn_url`: `https://{project_slug}.fn.amba.host/{name}`)."
|
|
5759
5774
|
].join(" "), {
|
|
5760
5775
|
project_id: z.string().describe("The Amba project ID."),
|
|
5761
5776
|
name: z.string().describe("Function name. Lowercase, /^[a-z][a-z0-9_-]*$/, ≤58 chars."),
|
|
5762
5777
|
code: z.string().describe("Bundled JavaScript module source (one entry file)."),
|
|
5763
5778
|
rate_limit: z.unknown().optional().describe("Optional rate-limit config object. Shape: see @layers/amba-shared:RateLimitConfig."),
|
|
5764
|
-
public: z.boolean().optional().describe("Deploy the function as PUBLIC (no X-Api-Key required) so external webhook senders can reach it. Default false. When true, you MUST verify the webhook HMAC signature inside the function body — public skips the credential gate only; rate-limiting and signed identity context still apply.")
|
|
5765
|
-
|
|
5779
|
+
public: z.boolean().optional().describe("Deploy the function as PUBLIC (no X-Api-Key required) so external webhook senders can reach it. Default false. When true, you MUST verify the webhook HMAC signature inside the function body — public skips the credential gate only; rate-limiting and signed identity context still apply."),
|
|
5780
|
+
forward_authorization: z.boolean().optional().describe("Forward a verified client Authorization Bearer to customer code. Default false. X-Api-Key and platform internal credentials are never forwarded."),
|
|
5781
|
+
mcp: z.unknown().optional().describe("Optional app-MCP tool config: {description?, input_schema?, annotations?}. Safety annotations support readOnlyHint, destructiveHint, idempotentHint, and openWorldHint booleans.")
|
|
5782
|
+
}, async ({ project_id, name, code, rate_limit, public: isPublic, forward_authorization: forwardAuthorization, mcp }, { pat }) => {
|
|
5766
5783
|
const byteLen = Buffer.byteLength(code, "utf8");
|
|
5767
5784
|
if (byteLen > MAX_BUNDLE_BYTES) throw new Error(`Bundle size ${byteLen} bytes exceeds ${MAX_BUNDLE_BYTES}-byte cap (10 MiB).`);
|
|
5768
5785
|
const form = new FormData();
|
|
@@ -5770,6 +5787,8 @@ function registerTools$18(server, apiClient) {
|
|
|
5770
5787
|
const metadata = { name };
|
|
5771
5788
|
if (rate_limit !== void 0 && rate_limit !== null) metadata.rate_limit = rate_limit;
|
|
5772
5789
|
if (isPublic === true) metadata.public = true;
|
|
5790
|
+
if (forwardAuthorization === true) metadata.forward_authorization = true;
|
|
5791
|
+
if (mcp !== void 0) metadata.mcp = mcp;
|
|
5773
5792
|
form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }), "metadata.json");
|
|
5774
5793
|
return jsonResult(await callWithPat(apiClient, pat, "POST", `/projects/${encodeURIComponent(project_id)}/functions/deploy`, { formData: form }));
|
|
5775
5794
|
}, ["amba_deploy_function"]);
|
|
@@ -5859,6 +5878,9 @@ function registerTools$18(server, apiClient) {
|
|
|
5859
5878
|
deleted: true
|
|
5860
5879
|
});
|
|
5861
5880
|
});
|
|
5881
|
+
registerTool(server, apiClient, "amba_functions_list_schedules", "List function schedules with their cron expression, timezone, pause state, next fire time, and most recent fire time. Rows degrade with enriched=false if one provider describe call fails.", { project_id: z.string().describe("The Amba project ID.") }, async ({ project_id }, { pat }) => {
|
|
5882
|
+
return jsonResult(await callWithPat(apiClient, pat, "GET", `/projects/${encodeURIComponent(project_id)}/functions/schedules`));
|
|
5883
|
+
});
|
|
5862
5884
|
registerTool(server, apiClient, "amba_functions_schedule", [
|
|
5863
5885
|
"Register a cron Schedule for a function. The schedule is keyed deterministically per (project, function) — re-calling with a different cron/timezone replaces the prior schedule rather than creating a duplicate.",
|
|
5864
5886
|
"Cron must be a standard 5- or 6-field whitespace-separated expression. `timezone` defaults to \"UTC\" if omitted. Timezones are IANA strings (e.g. \"America/Los_Angeles\").",
|
|
@@ -7996,7 +8018,7 @@ There is no separate "identity provisioning" step — \`auth\` is the default su
|
|
|
7996
8018
|
| --- | --- | --- |
|
|
7997
8019
|
| \`amba_developer_me\` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | \`{}\` |
|
|
7998
8020
|
| \`amba_projects_get\` | Read a project's config (bundle id, OAuth client id, platform). | \`{ project_id }\` |
|
|
7999
|
-
| \`amba_projects_update\` | Set
|
|
8021
|
+
| \`amba_projects_update\` | Set social-login audiences plus the exact magic-link callback and auth-email brand. | \`{ project_id, magic_link_redirect_url: "https://app.example.com/auth/callback", auth_email_brand_name: "My App" }\` |
|
|
8000
8022
|
| \`amba_users_list\` | Browse end-users (app_users) of the project — useful as a smoke check after the first sign-in. | \`{ project_id, limit: 20 }\` |
|
|
8001
8023
|
| \`amba_users_get\` | Fetch a single app_user by id. | \`{ project_id, user_id }\` |
|
|
8002
8024
|
| \`amba_users_bulk_update\` | Set custom properties on many users at once. | \`{ project_id, user_ids: [...], properties: { tier: "trial" } }\` |
|
|
@@ -10148,6 +10170,7 @@ const TOOL_CATEGORY = {
|
|
|
10148
10170
|
amba_functions_get_logs: "infrastructure",
|
|
10149
10171
|
amba_get_function_logs: "infrastructure",
|
|
10150
10172
|
amba_functions_schedule: "infrastructure",
|
|
10173
|
+
amba_functions_list_schedules: "infrastructure",
|
|
10151
10174
|
amba_schedule_function: "infrastructure",
|
|
10152
10175
|
amba_functions_pause_schedule: "infrastructure",
|
|
10153
10176
|
amba_pause_function_schedule: "infrastructure",
|
|
@@ -10706,6 +10729,7 @@ async function adminFetch(apiClient, bearer, call) {
|
|
|
10706
10729
|
async function clientFetch(clientAuth, call) {
|
|
10707
10730
|
const headers = { "X-Api-Key": clientAuth.apiKey };
|
|
10708
10731
|
if (clientAuth.sessionToken) headers["Authorization"] = `Bearer ${clientAuth.sessionToken}`;
|
|
10732
|
+
if (clientAuth.groupContext) headers["X-Group-Context"] = clientAuth.groupContext;
|
|
10709
10733
|
return proxyFetch(clientAuth.baseUrl, headers, call);
|
|
10710
10734
|
}
|
|
10711
10735
|
/**
|
|
@@ -11067,23 +11091,23 @@ function registerFunctionToolsFor(server, options) {
|
|
|
11067
11091
|
});
|
|
11068
11092
|
if (declaredShape) {
|
|
11069
11093
|
registerPublicTool(server, toolName, description, declaredShape, async (args) => {
|
|
11070
|
-
return
|
|
11094
|
+
return httpResult(await invokeFunction({
|
|
11071
11095
|
invokeUrl: fn.invoke_url,
|
|
11072
11096
|
clientAuth,
|
|
11073
11097
|
body: args
|
|
11074
11098
|
}));
|
|
11075
|
-
});
|
|
11099
|
+
}, [], fn.annotations ?? void 0);
|
|
11076
11100
|
return;
|
|
11077
11101
|
}
|
|
11078
11102
|
registerPublicTool(server, toolName, description, GENERIC_FUNCTION_SHAPE, async (args) => {
|
|
11079
11103
|
const { body, query } = args;
|
|
11080
|
-
return
|
|
11104
|
+
return httpResult(await invokeFunction({
|
|
11081
11105
|
invokeUrl: fn.invoke_url,
|
|
11082
11106
|
clientAuth,
|
|
11083
11107
|
body,
|
|
11084
11108
|
...query ? { query } : {}
|
|
11085
11109
|
}));
|
|
11086
|
-
});
|
|
11110
|
+
}, [], fn.annotations ?? void 0);
|
|
11087
11111
|
}
|
|
11088
11112
|
//#endregion
|
|
11089
11113
|
//#region src/auto/index.ts
|
|
@@ -11222,7 +11246,8 @@ async function registerAutoMcpTools(server, apiClient, options) {
|
|
|
11222
11246
|
fn,
|
|
11223
11247
|
clientAuth: {
|
|
11224
11248
|
apiKey: clientAuth.apiKey,
|
|
11225
|
-
...clientAuth.sessionToken ? { sessionToken: clientAuth.sessionToken } : {}
|
|
11249
|
+
...clientAuth.sessionToken ? { sessionToken: clientAuth.sessionToken } : {},
|
|
11250
|
+
...clientAuth.groupContext ? { groupContext: clientAuth.groupContext } : {}
|
|
11226
11251
|
}
|
|
11227
11252
|
});
|
|
11228
11253
|
return {
|
|
@@ -34,6 +34,17 @@ export declare function jsonResult(payload: unknown): {
|
|
|
34
34
|
text: string;
|
|
35
35
|
}[];
|
|
36
36
|
};
|
|
37
|
+
/** Preserve an HTTP response wrapper while mapping failures to MCP isError. */
|
|
38
|
+
export declare function httpResult(result: {
|
|
39
|
+
status: number;
|
|
40
|
+
body: unknown;
|
|
41
|
+
}): {
|
|
42
|
+
isError?: boolean | undefined;
|
|
43
|
+
content: {
|
|
44
|
+
type: "text";
|
|
45
|
+
text: string;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
37
48
|
/**
|
|
38
49
|
* Flatten an upstream HTTP response into the agent-facing tool payload.
|
|
39
50
|
* Body fields are spread FIRST so a future top-level `status` key in
|
|
@@ -44,6 +55,7 @@ export declare function passthroughResult(result: {
|
|
|
44
55
|
status: number;
|
|
45
56
|
body: unknown;
|
|
46
57
|
}): {
|
|
58
|
+
isError?: boolean | undefined;
|
|
47
59
|
content: {
|
|
48
60
|
type: "text";
|
|
49
61
|
text: string;
|
package/dist/lib/with-pat.d.ts
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* the same call shape without the pat machinery.
|
|
42
42
|
*/
|
|
43
43
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
44
|
+
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
44
45
|
import { z, type ZodRawShape } from 'zod';
|
|
45
46
|
import type { ApiClient } from '../api-client.js';
|
|
46
47
|
/** Wire-shape returned by an MCP tool handler. */
|
|
@@ -49,6 +50,8 @@ export interface ToolResult {
|
|
|
49
50
|
type: 'text';
|
|
50
51
|
text: string;
|
|
51
52
|
}>;
|
|
53
|
+
/** MCP-native failure signal. Tool transports still return HTTP 200. */
|
|
54
|
+
isError?: boolean;
|
|
52
55
|
}
|
|
53
56
|
/** Context passed to handlers registered via `registerTool`. */
|
|
54
57
|
export interface WithPatHandlerContext {
|
|
@@ -122,4 +125,6 @@ export declare function registerPublicTool<S extends ZodRawShape>(server: McpSer
|
|
|
122
125
|
* warning per alias per process. No active auth-tool rename uses this
|
|
123
126
|
* yet, but the parameter exists so future renames stay consistent.
|
|
124
127
|
*/
|
|
125
|
-
aliases?: readonly string[]
|
|
128
|
+
aliases?: readonly string[],
|
|
129
|
+
/** Explicit app-declared safety hints; absent keeps conservative derivation. */
|
|
130
|
+
annotations?: ToolAnnotations): void;
|
|
@@ -15,6 +15,6 @@
|
|
|
15
15
|
* canonical names (`amba_api_keys_create`) so agents pattern-match
|
|
16
16
|
* against the modern shape. Drift gate in `amba-setup.test.ts`.
|
|
17
17
|
*/
|
|
18
|
-
export declare const AMBA_SETUP_IDENTITY_MD = "# Identity\n\nEnd-user authentication for an Amba project: anonymous sessions, email/password, email OTP, SMS OTP, magic links, Sign in with Apple, Sign in with Google, and account linking. All flows return an `AuthResult` containing a `user` + a session token; the SDK persists tokens to the platform's native secure storage and replays them on the next launch. Subsequent SDK calls (collections, push, XP, etc.) are authenticated as the signed-in user automatically.\n\nThere is no separate \"identity provisioning\" step \u2014 `auth` is the default surface for every project. Your job here is to (a) wire `Amba.configure(...)` plus the right sign-in calls into the user's entry file, and (b) where the user wants social sign-in, set the audience identifiers on the project so the server can verify identity tokens.\n\n## MCP tools\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_developer_me` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | `{}` |\n| `amba_projects_get` | Read a project's config (bundle id, OAuth client id, platform). | `{ project_id }` |\n| `amba_projects_update` | Set
|
|
18
|
+
export declare const AMBA_SETUP_IDENTITY_MD = "# Identity\n\nEnd-user authentication for an Amba project: anonymous sessions, email/password, email OTP, SMS OTP, magic links, Sign in with Apple, Sign in with Google, and account linking. All flows return an `AuthResult` containing a `user` + a session token; the SDK persists tokens to the platform's native secure storage and replays them on the next launch. Subsequent SDK calls (collections, push, XP, etc.) are authenticated as the signed-in user automatically.\n\nThere is no separate \"identity provisioning\" step \u2014 `auth` is the default surface for every project. Your job here is to (a) wire `Amba.configure(...)` plus the right sign-in calls into the user's entry file, and (b) where the user wants social sign-in, set the audience identifiers on the project so the server can verify identity tokens.\n\n## MCP tools\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_developer_me` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | `{}` |\n| `amba_projects_get` | Read a project's config (bundle id, OAuth client id, platform). | `{ project_id }` |\n| `amba_projects_update` | Set social-login audiences plus the exact magic-link callback and auth-email brand. | `{ project_id, magic_link_redirect_url: \"https://app.example.com/auth/callback\", auth_email_brand_name: \"My App\" }` |\n| `amba_users_list` | Browse end-users (app_users) of the project \u2014 useful as a smoke check after the first sign-in. | `{ project_id, limit: 20 }` |\n| `amba_users_get` | Fetch a single app_user by id. | `{ project_id, user_id }` |\n| `amba_users_bulk_update` | Set custom properties on many users at once. | `{ project_id, user_ids: [...], properties: { tier: \"trial\" } }` |\n| `amba_api_keys_create` | Mint additional client/server keys (e.g. a separate `production` key). | `{ project_id, key_type: \"client\", environment: \"production\" }` |\n| `amba_api_keys_delete` | Revoke a leaked key. | `{ project_id, api_key_id }` |\n| `amba_roles_assign` | Grant an RBAC role to an app_user (admin / moderator / etc.). | `{ project_id, user_id, role_id }` |\n\nThere's no `amba_auth_*` namespace \u2014 auth is owned by the SDK on the client side, and there are no provisioning calls for it beyond setting the project's audience identifiers. If the user wants Apple / Google sign-in, the **mandatory** preflight is:\n\n```\namba_projects_update({\n project_id: \"<from Step 0>\",\n bundle_id: \"<their iOS bundle id>\",\n google_oauth_client_id: \"<their Google OAuth client id>\"\n})\n```\n\nWithout this, the server rejects identity tokens with `AUDIENCE_NOT_CONFIGURED` and the user thinks Amba is broken. If they don't know their bundle id, ask; if they don't have a Google OAuth client yet, tell them to create one at `console.cloud.google.com` and link it later via `amba_projects_update`.\n\n## SDK init per stack\n\n### Expo\n\n```bash\nnpx expo install @layers/amba-expo @react-native-async-storage/async-storage\n```\n\nIn `app/_layout.tsx` (or whatever your root layout is):\n\n```tsx\nimport { useEffect } from 'react';\nimport { Amba } from '@layers/amba-expo';\n\nexport default function RootLayout() {\n useEffect(() => {\n (async () => {\n await Amba.configure({\n apiKey: process.env.EXPO_PUBLIC_AMBA_CLIENT_KEY!,\n });\n await Amba.auth.signInAnonymously();\n })();\n }, []);\n return /* \u2026 */ null;\n}\n```\n\nFor Sign in with Apple, add `expo-apple-authentication`; capture the identity token and call `Amba.auth.signInWithApple(identityToken)`. For Sign in with Google, use `expo-auth-session/providers/google` and call `Amba.auth.signInWithGoogle(id_token)`.\n\n### React Native (bare)\n\n```bash\nnpm install @layers/amba-react-native @react-native-async-storage/async-storage\n```\n\n```tsx\nimport { Amba } from '@layers/amba-react-native';\n\nawait Amba.configure({ apiKey: process.env.AMBA_CLIENT_KEY! });\nawait Amba.auth.signInAnonymously();\n\n// Email OTP\nawait Amba.auth.requestEmailOtp(email);\nawait Amba.auth.verifyEmailOtp(email, code);\n\n// SMS OTP (E.164, leading \"+\")\nawait Amba.auth.requestSmsOtp('+14155551234');\nawait Amba.auth.verifySmsOtp('+14155551234', code);\n```\n\n### Web (browser / Next.js / Vite / Remix)\n\n```bash\nnpm install @layers/amba-web\n# Optional React hooks:\nnpm install @layers/amba-react\n```\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nawait Amba.configure({ apiKey: import.meta.env.VITE_AMBA_CLIENT_KEY });\nawait Amba.auth.signInAnonymously();\n\n// Magic link\nawait Amba.auth.requestMagicLink('user@example.com');\nconst token = new URLSearchParams(window.location.search).get('token');\nif (token) await Amba.auth.verifyMagicLink(token);\n```\n\nNext.js \u2014 call `Amba.configure(...)` once at the top of `app/layout.tsx` (or `pages/_app.tsx`). Anonymous sign-in should happen on the client; do not call SDK functions in server components.\n\n### iOS (Swift, SPM)\n\nIn `Package.swift` (or Xcode \u2192 File \u2192 Add Package Dependencies):\n\n```swift\n.package(url: \"https://github.com/layers/amba-sdk-ios\", from: \"1.0.0\")\n```\n\n```swift\nimport SwiftUI\nimport Amba\n\n@main\nstruct MyApp: App {\n init() {\n Task {\n try await Amba.configure(apiKey: ProcessInfo.processInfo.environment[\"AMBA_CLIENT_KEY\"]!)\n try await Amba.auth.signInAnonymously()\n }\n }\n var body: some Scene { WindowGroup { ContentView() } }\n}\n```\n\nSign in with Apple \u2014 use Apple's `AuthenticationServices` framework; pass the `identityToken` to `Amba.auth.signInWithApple`. Sign in with Google \u2014 use Google's `GoogleSignIn-iOS` SDK; pass the `idToken` to `Amba.auth.signInWithGoogle`.\n\n> Add the \"Sign in with Apple\" capability in **Xcode \u2192 target \u2192 Signing & Capabilities \u2192 + Capability**. Without it, the Apple auth call fails before it reaches Amba.\n\n### Android (Kotlin)\n\nIn `app/build.gradle.kts`:\n\n```kotlin\ndependencies {\n implementation(\"com.layers.amba:amba-sdk-android:0.1.0\")\n}\n```\n\nIn your `Application` subclass:\n\n```kotlin\nimport android.app.Application\nimport com.layers.amba.Amba\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\n\nclass MyApp : Application() {\n override fun onCreate() {\n super.onCreate()\n GlobalScope.launch {\n Amba.configure(apiKey = BuildConfig.AMBA_CLIENT_KEY)\n Amba.auth.signInAnonymously()\n }\n }\n}\n```\n\nSign in with Google \u2014 use Google's Credential Manager flow, capture `idToken`, then `Amba.auth.signInWithGoogle(idToken = idToken)`.\n\n### Flutter\n\n```yaml\ndependencies:\n amba: ^1.0.0\n```\n\n```dart\nimport 'package:amba/amba.dart';\n\nFuture<void> main() async {\n WidgetsFlutterBinding.ensureInitialized();\n await Amba.configure(apiKey: const String.fromEnvironment('AMBA_CLIENT_KEY'));\n await Amba.auth.signInAnonymously();\n runApp(const MyApp());\n}\n```\n\nPass the key in: `flutter run --dart-define=AMBA_CLIENT_KEY=$AMBA_CLIENT_KEY`. Apple: `sign_in_with_apple` plugin \u2192 `Amba.auth.signInWithApple`. Google: `google_sign_in` plugin \u2192 `Amba.auth.signInWithGoogle`.\n\n## Common follow-ups\n\nAsk one bundled multi-choice \u2014 don't drip-feed.\n\n1. **Which sign-in methods do you want?** (multi-select)\n - [x] Anonymous (recommended \u2014 call at app start, lets users use the app immediately)\n - [ ] Email + password\n - [ ] Email OTP (6-digit code emailed)\n - [ ] Magic link (single click email)\n - [ ] Phone OTP / SMS (E.164, requires SMS provider configured)\n - [ ] Sign in with Apple (iOS / web; required for iOS apps that have any third-party auth per App Store guideline 4.8)\n - [ ] Sign in with Google (Android / iOS / web)\n\n2. **If Apple is selected:** what's your iOS bundle id?\n\n3. **If Google is selected:** what's your Google OAuth client id? Format: `123456789-abc.apps.googleusercontent.com`. If they don't have one, point them at `console.cloud.google.com` and proceed without it \u2014 they can paste it later via `amba_projects_update`.\n\n4. **If anonymous is selected:** when do you want users to upgrade?\n - On a \"Save your progress\" prompt (offer Apple/Google linking)\n - Behind a paywall / premium gate\n - Never auto-prompt (user upgrades from settings)\n - Defaults to \"never auto-prompt\".\n\n## Re-run behavior\n\nOn a second invocation that targets identity:\n\n1. Call `amba_projects_get({ project_id })` to read current `bundle_id` and `google_oauth_client_id`. Compare to what the user gave you:\n - If both already set \u2192 no `amba_projects_update` needed.\n - If user is adding a new social provider that needs an audience \u2192 call `amba_projects_update` with just the new field. Don't blow away the existing one.\n\n2. For new sign-in methods, append the per-method code block to the existing entry file *without* re-emitting `Amba.configure(...)` (it's already there). Detection: search for `Amba.configure` in the entry file; if present, skip the configure block.\n\n3. If the user asks to \"switch from anonymous to email-only\" or similar destructive change, **don't auto-do it**. Explain that existing anonymous user data would be unreachable without a link flow, then offer:\n - Add the new method alongside anonymous (recommended)\n - Add a forced upgrade prompt in onboarding\n - Migrate manually via `Amba.auth.linkEmailOtp(email, code)` \u2014 keeps existing user data\n";
|
|
19
19
|
export declare const AMBA_SETUP_IDENTITY_URI = "amba://setup/identity";
|
|
20
20
|
export declare const AMBA_SETUP_IDENTITY_MIME = "text/markdown";
|
|
@@ -53,7 +53,7 @@ export declare const AMBA_SETUP_SUB_RESOURCES: readonly [{
|
|
|
53
53
|
readonly name: "amba-setup-identity";
|
|
54
54
|
readonly uri: "amba://setup/identity";
|
|
55
55
|
readonly mime: "text/markdown";
|
|
56
|
-
readonly body: "# Identity\n\nEnd-user authentication for an Amba project: anonymous sessions, email/password, email OTP, SMS OTP, magic links, Sign in with Apple, Sign in with Google, and account linking. All flows return an `AuthResult` containing a `user` + a session token; the SDK persists tokens to the platform's native secure storage and replays them on the next launch. Subsequent SDK calls (collections, push, XP, etc.) are authenticated as the signed-in user automatically.\n\nThere is no separate \"identity provisioning\" step — `auth` is the default surface for every project. Your job here is to (a) wire `Amba.configure(...)` plus the right sign-in calls into the user's entry file, and (b) where the user wants social sign-in, set the audience identifiers on the project so the server can verify identity tokens.\n\n## MCP tools\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_developer_me` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | `{}` |\n| `amba_projects_get` | Read a project's config (bundle id, OAuth client id, platform). | `{ project_id }` |\n| `amba_projects_update` | Set
|
|
56
|
+
readonly body: "# Identity\n\nEnd-user authentication for an Amba project: anonymous sessions, email/password, email OTP, SMS OTP, magic links, Sign in with Apple, Sign in with Google, and account linking. All flows return an `AuthResult` containing a `user` + a session token; the SDK persists tokens to the platform's native secure storage and replays them on the next launch. Subsequent SDK calls (collections, push, XP, etc.) are authenticated as the signed-in user automatically.\n\nThere is no separate \"identity provisioning\" step — `auth` is the default surface for every project. Your job here is to (a) wire `Amba.configure(...)` plus the right sign-in calls into the user's entry file, and (b) where the user wants social sign-in, set the audience identifiers on the project so the server can verify identity tokens.\n\n## MCP tools\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_developer_me` | Verify the developer PAT and read the developer's profile. Pre-flight check before any provisioning. | `{}` |\n| `amba_projects_get` | Read a project's config (bundle id, OAuth client id, platform). | `{ project_id }` |\n| `amba_projects_update` | Set social-login audiences plus the exact magic-link callback and auth-email brand. | `{ project_id, magic_link_redirect_url: \"https://app.example.com/auth/callback\", auth_email_brand_name: \"My App\" }` |\n| `amba_users_list` | Browse end-users (app_users) of the project — useful as a smoke check after the first sign-in. | `{ project_id, limit: 20 }` |\n| `amba_users_get` | Fetch a single app_user by id. | `{ project_id, user_id }` |\n| `amba_users_bulk_update` | Set custom properties on many users at once. | `{ project_id, user_ids: [...], properties: { tier: \"trial\" } }` |\n| `amba_api_keys_create` | Mint additional client/server keys (e.g. a separate `production` key). | `{ project_id, key_type: \"client\", environment: \"production\" }` |\n| `amba_api_keys_delete` | Revoke a leaked key. | `{ project_id, api_key_id }` |\n| `amba_roles_assign` | Grant an RBAC role to an app_user (admin / moderator / etc.). | `{ project_id, user_id, role_id }` |\n\nThere's no `amba_auth_*` namespace — auth is owned by the SDK on the client side, and there are no provisioning calls for it beyond setting the project's audience identifiers. If the user wants Apple / Google sign-in, the **mandatory** preflight is:\n\n```\namba_projects_update({\n project_id: \"<from Step 0>\",\n bundle_id: \"<their iOS bundle id>\",\n google_oauth_client_id: \"<their Google OAuth client id>\"\n})\n```\n\nWithout this, the server rejects identity tokens with `AUDIENCE_NOT_CONFIGURED` and the user thinks Amba is broken. If they don't know their bundle id, ask; if they don't have a Google OAuth client yet, tell them to create one at `console.cloud.google.com` and link it later via `amba_projects_update`.\n\n## SDK init per stack\n\n### Expo\n\n```bash\nnpx expo install @layers/amba-expo @react-native-async-storage/async-storage\n```\n\nIn `app/_layout.tsx` (or whatever your root layout is):\n\n```tsx\nimport { useEffect } from 'react';\nimport { Amba } from '@layers/amba-expo';\n\nexport default function RootLayout() {\n useEffect(() => {\n (async () => {\n await Amba.configure({\n apiKey: process.env.EXPO_PUBLIC_AMBA_CLIENT_KEY!,\n });\n await Amba.auth.signInAnonymously();\n })();\n }, []);\n return /* … */ null;\n}\n```\n\nFor Sign in with Apple, add `expo-apple-authentication`; capture the identity token and call `Amba.auth.signInWithApple(identityToken)`. For Sign in with Google, use `expo-auth-session/providers/google` and call `Amba.auth.signInWithGoogle(id_token)`.\n\n### React Native (bare)\n\n```bash\nnpm install @layers/amba-react-native @react-native-async-storage/async-storage\n```\n\n```tsx\nimport { Amba } from '@layers/amba-react-native';\n\nawait Amba.configure({ apiKey: process.env.AMBA_CLIENT_KEY! });\nawait Amba.auth.signInAnonymously();\n\n// Email OTP\nawait Amba.auth.requestEmailOtp(email);\nawait Amba.auth.verifyEmailOtp(email, code);\n\n// SMS OTP (E.164, leading \"+\")\nawait Amba.auth.requestSmsOtp('+14155551234');\nawait Amba.auth.verifySmsOtp('+14155551234', code);\n```\n\n### Web (browser / Next.js / Vite / Remix)\n\n```bash\nnpm install @layers/amba-web\n# Optional React hooks:\nnpm install @layers/amba-react\n```\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nawait Amba.configure({ apiKey: import.meta.env.VITE_AMBA_CLIENT_KEY });\nawait Amba.auth.signInAnonymously();\n\n// Magic link\nawait Amba.auth.requestMagicLink('user@example.com');\nconst token = new URLSearchParams(window.location.search).get('token');\nif (token) await Amba.auth.verifyMagicLink(token);\n```\n\nNext.js — call `Amba.configure(...)` once at the top of `app/layout.tsx` (or `pages/_app.tsx`). Anonymous sign-in should happen on the client; do not call SDK functions in server components.\n\n### iOS (Swift, SPM)\n\nIn `Package.swift` (or Xcode → File → Add Package Dependencies):\n\n```swift\n.package(url: \"https://github.com/layers/amba-sdk-ios\", from: \"1.0.0\")\n```\n\n```swift\nimport SwiftUI\nimport Amba\n\n@main\nstruct MyApp: App {\n init() {\n Task {\n try await Amba.configure(apiKey: ProcessInfo.processInfo.environment[\"AMBA_CLIENT_KEY\"]!)\n try await Amba.auth.signInAnonymously()\n }\n }\n var body: some Scene { WindowGroup { ContentView() } }\n}\n```\n\nSign in with Apple — use Apple's `AuthenticationServices` framework; pass the `identityToken` to `Amba.auth.signInWithApple`. Sign in with Google — use Google's `GoogleSignIn-iOS` SDK; pass the `idToken` to `Amba.auth.signInWithGoogle`.\n\n> Add the \"Sign in with Apple\" capability in **Xcode → target → Signing & Capabilities → + Capability**. Without it, the Apple auth call fails before it reaches Amba.\n\n### Android (Kotlin)\n\nIn `app/build.gradle.kts`:\n\n```kotlin\ndependencies {\n implementation(\"com.layers.amba:amba-sdk-android:0.1.0\")\n}\n```\n\nIn your `Application` subclass:\n\n```kotlin\nimport android.app.Application\nimport com.layers.amba.Amba\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\n\nclass MyApp : Application() {\n override fun onCreate() {\n super.onCreate()\n GlobalScope.launch {\n Amba.configure(apiKey = BuildConfig.AMBA_CLIENT_KEY)\n Amba.auth.signInAnonymously()\n }\n }\n}\n```\n\nSign in with Google — use Google's Credential Manager flow, capture `idToken`, then `Amba.auth.signInWithGoogle(idToken = idToken)`.\n\n### Flutter\n\n```yaml\ndependencies:\n amba: ^1.0.0\n```\n\n```dart\nimport 'package:amba/amba.dart';\n\nFuture<void> main() async {\n WidgetsFlutterBinding.ensureInitialized();\n await Amba.configure(apiKey: const String.fromEnvironment('AMBA_CLIENT_KEY'));\n await Amba.auth.signInAnonymously();\n runApp(const MyApp());\n}\n```\n\nPass the key in: `flutter run --dart-define=AMBA_CLIENT_KEY=$AMBA_CLIENT_KEY`. Apple: `sign_in_with_apple` plugin → `Amba.auth.signInWithApple`. Google: `google_sign_in` plugin → `Amba.auth.signInWithGoogle`.\n\n## Common follow-ups\n\nAsk one bundled multi-choice — don't drip-feed.\n\n1. **Which sign-in methods do you want?** (multi-select)\n - [x] Anonymous (recommended — call at app start, lets users use the app immediately)\n - [ ] Email + password\n - [ ] Email OTP (6-digit code emailed)\n - [ ] Magic link (single click email)\n - [ ] Phone OTP / SMS (E.164, requires SMS provider configured)\n - [ ] Sign in with Apple (iOS / web; required for iOS apps that have any third-party auth per App Store guideline 4.8)\n - [ ] Sign in with Google (Android / iOS / web)\n\n2. **If Apple is selected:** what's your iOS bundle id?\n\n3. **If Google is selected:** what's your Google OAuth client id? Format: `123456789-abc.apps.googleusercontent.com`. If they don't have one, point them at `console.cloud.google.com` and proceed without it — they can paste it later via `amba_projects_update`.\n\n4. **If anonymous is selected:** when do you want users to upgrade?\n - On a \"Save your progress\" prompt (offer Apple/Google linking)\n - Behind a paywall / premium gate\n - Never auto-prompt (user upgrades from settings)\n - Defaults to \"never auto-prompt\".\n\n## Re-run behavior\n\nOn a second invocation that targets identity:\n\n1. Call `amba_projects_get({ project_id })` to read current `bundle_id` and `google_oauth_client_id`. Compare to what the user gave you:\n - If both already set → no `amba_projects_update` needed.\n - If user is adding a new social provider that needs an audience → call `amba_projects_update` with just the new field. Don't blow away the existing one.\n\n2. For new sign-in methods, append the per-method code block to the existing entry file *without* re-emitting `Amba.configure(...)` (it's already there). Detection: search for `Amba.configure` in the entry file; if present, skip the configure block.\n\n3. If the user asks to \"switch from anonymous to email-only\" or similar destructive change, **don't auto-do it**. Explain that existing anonymous user data would be unreachable without a link flow, then offer:\n - Add the new method alongside anonymous (recommended)\n - Add a forced upgrade prompt in onboarding\n - Migrate manually via `Amba.auth.linkEmailOtp(email, code)` — keeps existing user data\n";
|
|
57
57
|
readonly surface: "identity";
|
|
58
58
|
readonly title: "Amba setup — identity";
|
|
59
59
|
readonly description: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@layers/amba-mcp",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
28
28
|
"zod": "^3.25.0",
|
|
29
|
-
"@layers/amba-shared": "4.0.
|
|
29
|
+
"@layers/amba-shared": "4.0.5"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.10.2",
|