@layers/amba-mcp 4.0.6 → 4.0.7
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 +42 -20
- package/dist/auto/describe.d.ts +21 -3
- package/dist/auto/function-tools.d.ts +51 -0
- package/dist/auto/index.d.ts +65 -21
- package/dist/auto/json-schema-to-zod.d.ts +37 -0
- package/dist/expo-build-prompt.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1556 -401
- package/dist/resources/amba-setup-infrastructure.d.ts +5 -4
- package/dist/resources/amba-setup.d.ts +1 -1
- package/dist/resources/expo-build-prompt.d.ts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/tools/affiliate.d.ts +12 -0
- package/dist/tools/agent-checkout.d.ts +17 -0
- package/dist/tools/ai-prompts-admin.d.ts +3 -2
- package/dist/tools/app-mcp.d.ts +22 -0
- package/dist/tools/domains.d.ts +2 -0
- package/dist/tools/orgs.d.ts +8 -0
- package/dist/tools/payments.d.ts +1 -0
- package/dist/tools/promotion.d.ts +5 -1
- package/dist/tools/secrets.d.ts +3 -4
- package/dist/tools/service-accounts.d.ts +10 -0
- package/package.json +1 -1
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* Companion to `AMBA_SETUP_GUIDE_MD`. Step 3 of the main playbook
|
|
5
5
|
* tells the agent to fetch this resource when wiring infrastructure.
|
|
6
6
|
*
|
|
7
|
-
* Surface scope: collections (
|
|
7
|
+
* Surface scope: collections (relational Postgres tables), functions
|
|
8
8
|
* (serverless), analytics, AI prompts, secrets, configs / feature
|
|
9
9
|
* flags, third-party integrations, media (file storage + CDN),
|
|
10
10
|
* sites (static asset hosting).
|
|
11
11
|
*
|
|
12
12
|
* Twin: `packages/cli/skill-bundle/references/infrastructure.md`.
|
|
13
|
-
* Drift gate in `amba-setup.test.ts`.
|
|
14
|
-
*
|
|
13
|
+
* Drift gate in `amba-setup.test.ts`. Collections are named as relational
|
|
14
|
+
* Postgres tables; the hosting supplier (Neon) and the rest of the infra
|
|
15
|
+
* stack (Temporal / R2) stay scrubbed before exposing on the wire.
|
|
15
16
|
*/
|
|
16
|
-
export declare const AMBA_SETUP_INFRASTRUCTURE_MD = "# Infrastructure\n\nThe plumbing that sits behind every other surface: custom database tables (Collections \u2014 schema-first, per-tenant), serverless functions (run server-side code without standing up a backend), analytics (events + sessions), AI prompts (managed LLM templates, callable from the SDK with per-tenant keys), secrets, runtime configs, feature flags, third-party integrations (RevenueCat / Superwall / Resend / Stripe / etc.), media (file storage + CDN), and sites (static asset hosting at `*.app.amba.host`).\n\nIf gamification, economy, and social are the playable surface, **infrastructure is what you build a custom product on top of**. Anything that doesn't fit the canned surfaces lands here.\n\n## MCP tools\n\n### Collections (typed tables)\n\nA 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 \u2014 admin tools bypass this.\n\nAdmin 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`) \u2014 NOT `project_id` and NOT a `pat`. Every row tool names the collection with `name`, never `collection`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `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 }` |\n| `amba_collections_list` | List collections in this project. | `{ project_id }` |\n| `amba_collections_get` | Read one collection's schema. | `{ project_id, name: \"todos\" }` |\n| `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: \"integer\", nullable: true } }` |\n| `amba_collections_delete` | Drop the table (destructive). `confirm` must equal the collection name. | `{ project_id, name: \"todos\", confirm: \"todos\" }` |\n| `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 } }` |\n| `amba_admin_insert_rows` | Bulk-insert up to 500 rows in one atomic statement \u2014 the canonical seeding/migration path. `on_conflict`: `\"error\"` (default) or `\"skip\"`. | `{ project_id, name: \"questions\", rows: [{ q: \"...\" }, { q: \"...\" }], on_conflict: \"skip\" }` |\n| `amba_admin_list_rows` | Read rows as the developer. | `{ project_id, name: \"todos\", limit: 100 }` |\n| `amba_client_insert_row` | Insert as an end-user. Requires `api_key` (+ `session_token`). | `{ api_key, session_token, name: \"todos\", row: {...} }` |\n| `amba_client_list_rows` | Read as an end-user (auto user-scoped). | `{ api_key, session_token, name: \"todos\" }` |\n| `amba_client_get_row` | Get one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `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: {...} }` |\n| `amba_client_delete_row` | Soft-delete one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `amba_client_count_rows` | Count rows matching an optional `where`. | `{ api_key, session_token, name: \"todos\", where: {...} }` |\n| `amba_client_find_rows` | Filter / sort / paginate rows (SDK-shaped `filter`). | `{ api_key, session_token, name: \"todos\", filter: {...}, order: [\"created_at desc\"], limit: 50 }` |\n| `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 }` |\n\nColumn types: `text`, `integer`, `bigint`, `numeric`, `boolean`, `timestamptz`, `date`, `jsonb`, `uuid`, `vector(<dim>)` (e.g. `vector(1536)` for OpenAI embeddings), plus array forms `text[]`, `integer[]`, `bigint[]`, `numeric[]`, `boolean[]`, `uuid[]`. Use `integer` (not `int`), `numeric` (not `float`/`real`/`double`), and `jsonb` (not `json`) \u2014 the validator rejects the aliases.\n\n### Functions (serverless code)\n\nRun user code in a sandbox triggered by HTTP, cron, or webhook. The function gets the tenant connection automatically via injected env.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_functions_deploy` | Deploy a function from source. | `{ project_id, name: \"send_welcome_email\", runtime: \"node22\", source: \"export default async (req) => { ... }\", trigger: { type: \"http\" } }` |\n| `amba_functions_list` | List functions. | `{ project_id }` |\n| `amba_functions_get` | Read function metadata. | `{ project_id, function_id }` |\n| `amba_functions_get_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |\n| `amba_functions_delete` | Delete a function. | `{ project_id, function_id }` |\n| `amba_functions_schedule` | Attach a cron schedule. | `{ project_id, function_id, cron: \"0 9 * * *\", timezone: \"America/Los_Angeles\" }` |\n| `amba_functions_pause_schedule` | Pause a scheduled trigger without deleting it. | `{ project_id, function_id }` |\n| `amba_functions_resume_schedule` | Resume. | `{ project_id, function_id }` |\n| `amba_functions_trigger_schedule` | Fire a scheduled function ad-hoc (testing). | `{ project_id, function_id }` |\n\n### AI prompts\n\nManaged LLM templates: a stored prompt with provider + model + system message, invoked by name from the SDK. The actual LLM call is rewritten server-side per-tenant \u2014 the customer's provider API key (Anthropic / OpenAI / Mistral / Gemini) stays server-side, never on the device.\n\n**Two steps, in order:** first register the provider key with `amba_ai_providers_set`, then create prompts against it. A prompt registered before its provider has a key still saves, but invocations fail with `provider_not_configured` (424) until the key is set.\n\n> The provider key is **not** a function secret. `amba_secrets_set` writes function-scoped Worker secrets, which the AI gateway never reads. Provider keys live in a separate gateway-owned store and are set **only** via `amba_ai_providers_set`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_ai_providers_set` | Register / rotate the upstream provider API key. **Do this first.** | `{ project_id, provider: \"anthropic\", api_key: \"sk-ant-...\" }` |\n| `amba_ai_providers_list` | List registered providers (`configured` = key set). | `{ project_id }` |\n| `amba_ai_providers_delete` | Revoke a provider key (fails if prompts still reference it). | `{ project_id, provider: \"anthropic\" }` |\n| `amba_ai_prompts_create` | Create a prompt template. `client_invokable: true` lets the device SDK invoke it directly. | `{ project_id, name: \"summarize\", provider: \"anthropic\", model: \"claude-opus-4-5\", system_prompt: \"Summarize the user's text in 2 sentences.\", client_invokable: true }` |\n| `amba_ai_prompts_list` | List prompts. | `{ project_id }` |\n| `amba_ai_prompts_get` | Read one prompt. | `{ project_id, name }` |\n| `amba_ai_prompts_update` | Edit a prompt (replaces all fields; bumps version). | `{ project_id, name, provider, model, system_prompt: \"...\" }` |\n| `amba_ai_prompts_invoke` | Invoke a prompt server-side (admin testing). `messages` is a provider-shaped array. | `{ project_id, name, messages: [{ role: \"user\", content: \"...\" }] }` |\n| `amba_ai_prompts_delete` | Delete. | `{ project_id, name }` |\n\n### Analytics + events + sessions\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_analytics_get` | Top-level metrics dashboard (MAU, DAU, retention). | `{ project_id, period: \"7d\" }` |\n| `amba_events_list` | Browse raw events. | `{ project_id, limit: 100, since: \"2026-05-19T00:00:00Z\" }` |\n| `amba_events_count` | Count events matching a filter. | `{ project_id, event: \"workout_completed\", since: \"...\" }` |\n| `amba_sessions_list` | List user sessions. | `{ project_id, limit: 50 }` |\n| `amba_sessions_analytics` | Session-level metrics. | `{ project_id, period: \"7d\" }` |\n| `amba_users_list_events` | Per-user event history. | `{ project_id, user_id }` |\n| `amba_users_export` | Export the full user list. | `{ project_id, format: \"csv\" }` |\n\n### Secrets + configs + integrations\n\nSecrets here are **function-scoped** \u2014 they become environment bindings on your deployed functions. They are NOT where AI provider keys go (use `amba_ai_providers_set` for those \u2014 see AI prompts above).\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_secrets_set` | Set a function-scoped secret (encrypted at rest; bound on the next deploy). | `{ project_id, name: \"STRIPE_WEBHOOK_SECRET\", value: \"whsec_...\" }` |\n| `amba_secrets_get` | Read a secret (returns `\"<redacted>\"` unless explicitly requested). | `{ project_id, name }` |\n| `amba_secrets_list` | List secret names. | `{ project_id }` |\n| `amba_secrets_delete` | Delete. | `{ project_id, name }` |\n| `amba_configs_create` | Create a runtime config value (read from SDK as `Amba.config.fetch()`). | `{ project_id, key: \"primary_color\", value: \"#ff0066\", segment_id: null }` |\n| `amba_configs_list` | List configs. | `{ project_id }` |\n| `amba_configs_update` | Edit. | `{ project_id, config_id, value: \"...\" }` |\n| `amba_configs_delete` | Delete. | `{ project_id, config_id }` |\n| `amba_integrations_list` | List third-party integrations. | `{ project_id }` |\n| `amba_integrations_configure` | Configure a provider. | `{ project_id, provider: \"revenuecat\", config: { webhook_secret: \"...\", default_offering: \"...\" } }` |\n| `amba_integrations_set` | Set/replace integration config wholesale. | `{ project_id, provider, config }` |\n| `amba_integrations_patch` | Patch one field. | `{ project_id, provider, patch: { webhook_secret: \"...\" } }` |\n| `amba_integrations_test` | Send a test event to a configured provider. | `{ project_id, provider }` |\n\n### Media (file storage + CDN)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_media_upload` | Upload a file (returns a tenant-scoped URL). | `{ project_id, name: \"logo.png\", content_type: \"image/png\", data: \"<base64>\" }` |\n| `amba_media_list` | List files. | `{ project_id, folder: \"/\", limit: 100 }` |\n| `amba_media_delete` | Delete a file. | `{ project_id, file_id }` |\n| `amba_media_create_folder` | Create a logical folder. | `{ project_id, path: \"/uploads/avatars\" }` |\n| `amba_media_list_folders` | List folders. | `{ project_id }` |\n| `amba_media_delete_folder` | Delete a folder (must be empty). | `{ project_id, path }` |\n\n### Sites (static asset hosting)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_sites_deploy` | Deploy a static site bundle (zip / tar). | `{ project_id, name: \"marketing\", bundle: \"<base64>\", index: \"index.html\" }` |\n| `amba_sites_list` | List sites. | `{ project_id }` |\n| `amba_sites_get` | Read a site. | `{ project_id, site_id }` |\n| `amba_sites_add_domain` | Attach a custom domain. | `{ project_id, site_id, domain: \"marketing.example.com\" }` |\n| `amba_sites_list_domains` | List domains on a site. | `{ project_id, site_id }` |\n| `amba_sites_remove_domain` | Detach a domain. | `{ project_id, site_id, domain }` |\n| `amba_sites_delete` | Delete a site. | `{ project_id, site_id }` |\n\n## SDK init per stack\n\n`Amba.configure(...)` runs first. The infrastructure surfaces \u2014 collections, AI, config, flags, events \u2014 are SDK-side reads; the snippets below show what the client calls look like.\n\n### Expo / React Native\n\n```tsx\nimport { Amba } from '@layers/amba-expo';\n\n// Collections \u2014 typed table, user-scoped reads + writes\ntype Todo = { id: string; title: string; done: boolean; created_at: string };\n\nconst { data: todos } = await Amba.collections.find<Todo>('todos', {\n filter: Amba.collections.where.eq('done', false),\n order: [{ column: 'created_at', direction: 'desc' }],\n limit: 50,\n});\n\nconst newTodo = await Amba.collections.insert('todos', { title: 'Ship the app', done: false });\nawait Amba.collections.update('todos', newTodo.id, { done: true });\nawait Amba.collections.delete('todos', newTodo.id);\n\n// AI \u2014 call a managed prompt (prompt_slug names the registered prompt)\nconst response = await Amba.ai.anthropic.messages.create({\n prompt_slug: 'summarize',\n variables: { text: 'A long article about backend services \u2026' },\n});\n\n// Track an analytics event\nawait Amba.events.track('button_clicked', { button: 'cta' });\n\n// Read runtime config\nconst config = await Amba.config.fetch();\n\n// Read a feature flag\nconst showBeta = await Amba.flags.get('beta_feature');\n\n// Diagnostics \u2014 wire-verify\nconst ping = await Amba.diagnostics.ping();\nif (!ping.ok) console.error('Amba misconfigured:', ping);\n```\n\n### Web\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nconst { data: todos } = await Amba.collections.find('todos', {\n filter: Amba.collections.where.eq('done', false),\n limit: 50,\n});\nawait Amba.collections.insert('todos', { title: 'Ship', done: false });\nawait Amba.events.track('page_view', { path: location.pathname });\n```\n\nWith `@layers/amba-react`:\n\n```tsx\nimport { useCollection, useFlag } from '@layers/amba-react';\n\nfunction TodoList() {\n const { data: todos, loading, refetch } = useCollection<{ id: string; title: string }>('todos');\n const showArchive = useFlag('archive_todos');\n if (loading) return <Spinner />;\n return (\n <ul>\n {todos?.map(t => <li key={t.id}>{t.title}</li>)}\n {showArchive && <ArchiveButton onArchive={refetch} />}\n </ul>\n );\n}\n```\n\n### iOS (Swift)\n\n```swift\nimport Amba\n\nstruct Todo: Codable {\n let id: String\n let title: String\n let done: Bool\n}\n\nlet response = try await Amba.collections.find(\"todos\", as: Todo.self)\n_ = try await Amba.collections.insert(\"todos\", row: [\"title\": \"Ship\", \"done\": false])\n\nlet config = try await Amba.config.fetch()\nlet showBeta = try await Amba.flags.get(name: \"beta_feature\")\ntry await Amba.events.track(\"app_opened\", properties: [\"source\": \"deep_link\"])\n\nlet reply = try await Amba.ai.anthropic.messages.create(\n request: AiMessageRequest(promptSlug: \"summarize\", variables: [\"text\": \"A long article...\"])\n)\n```\n\n### Android (Kotlin)\n\n```kotlin\ndata class Todo(val id: String, val title: String, val done: Boolean)\n\nval todos = Amba.collections.find<Todo>(\"todos\")\nAmba.collections.insert(\"todos\", mapOf(\"title\" to \"Ship\", \"done\" to false))\n\nval config = Amba.config.fetch()\nval showBeta = Amba.flags.get(\"beta_feature\")\nAmba.events.track(\"app_opened\", mapOf(\"source\" to \"deep_link\"))\n```\n\n### Flutter\n\n```dart\nimport 'package:amba/amba.dart';\n\nfinal response = await Amba.collections.find('todos', limit: 50);\nawait Amba.collections.insert('todos', {'title': 'Ship', 'done': false});\nfinal config = await Amba.config.fetch();\nfinal showBeta = await Amba.flags.get('beta_feature');\nawait Amba.events.track('app_opened', {'source': 'deep_link'});\n```\n\n## Common follow-ups\n\nBatch.\n\n1. **Custom data tables (collections):** any domain-specific tables to create?\n - Yes \u2014 I'll list them. (For each: name + columns + types.)\n - No, just use the canned Amba surfaces (auth, push, gamification, etc.)\n - Auto-create from the existing code's models \u2014 read `lib/models/`, `src/types/`, `Models/`, infer column lists, confirm with me.\n\n2. **Custom backend logic (functions):** any server-side code to deploy?\n - Yes \u2014 describe what it should do. (Then offer to scaffold a function template and deploy.)\n - No\n\n3. **AI features:** want managed LLM prompts?\n - Yes \u2014 what's the use case? (summarize, translate, classify, generate, custom)\n - No\n\n4. **Analytics:** which tracker do you want?\n - Only Amba's built-in events (recommended \u2014 already wired)\n - Amba + Mixpanel / PostHog / Segment forwarding (configure via `amba_integrations_configure`)\n - None (rarely useful \u2014 events drive XP / achievements / streaks; disabling cripples gamification)\n\n5. **Third-party integrations to set up:**\n - [ ] RevenueCat (IAP / subscriptions on iOS + Android)\n - [ ] Superwall (paywall A/B)\n - [ ] Resend (transactional email)\n - [ ] Stripe (web payments / subscriptions)\n - [ ] Mixpanel / PostHog / Segment (analytics forwarding)\n - [ ] OpenAI / Anthropic / Mistral / Gemini LLM keys (required for `Amba.ai.*` \u2014 set via `amba_ai_providers_set`, **not** `amba_integrations_configure`)\n\n6. **Feature flags:** seed any starter flags?\n - Yes \u2014 wire `beta_feature` (off by default) so I can ship the wiring before the feature exists\n - No\n\n7. **Static site:** want a marketing page hosted under your tenant subdomain?\n - Yes \u2014 scaffold and deploy a 1-page index\n - No\n\n## Re-run behavior\n\n1. Before creating:\n - `amba_collections_list` \u2014 match on `name`. Collisions: never silently recreate (data loss). Offer `amba_collections_alter` to add new columns instead.\n - `amba_functions_list` \u2014 match on `name`. Collisions: ask to redeploy (with the new source) or skip.\n - `amba_ai_prompts_list` \u2014 match on `name`. Same. (And `amba_ai_providers_list` \u2014 match on `provider`; re-running `amba_ai_providers_set` rotates the key in place.)\n - `amba_integrations_list` \u2014 match on `provider`. Same.\n - `amba_configs_list` \u2014 match on `key`. Same.\n\n2. **Never call `amba_collections_delete` on re-run unless the user explicitly asks** \u2014 this drops the underlying table and every row in it across every user of the tenant.\n\n3. For functions: re-deploying replaces source in place (versioned server-side). It's safe to call `amba_functions_deploy` with the same name + new source.\n\n4. For integrations: if a provider is already configured, prefer `amba_integrations_patch` (partial update) over `amba_integrations_set` (full replace).\n\n5. Secrets: don't list secret values in chat output, even on read. Just confirm \"OPENAI_API_KEY is set\" / \"not set\".\n";
|
|
17
|
+
export declare const AMBA_SETUP_INFRASTRUCTURE_MD = "# Infrastructure\n\nThe plumbing that sits behind every other surface: relational Postgres tables (Collections \u2014 schema-first, per-tenant), serverless functions (run server-side code without standing up a backend), analytics (events + sessions), AI prompts (managed LLM templates, callable from the SDK with per-tenant keys), secrets, runtime configs, feature flags, third-party integrations (RevenueCat / Superwall / Stripe / push credentials), media (file storage + CDN), and sites (static asset hosting at `*.app.amba.host`).\n\nIf gamification, economy, and social are the playable surface, **infrastructure is what you build a custom product on top of**. Anything that doesn't fit the canned surfaces lands here.\n\n## MCP tools\n\n### Collections (relational Postgres tables)\n\nA collection is a relational Postgres table inside the project's isolated tenant database \u2014 typed columns, foreign keys, transactions, unique indexes, and vector search. 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 \u2014 admin tools bypass this.\n\nAdmin 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`) \u2014 NOT `project_id` and NOT a `pat`. Every row tool names the collection with `name`, never `collection`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `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 }, { name: \"due_at\", type: \"timestamptz\", nullable: true }], shared: false }` |\n| `amba_collections_list` | List collections in this project. | `{ project_id }` |\n| `amba_collections_get` | Read one collection's schema. | `{ project_id, name: \"todos\" }` |\n| `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: \"integer\", nullable: true } }` |\n| `amba_collections_delete` | Drop the table (destructive). `confirm` must equal the collection name. | `{ project_id, name: \"todos\", confirm: \"todos\" }` |\n| `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 } }` |\n| `amba_admin_insert_rows` | Bulk-insert up to 500 rows in one atomic statement \u2014 the canonical seeding/migration path. `on_conflict`: `\"error\"` (default) or `\"skip\"`. | `{ project_id, name: \"questions\", rows: [{ q: \"...\" }, { q: \"...\" }], on_conflict: \"skip\" }` |\n| `amba_admin_list_rows` | Read rows as the developer. | `{ project_id, name: \"todos\", limit: 100 }` |\n| `amba_client_insert_row` | Insert as an end-user. Requires `api_key` (+ `session_token`). | `{ api_key, session_token, name: \"todos\", row: {...} }` |\n| `amba_client_list_rows` | Read as an end-user (auto user-scoped). | `{ api_key, session_token, name: \"todos\" }` |\n| `amba_client_get_row` | Get one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `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: {...} }` |\n| `amba_client_delete_row` | Soft-delete one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `amba_client_count_rows` | Count rows matching an optional `where`. | `{ api_key, session_token, name: \"todos\", where: {...} }` |\n| `amba_client_find_rows` | Filter / sort / paginate rows (SDK-shaped `filter`). | `{ api_key, session_token, name: \"todos\", filter: {...}, order: [\"created_at desc\"], limit: 50 }` |\n| `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 }` |\n\nColumn types: `text`, `integer`, `bigint`, `numeric`, `boolean`, `timestamptz`, `date`, `jsonb`, `uuid`, `vector` (pass a separate `dimension` field, e.g. `{ name: \"embedding\", type: \"vector\", dimension: 1536 }` for OpenAI embeddings), plus array forms `text[]`, `integer[]`, `bigint[]`, `numeric[]`, `boolean[]`, `uuid[]`. Columns are NOT NULL unless `nullable: true`; column defaults are not supported (set values at insert time). Use `integer` (not `int`), `numeric` (not `float`/`real`/`double`), and `jsonb` (not `json`) \u2014 the validator rejects the aliases.\n\n### Functions (serverless code)\n\nRun user code in a sandbox triggered by HTTP, cron, or webhook. The function gets the tenant connection automatically via injected env.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_functions_deploy` | Deploy a function from source. | `{ project_id, name: \"send_welcome_email\", runtime: \"node22\", source: \"export default async (req) => { ... }\", trigger: { type: \"http\" } }` |\n| `amba_functions_list` | List functions. | `{ project_id }` |\n| `amba_functions_get` | Read function metadata. | `{ project_id, function_id }` |\n| `amba_functions_get_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |\n| `amba_functions_delete` | Delete a function. | `{ project_id, function_id }` |\n| `amba_functions_schedule` | Attach a cron schedule. | `{ project_id, function_id, cron: \"0 9 * * *\", timezone: \"America/Los_Angeles\" }` |\n| `amba_functions_pause_schedule` | Pause a scheduled trigger without deleting it. | `{ project_id, function_id }` |\n| `amba_functions_resume_schedule` | Resume. | `{ project_id, function_id }` |\n| `amba_functions_trigger_schedule` | Fire a scheduled function ad-hoc (testing). | `{ project_id, function_id }` |\n| `amba_function_domains_attach` | Attach one exact hostname to one function; returns DNS validation instructions. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n| `amba_function_domains_list` | List provider-neutral hostname, ownership, and certificate status. | `{ project_id, name: \"feed\" }` |\n| `amba_function_domains_refresh` | Re-poll DNS ownership and certificate state. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n| `amba_function_domains_remove` | Detach an exact function hostname. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n\nFunction-domain routing preserves the complete incoming path and query string.\nIt is exact-host only (no wildcard/path rewrite and no automatic `www` for a\nsubdomain). Podcast/feed clients cannot attach an Amba API key, so their\nfunction must be deployed with `public: true` and validate any private token\ninside the handler. Effective-tier caps are free 1, pro 5, scale 20, and\nenterprise/comped 50; attach is limited to five attempts per project per hour.\nUnverified claims become eligible for reclaim after 24 hours, and routing\nresources are allocated only after ownership is active.\n\n### AI prompts\n\nManaged LLM templates: a stored prompt with provider + model + system message, invoked by name from the SDK. The actual LLM call is rewritten server-side per-tenant \u2014 the customer's provider API key (Anthropic / OpenAI / Mistral / Gemini) stays server-side, never on the device.\n\n**Two steps, in order:** first register the provider key with `amba_ai_providers_set`, then create prompts against it. A prompt registered before its provider has a key still saves, but invocations fail with `provider_not_configured` (424) until the key is set.\n\n> The provider key is **not** a function secret. `amba_secrets_set` writes function-scoped Worker secrets, which the AI gateway never reads. Provider keys live in a separate gateway-owned store and are set **only** via `amba_ai_providers_set`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_ai_providers_set` | Register / rotate the upstream provider API key. **Do this first.** | `{ project_id, provider: \"anthropic\", api_key: \"sk-ant-...\" }` |\n| `amba_ai_providers_list` | List registered providers (`configured` = key set). | `{ project_id }` |\n| `amba_ai_providers_delete` | Revoke a provider key (fails if prompts still reference it). | `{ project_id, provider: \"anthropic\" }` |\n| `amba_ai_prompts_create` | Create a prompt template. `client_invokable: true` lets the device SDK invoke it directly. | `{ project_id, name: \"summarize\", provider: \"anthropic\", model: \"claude-opus-4-5\", system_prompt: \"Summarize the user's text in 2 sentences.\", client_invokable: true }` |\n| `amba_ai_prompts_list` | List prompts. | `{ project_id }` |\n| `amba_ai_prompts_get` | Read one prompt. | `{ project_id, name }` |\n| `amba_ai_prompts_update` | Edit a prompt (replaces all fields; bumps version). | `{ project_id, name, provider, model, system_prompt: \"...\" }` |\n| `amba_ai_prompts_invoke` | Invoke by name server-side (admin testing; works with `client_invokable: false`). Uses the named gateway path, so the prompt budget, rate limit, token cap, and spend attribution are enforced. | `{ project_id, name, messages: [{ role: \"user\", content: \"...\" }] }` |\n| `amba_ai_prompts_delete` | Delete. | `{ project_id, name }` |\n\n### Analytics + events + sessions\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_analytics_get` | Top-level metrics dashboard (MAU, DAU, retention). | `{ project_id, period: \"7d\" }` |\n| `amba_events_list` | Browse raw events. | `{ project_id, limit: 100, since: \"2026-05-19T00:00:00Z\" }` |\n| `amba_events_count` | Count events matching a filter. | `{ project_id, event: \"workout_completed\", since: \"...\" }` |\n| `amba_sessions_list` | List user sessions. | `{ project_id, limit: 50 }` |\n| `amba_sessions_analytics` | Session-level metrics. | `{ project_id, period: \"7d\" }` |\n| `amba_users_list_events` | Per-user event history. | `{ project_id, user_id }` |\n| `amba_users_export` | Export the full user list. | `{ project_id, format: \"csv\" }` |\n\n### Secrets + configs + integrations\n\nSecrets here become environment bindings on deployed functions. Omit `function`\nfor a project-wide secret or pass it to scope the value to one function. Setting\nor rotating a secret queues an asynchronous update for already-deployed\nfunctions; later deployments reconcile the binding too. They are NOT where AI\nprovider keys go (use `amba_ai_providers_set` for those \u2014 see AI prompts above).\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_secrets_set` | Set or rotate a function secret; omit `function` for project-wide scope or pass it for one function. Already-deployed functions receive it asynchronously. | `{ project_id, name: \"STRIPE_WEBHOOK_SECRET\", value: \"whsec_...\" }` |\n| `amba_secrets_get` | Read a secret (returns `\"<redacted>\"` unless explicitly requested). | `{ project_id, name }` |\n| `amba_secrets_list` | List secret names. | `{ project_id }` |\n| `amba_secrets_delete` | Delete. | `{ project_id, name }` |\n| `amba_configs_create` | Create a runtime config value (read from SDK as `Amba.config.fetch()`). | `{ project_id, key: \"primary_color\", value: \"#ff0066\", segment_id: null }` |\n| `amba_configs_list` | List configs. | `{ project_id }` |\n| `amba_configs_update` | Edit. | `{ project_id, config_id, value: \"...\" }` |\n| `amba_configs_delete` | Delete. | `{ project_id, config_id }` |\n| `amba_integrations_list` | List third-party integrations. | `{ project_id }` |\n| `amba_integrations_configure` | Configure a provider. | `{ project_id, provider: \"revenuecat\", config: { webhook_secret: \"...\", default_offering: \"...\" } }` |\n| `amba_integrations_set` | Set/replace integration config wholesale. | `{ project_id, provider, config }` |\n| `amba_integrations_patch` | Patch one field. | `{ project_id, provider, patch: { webhook_secret: \"...\" } }` |\n| `amba_integrations_test` | Send a test event to a configured provider. | `{ project_id, provider }` |\n\n### Media (file storage + CDN)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_media_upload` | Upload a file (returns a tenant-scoped URL). | `{ project_id, name: \"logo.png\", content_type: \"image/png\", data: \"<base64>\" }` |\n| `amba_media_list` | List files. | `{ project_id, folder: \"/\", limit: 100 }` |\n| `amba_media_delete` | Delete a file. | `{ project_id, file_id }` |\n| `amba_media_create_folder` | Create a logical folder. | `{ project_id, path: \"/uploads/avatars\" }` |\n| `amba_media_list_folders` | List folders. | `{ project_id }` |\n| `amba_media_delete_folder` | Delete a folder (must be empty). | `{ project_id, path }` |\n\n### Sites (static asset hosting)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_sites_deploy` | Deploy a static site bundle (zip / tar). | `{ project_id, name: \"marketing\", bundle: \"<base64>\", index: \"index.html\" }` |\n| `amba_sites_list` | List sites. | `{ project_id }` |\n| `amba_sites_get` | Read a site. | `{ project_id, site_id }` |\n| `amba_sites_add_domain` | Attach a custom domain. | `{ project_id, site_id, domain: \"marketing.example.com\" }` |\n| `amba_sites_list_domains` | List domains on a site. | `{ project_id, site_id }` |\n| `amba_sites_remove_domain` | Detach a domain. | `{ project_id, site_id, domain }` |\n| `amba_sites_delete` | Delete a site. | `{ project_id, site_id }` |\n\n### Purchased domains + email forwarding\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_domains_search` | Search available domains (free). | `{ project_id, query: \"myapp\" }` |\n| `amba_domains_check` | Check authoritative price + availability. | `{ project_id, domains: [\"myapp.com\"] }` |\n| `amba_domains_purchase` | Quote, then confirm, a domain purchase. | `{ project_id, domain: \"myapp.com\", site: \"marketing\" }` |\n| `amba_domains_list` | List purchased domains. | `{ project_id }` |\n| `amba_domains_email_enable` | Enable inbound routing when no MX conflict exists. | `{ project_id, domain: \"myapp.com\" }` |\n| `amba_domains_email_destinations_add` | Add a destination mailbox; returns action-required until verified. | `{ project_id, domain: \"myapp.com\", email: \"owner@example.net\" }` |\n| `amba_domains_email_destinations_get` | Poll destination verification. | `{ project_id, domain: \"myapp.com\", destination_id }` |\n| `amba_domains_email_forwards_set` | Create/update a literal forward. | `{ project_id, domain: \"myapp.com\", source: \"support\", destination: \"owner@example.net\" }` |\n| `amba_domains_email_forwards_list` | List literal forwards. | `{ project_id, domain: \"myapp.com\" }` |\n| `amba_domains_email_forwards_delete` | Delete a literal forward. | `{ project_id, domain: \"myapp.com\", forward_id }` |\n| `amba_domains_email_catch_all_set` | Enable/update/disable catch-all. | `{ project_id, domain: \"myapp.com\", enabled: true, destination: \"owner@example.net\" }` |\n| `amba_domains_email_catch_all_get` | Read catch-all state. | `{ project_id, domain: \"myapp.com\" }` |\n\n## SDK init per stack\n\n`Amba.configure(...)` runs first. The infrastructure surfaces \u2014 collections, AI, config, flags, events \u2014 are SDK-side reads; the snippets below show what the client calls look like.\n\n### Expo / React Native\n\n```tsx\nimport { Amba } from '@layers/amba-expo';\n\n// Collections \u2014 typed table, user-scoped reads + writes\ntype Todo = { id: string; title: string; done: boolean; created_at: string };\n\nconst { data: todos } = await Amba.collections.find<Todo>('todos', {\n filter: Amba.collections.where.eq('done', false),\n order: [{ column: 'created_at', direction: 'desc' }],\n limit: 50,\n});\n\nconst newTodo = await Amba.collections.insert('todos', { title: 'Ship the app', done: false });\nawait Amba.collections.update('todos', newTodo.id, { done: true });\nawait Amba.collections.delete('todos', newTodo.id);\n\n// AI \u2014 call a managed prompt (prompt_slug names the registered prompt)\nconst response = await Amba.ai.anthropic.messages.create({\n prompt_slug: 'summarize',\n variables: { text: 'A long article about backend services \u2026' },\n});\n\n// Track an analytics event\nawait Amba.events.track('button_clicked', { button: 'cta' });\n\n// Read runtime config\nconst config = await Amba.config.fetch();\n\n// Read a feature flag\nconst showBeta = await Amba.flags.get('beta_feature');\n\n// Diagnostics \u2014 wire-verify\nconst ping = await Amba.diagnostics.ping();\nif (!ping.ok) console.error('Amba misconfigured:', ping);\n```\n\n### Web\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nconst { data: todos } = await Amba.collections.find('todos', {\n filter: Amba.collections.where.eq('done', false),\n limit: 50,\n});\nawait Amba.collections.insert('todos', { title: 'Ship', done: false });\nawait Amba.events.track('page_view', { path: location.pathname });\n```\n\nWith `@layers/amba-react`:\n\n```tsx\nimport { useCollection, useFlag } from '@layers/amba-react';\n\nfunction TodoList() {\n const { data: todos, loading, refetch } = useCollection<{ id: string; title: string }>('todos');\n const showArchive = useFlag('archive_todos');\n if (loading) return <Spinner />;\n return (\n <ul>\n {todos?.map(t => <li key={t.id}>{t.title}</li>)}\n {showArchive && <ArchiveButton onArchive={refetch} />}\n </ul>\n );\n}\n```\n\n### iOS (Swift)\n\n```swift\nimport Amba\n\nstruct Todo: Codable {\n let id: String\n let title: String\n let done: Bool\n}\n\nlet response = try await Amba.collections.find(\"todos\", as: Todo.self)\n_ = try await Amba.collections.insert(\"todos\", row: [\"title\": \"Ship\", \"done\": false])\n\nlet config = try await Amba.config.fetch()\nlet showBeta = try await Amba.flags.get(name: \"beta_feature\")\ntry await Amba.events.track(\"app_opened\", properties: [\"source\": \"deep_link\"])\n\nlet reply = try await Amba.ai.anthropic.messages.create(\n request: AiMessageRequest(promptSlug: \"summarize\", variables: [\"text\": \"A long article...\"])\n)\n```\n\n### Android (Kotlin)\n\n```kotlin\ndata class Todo(val id: String, val title: String, val done: Boolean)\n\nval todos = Amba.collections.find<Todo>(\"todos\")\nAmba.collections.insert(\"todos\", mapOf(\"title\" to \"Ship\", \"done\" to false))\n\nval config = Amba.config.fetch()\nval showBeta = Amba.flags.get(\"beta_feature\")\nAmba.events.track(\"app_opened\", mapOf(\"source\" to \"deep_link\"))\n```\n\n### Flutter\n\n```dart\nimport 'package:amba/amba.dart';\n\nfinal response = await Amba.collections.find('todos', limit: 50);\nawait Amba.collections.insert('todos', {'title': 'Ship', 'done': false});\nfinal config = await Amba.config.fetch();\nfinal showBeta = await Amba.flags.get('beta_feature');\nawait Amba.events.track('app_opened', {'source': 'deep_link'});\n```\n\n## Common follow-ups\n\nBatch.\n\n1. **Custom data tables (collections):** any domain-specific tables to create?\n - Yes \u2014 I'll list them. (For each: name + columns + types.)\n - No, just use the canned Amba surfaces (auth, push, gamification, etc.)\n - Auto-create from the existing code's models \u2014 read `lib/models/`, `src/types/`, `Models/`, infer column lists, confirm with me.\n\n2. **Custom backend logic (functions):** any server-side code to deploy?\n - Yes \u2014 describe what it should do. (Then offer to scaffold a function template and deploy.)\n - No\n\n3. **AI features:** want managed LLM prompts?\n - Yes \u2014 what's the use case? (summarize, translate, classify, generate, custom)\n - No\n\n4. **Analytics:** which tracker do you want?\n - Only Amba's built-in events (recommended \u2014 already wired)\n - Amba + your own analytics pipeline (subscribe a webhook to project events via `amba_webhooks_create` and forward server-side)\n - None (rarely useful \u2014 events drive XP / achievements / streaks; disabling cripples gamification)\n\n5. **Third-party integrations to set up:**\n - [ ] RevenueCat (IAP / subscriptions on iOS + Android)\n - [ ] Superwall (paywall A/B)\n - [ ] Stripe Billing (web subscriptions through the app's own Stripe account \u2014 provider `stripe_billing`)\n - [ ] OpenAI / Anthropic / Mistral / Gemini LLM keys (required for `Amba.ai.*` \u2014 set via `amba_ai_providers_set`, **not** `amba_integrations_configure`)\n\n6. **Feature flags:** seed any starter flags?\n - Yes \u2014 wire `beta_feature` (off by default) so I can ship the wiring before the feature exists\n - No\n\n7. **Static site:** want a marketing page hosted under your tenant subdomain?\n - Yes \u2014 scaffold and deploy a 1-page index\n - No\n\n## Re-run behavior\n\n1. Before creating:\n - `amba_collections_list` \u2014 match on `name`. Collisions: never silently recreate (data loss). Offer `amba_collections_alter` to add new columns instead.\n - `amba_functions_list` \u2014 match on `name`. Collisions: ask to redeploy (with the new source) or skip.\n - `amba_ai_prompts_list` \u2014 match on `name`. Same. (And `amba_ai_providers_list` \u2014 match on `provider`; re-running `amba_ai_providers_set` rotates the key in place.)\n - `amba_integrations_list` \u2014 match on `provider`. Same.\n - `amba_configs_list` \u2014 match on `key`. Same.\n\n2. **Never call `amba_collections_delete` on re-run unless the user explicitly asks** \u2014 this drops the underlying table and every row in it across every user of the tenant.\n\n3. For functions: re-deploying replaces source in place (versioned server-side). It's safe to call `amba_functions_deploy` with the same name + new source.\n\n4. For integrations: if a provider is already configured, prefer `amba_integrations_patch` (partial update) over `amba_integrations_set` (full replace).\n\n5. Secrets: don't list secret values in chat output, even on read. Just confirm \"OPENAI_API_KEY is set\" / \"not set\".\n";
|
|
17
18
|
export declare const AMBA_SETUP_INFRASTRUCTURE_URI = "amba://setup/infrastructure";
|
|
18
19
|
export declare const AMBA_SETUP_INFRASTRUCTURE_MIME = "text/markdown";
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* The body is plain CommonMark — no MDX, no JSX — so it renders
|
|
41
41
|
* identically wherever it lands.
|
|
42
42
|
*/
|
|
43
|
-
export declare const AMBA_SETUP_GUIDE_MD = "# Amba\n\n## What Amba is\n\nAmba is the backend your app calls at runtime via the SDK. The MCP tools (`amba_*`) are for provisioning at build time \u2014 call them as the coding agent.\n\nOne Amba project = one isolated tenant database + one set of API keys + a hosted MCP at `mcp.amba.dev` for agent-driven provisioning. The runtime traffic goes through an SDK (`@layers/amba-*` on npm, `Amba` on SPM / Maven / pub.dev). The provisioning traffic \u2014 what you're about to do \u2014 goes through the tools listed below.\n\nYour job is to take the user from \"I have an app\" to \"Amba is wired in and the features are real\" in one pass, without 12 follow-up questions. The journey below is the standard playbook. Don't deviate without a reason.\n\n## Step 0 \u2014 Bootstrap credentials\n\nYou're in an MCP client (Claude.ai web, Claude Desktop, Cursor, Windsurf, etc.) without a local CLI. The MCP server has one tool that works **without** a Bearer token: `amba_developer_signup`. Use it to mint a personal access token (PAT), project, and client/server keys in a single round trip \u2014 no browser, no email verification.\n\nCall:\n\n```\namba_developer_signup({\n email: \"sandbox-<unix-seconds>-<6char-nonce>@layers.com\",\n password: \"<32 random base64url chars>\",\n name: \"amba-sandbox-agent\"\n})\n```\n\nThe response shape:\n\n```json\n{\n \"data\": {\n \"pat\": \"amb_dpat_\u2026\",\n \"developer\": { \"id\": \"\u2026\", \"email\": \"\u2026\", \"tier\": \"agent_sandbox\" },\n \"project\": {\n \"project_id\": \"\u2026\",\n \"client_key\": \"amb_ck_\u2026\",\n \"server_key\": \"amb_sk_\u2026\",\n \"provisioning_status\": \"provisioning\",\n \"verify_url\": \"https://app.amba.dev/verify?token=\u2026\",\n \"verify_token\": \"\u2026\"\n }\n }\n}\n```\n\nAfter signup, the PAT is in your hand \u2014 keep building in THIS session immediately. Pass `pat: \"<the PAT from the response>\"` as an inline argument on every subsequent `amba_*` tool call; the inline arg overrides the inbound Bearer for that one call so no client config change is needed mid-session. For future sessions, write the matching snippet from `mcp_config` to the customer's MCP-client config file so the static Bearer takes over automatically on the next launch.\n\nThe project status is `\"provisioning\"` immediately after signup; the per-tenant DB activates within ~5 seconds. Don't block \u2014 your next call may briefly retry, that's fine. If you want to be polite, call `amba_projects_get_provisioning_status({ project_id })` once and proceed when it returns `\"active\"` (or after 15s, whichever first).\n\nTell the user where their credentials live:\n\n- `pat` \u2014 the Bearer they should configure in this MCP client's settings (and treat like a password).\n- `project_id`, `client_key` \u2014 the values they paste into their app's `.env.local` / `.env`.\n- `server_key` \u2014 never ship to user devices; only into a server `.env` or a secret manager. The `amb_dev_sk_` / `amb_live_sk_` prefix is the marker.\n\n**Already have a PAT?** Skip the signup. Call `amba_developer_me({})` to verify the Bearer; if it succeeds, either reuse the most recent project (`amba_projects_list`) or call `amba_projects_create({ name: \"<app-name>\", platform: \"all\" })` and then `amba_api_keys_create` twice to mint client + server keys for `environment: \"development\"`.\n\n**Can't reach `amba_developer_signup` \u2014 it's not in your tool list, or your harness blocks the call?** Do NOT try to work around it by adding the MCP server yourself, running an installer, or editing your own settings/permissions \u2014 refusing to self-modify or widen your own permissions is the correct call, and you should not circumvent it. Instead, hand the developer **one** command to run in **their own** terminal (which is not subject to your sandbox):\n\n```bash\nnpx @layers/amba init\n```\n\nIt creates the account with no browser and writes `AMBA_PROJECT_ID` + `AMBA_CLIENT_KEY` to `.env.local` (and wires this MCP server up for your next session). Ask them to run it, tell you when it's done, then resume from this Step 0 by reading `.env.local`. Asking the developer to paste an `amb_ck_\u2026` client key is the last-resort lite path \u2014 it's enough to wire `Amba.configure()` + runtime calls and verify events land, but a client key alone can't do admin provisioning (creating achievements, collections, segments, etc.), which needs the PAT or server key.\n\n## Step 1 \u2014 Classify the app\n\nLook at what the user told you and at any files they shared. You're trying to pick one of ten presets in 30 seconds, not write a treatise. Inputs:\n\n- The user's prompt \u2014 \"I'm building a fitness tracker\" / \"a marketplace for\u2026\" / \"a Duolingo for X\".\n- README content if shared.\n- `package.json` / `pubspec.yaml` / `build.gradle.kts` / `Package.swift` \u2014 framework + dependencies.\n- Screen / view names \u2014 `WorkoutScreen`, `MatchView`, `LessonPage`, `CartView`, `ProductDetail`, `ChatThread`.\n\nPick the closest match:\n\n| Preset | When | Default Amba surfaces |\n| --- | --- | --- |\n| **fitness** | health / fitness tracker (workouts, steps, meditation) | identity (Apple+Google), push, XP, achievements, streaks, leaderboards, content (daily tips) |\n| **social** | social network / community (friends, feeds, groups) | identity, push, friends, groups, feeds, messaging, moderation, content |\n| **marketplace** | commerce / marketplace (catalog, stores, payments) | identity, push, catalog, stores, currencies (loyalty), reviews, segments |\n| **productivity** | productivity / SaaS tool (collaboration, milestones) | identity (Apple+Google+OTP), push, collections, achievements, content (changelog), segments |\n| **education** | education / learning app (courses, progress, rewards) | identity, push, XP, achievements, streaks, leaderboards, content (lessons), onboarding |\n| **game** | game / casual gaming | identity (anon-first), push, XP, achievements, currencies, inventory, leaderboards, challenges, stores |\n| **dating** | dating / matching app | identity (phone-OTP), push, friends (matches), messaging, moderation (heavy), reviews |\n| **content_creator** | content platform (feeds, subscriptions, tips) | identity, push, feeds, content, currencies (tips), referrals, stores (subscriptions) |\n| **ai_chatbot** | AI / chatbot / assistant app | identity, push, AI prompts, currencies (credits), content (system prompts), onboarding |\n| **custom** | none of the above | pick features individually |\n\nDetection heuristics, in priority order:\n\n1. The user's own description \u2014 most direct signal.\n2. Filename match in `screens/` or `views/` (high signal).\n3. Dependency in `package.json` \u2014 `react-native-health` \u2192 fitness, `@stream-io/*` \u2192 social or dating, `@stripe/*` \u2192 marketplace, `revenuecat` \u2192 marketplace or content_creator.\n4. README copy \u2014 \"fitness\", \"habit\", \"match\", \"chat\", \"store\", \"subscription\".\n\nIf two presets tie, pick the one the user's filenames match more closely. If still tied or no signal, fall back to **custom** and let them pick.\n\n## Step 2 \u2014 Confirm with the user\n\nUse a single multi-choice. Quote the surfaces from the table above so they know what they're getting.\n\n**Question 1: classification + scope**\n\n> I'm reading this as a **\\{kind\\}** app. I'd wire up: **\\{surfaces\\}**. Sound right?\n>\n> 1. Yes, wire it up as proposed (Recommended)\n> 2. Same kind but I want to pick features individually\n> 3. Wrong kind \u2014 let me pick from the list\n> 4. Custom \u2014 I'll pick features manually\n\nIf the user picks 1, go to Step 3. If 2 or 4, follow up with a multi-select of surfaces. If 3, present the table again and pick a different preset.\n\n**Question 2 (preset-specific):** see the per-surface sub-resources (`amba://setup/<surface>`) for the full \"Common follow-ups\" list. Examples:\n\n- **fitness / game / education** \u2014 leaderboard scope? (all-time, weekly, daily, none)\n- **game / content_creator** \u2014 virtual currency name? (`gold`, `gems`, `coins`, `credits` \u2014 defaults to `coins`)\n- **content_creator** \u2014 monetization? (tips, subscriptions, both)\n- **dating** \u2014 phone OTP or email-only? (phone strongly recommended)\n- **ai_chatbot** \u2014 daily free credit cap?\n\nBatch the follow-ups into one or two multi-choice rounds. Don't drip-feed six separate questions.\n\n## Step 3 \u2014 Wire it up\n\nFor each surface in the confirmed set, read the relevant sub-resource and execute its procedure. Each sub-resource is the full per-surface playbook (MCP tools + SDK init per stack + common follow-ups + re-run behavior):\n\n- **identity** (auth, anonymous/Apple/Google/OTP/magic-link, link/unlink) \u2192 `amba://setup/identity`\n- **engagement** (push, segments, content libraries, onboarding flows, deeplinks, referrals, tracked links) \u2192 `amba://setup/engagement`\n- **gamification** (XP rules, achievements, streaks, leaderboards, challenges) \u2192 `amba://setup/gamification`\n- **economy** (currencies, catalog, stores, inventory) \u2192 `amba://setup/economy`\n- **social** (friends, groups, feeds, messaging, moderation, reviews) \u2192 `amba://setup/social`\n- **infrastructure** (collections / DB tables, functions, analytics, AI prompts, media, secrets, configs, integrations, sites) \u2192 `amba://setup/infrastructure`\n\nThe general flow for every surface:\n\n1. **Detect stack.** Look at `package.json`, `pubspec.yaml`, `build.gradle.kts`, `ios/*.xcodeproj`. The detection rules:\n - `pubspec.yaml` present \u2192 Flutter.\n - `package.json` with `expo` \u2192 Expo.\n - `package.json` with `react-native` (no `expo`) \u2192 bare React Native.\n - `package.json` with `react` (no `react-native`) \u2192 web (or Next.js \u2014 same SDK).\n - `Package.swift` or `*.xcodeproj` only \u2192 iOS Swift.\n - `build.gradle.kts` or `build.gradle` with `com.android.application` \u2192 Android Kotlin.\n - Multiple (e.g. `ios/` + `android/` inside an Expo repo) \u2192 Expo wins.\n\n2. **Create resources via MCP.** Call the `amba_<surface>_create` tools to mint the definitions. Always include `project_id` from the project you created in Step 0. Always show the user the tool call before making destructive changes (creating a resource isn't destructive \u2014 but creating 30 of them is noisy).\n\n3. **Write SDK init code.** Drop the per-stack snippet (from the sub-resource) into the user's entry file. Detection:\n - Expo / React Native: `app/_layout.tsx`, `App.tsx`, `index.js` (in that order)\n - web / Next.js: `app/layout.tsx`, `pages/_app.tsx`, `src/main.tsx`, `src/App.tsx`\n - iOS Swift: `Sources/<App>/<App>App.swift`, `App/AppDelegate.swift`\n - Android Kotlin: `app/src/main/java/.../<App>.kt` (the `Application` subclass \u2014 create one if missing)\n - Flutter: `lib/main.dart`\n\n Always make additive edits \u2014 `await Amba.configure(...)` next to existing init, not replacing it. Never refactor existing auth or storage code; if the user has Firebase Auth or Supabase, leave it. Amba's auth is opt-in per call.\n\n4. **Run the project's existing test command** to confirm nothing broke. Detection:\n - `package.json` `scripts.test` \u2192 `npm test` (or `pnpm test` if `pnpm-lock.yaml` present)\n - `pubspec.yaml` \u2192 `flutter test`\n - `build.gradle.kts` \u2192 `./gradlew test` (skip on first wire-up \u2014 slow)\n - iOS \u2014 skip (need a simulator).\n\n If tests fail because of your edits, undo the offending edit and surface a clear error. If they fail for unrelated reasons (pre-existing red), note it and proceed.\n\n5. **Verify with the SDK.** Tell the user to call `Amba.diagnostics.ping()` (`Amba.Diagnostics.Ping()` on Unity) in their entry file. It returns `{ ok, server_project_id, environment, key_fingerprint, latency_ms }`. `ok: true` with the expected `server_project_id` confirms the wiring.\n\n## Step 4 \u2014 Report\n\nTell the user a structured summary. Use this exact shape so they can skim it fast:\n\n```\nAmba is wired in. Here's what changed:\n\nDONE\n - identity: Apple + Google sign-in available; signInAnonymously() called at app start\n - gamification: 3 achievements, 1 streak, 1 leaderboard created\n resources: first_workout, week_warrior, century_club / daily_workout / weekly_xp\n - engagement: push registration wired; default segment \"active_users\" created\n\nSKIPPED (low signal \u2014 re-run with /amba <feature> if you want them)\n - economy: no in-app currency UI found in your screens\n - social: no friends/feed surfaces found\n\nNEEDS YOUR INPUT\n - Apple Sign In: add the \"Sign in with Apple\" capability in Xcode > Signing & Capabilities.\n - Google Sign In: paste your Google OAuth client ID into amba_projects_update({ google_oauth_client_id: \"...\" }).\n - APNs / FCM: configure credentials with amba_integrations_configure (paste the .p8 / service-account JSON inline) before push delivers.\n\nNEXT STEPS\n - Paste AMBA_CLIENT_KEY into your build env (already shown above)\n - Trigger a workout in your existing flow \u2014 watch the achievement unlock + XP land\n - Open https://app.amba.dev to see users pour in\n```\n\nBe specific. List resources by key, not \"some achievements\". If something needs the user's input (third-party credentials, OAuth client IDs, push certs), say it clearly with the exact next action.\n\n## Stance (read this once)\n\n- **Don't ask which surfaces to use.** Classify, then confirm in one multi-choice. The taxonomy is the whole point.\n- **Default to additive, non-breaking changes.** Don't refactor existing auth, storage, or networking code. Drop in `await Amba.configure(...)` next to whatever the user already has.\n- **Never create resources without the user's confirmation in Step 2.** A 3rd-party \"convenience\" achievement called `first_login` is debt.\n- **If something is genuinely ambiguous** (leaderboard scope, currency real-money vs virtual, dating phone vs email), ask via a follow-up multi-choice. Don't guess and don't paragraph-it.\n- **clientKey vs serverKey.** `AMBA_CLIENT_KEY` (`amb_dev_ck_\u2026` in dev, `amb_live_ck_\u2026` in prod) ships to user devices. `AMBA_SERVER_KEY` (`amb_dev_sk_\u2026` / `amb_live_sk_\u2026`) never does \u2014 only into server `.env` or a secret manager. Mixing them is the #1 security mistake; if you're writing into a file that ships with the app binary, it's the client key, period.\n- **Don't echo the PAT in chat output on every call.** Showing it once after signup is fine; do not repeat it.\n\n## Get credentials (cheat sheet)\n\n- No terminal, in an MCP client: call `amba_developer_signup` (no Bearer required) \u2014 this guide's Step 0.\n- With a terminal: `npx -y @layers/amba init` signs up, mints a project + client/server keys, writes `.env.local` + `AMBA.md`, installs the `/amba` skill, and wires `mcpServers.amba` into every detected MCP-client config in one command. Auto-detects non-TTY invocations (the coding-agent bash-tool case) and runs headlessly.\n- Bind the sandbox account to a real email later: `npx @layers/amba claim me@example.com`. The backend emails a one-click magic link; clicking it lifts the sandbox cap to the Free tier.\n- Hosted MCP endpoint: `https://mcp.amba.dev/mcp` (Streamable HTTP, Bearer auth).\n\n## SDKs\n\n| Stack | Registry | Package |\n|---|---|---|\n| Browser / Node / React / React Native / Expo | npm | `@layers/amba-{web,node,react,react-native,expo}` |\n| Swift | SPM | `https://github.com/layers/amba-sdk-ios` |\n| Kotlin | Maven Central | `com.layers.amba:amba-sdk-android` |\n| Flutter | pub.dev | `amba` |\n| Unity | UPM (git) | `https://github.com/layers/amba-sdk-unity.git` |\n\nAll SDKs expose the same surface: `Amba.configure({ projectId, apiKey })`, then `Amba.events.track(...)`, `Amba.users.*`, `Amba.collections.*`, etc. Per-stack quickstart pages with the exact initialization snippet: `https://docs.amba.dev/sdk/<framework>`.\n\n## What Amba does\n\n### Identity\n- **users** \u2014 app-user registry. Auto-created on first SDK call; admin via `amba_users_*`.\n- **roles + permissions** \u2014 RBAC. Define with `amba_roles_create`; assign via `amba_roles_assign`.\n- **api_keys** \u2014 client + server keys per project. Mint via `amba_api_keys_create`.\n\n### Engagement\n- **onboarding** \u2014 multi-step first-run flows. Define with `amba_onboarding_create`; SDK `Amba.onboarding.next()`.\n- **segments** \u2014 user cohorts. Define with `amba_segments_create`; used as push/feed targets.\n- **push** \u2014 scheduled or triggered notifications. Chain: configure integrations (apns/fcm) \u2192 `amba_push_campaigns_create` \u2192 `amba_push_campaigns_send` (or schedule).\n- **referrals** \u2014 referral codes. Define with `amba_referrals_create`.\n- **deeplinks** \u2014 universal links. Set domain with `amba_deeplinks_set_config`.\n- **tracked_links** \u2014 UTM-tagged outbound links. Define with `amba_tracked_links_create`.\n- **content** \u2014 episodic delivery (lessons, quotes, daily prompts). Chain: `amba_content_libraries_create` \u2192 `amba_content_items_add` \u2192 `amba_content_schedules_create`.\n\n### Gamification\n- **xp** \u2014 experience points + level. Define rules with `amba_xp_rules_create`; SDK `Amba.xp.getBalance`.\n- **achievements** \u2014 earnable badges. Define with `amba_achievements_create`; unlock via xp rules or `amba_inventory_grant_item`.\n- **streaks** \u2014 recurring engagement counters. Define with `amba_streaks_create`; client calls `Amba.streaks.qualify(key)`.\n- **leaderboards** \u2014 ranked user lists. Define with `amba_leaderboards_create`; populated from events.\n- **challenges** \u2014 time-bounded goals. Define with `amba_challenges_create`; progress via SDK.\n\n### Economy\n- **currencies** \u2014 virtual currencies (coins, gems). Define with `amba_currencies_create`; grant via `amba_currencies_grant` or event rules via `amba_currency_grant_rules_create`; debit via `amba_currencies_spend` (atomic, rejects on insufficient funds).\n- **catalog + stores** \u2014 purchasable items + storefronts. Chain: `amba_catalog_items_create` \u2192 `amba_catalog_items_set_price` \u2192 `amba_stores_create` \u2192 `amba_stores_add_listing`. (Define currency first.)\n- **inventory** \u2014 items users own. Read via SDK `Amba.inventory.*`; grant with `amba_inventory_grant_item`.\n\n### Social\n- **friendships** \u2014 friend graph. SDK `Amba.friends.*`; admin via `amba_friendships_*`.\n- **groups** \u2014 guilds/parties/chats. Define with `amba_groups_create`; members managed via SDK + admin tools.\n- **messaging** \u2014 DMs + group chat. Enabled by default; moderate via `amba_messaging_*`.\n- **feeds** \u2014 algorithmic activity feeds. Define ranking with `amba_feeds_rules_create`.\n- **reviews** \u2014 user-submitted reviews. Enabled by default; moderate via `amba_reviews_*`.\n- **moderation** \u2014 content review queue + trust scores. Configure with `amba_moderation_configure`; review via `amba_moderation_queue_list`.\n\n### Analytics\n- **events** \u2014 track user actions. SDK `Amba.events.track()`; query via `amba_events_count`.\n- **sessions** \u2014 session telemetry. Tracked automatically; query via `amba_sessions_list`.\n- **analytics** \u2014 funnels + retention. Query via `amba_analytics_get`.\n\n### Infrastructure\n- **collections** \u2014 your own typed key-value tables. Define with `amba_collections_create`; read/write from SDK `Amba.client.*`.\n- **functions** \u2014 serverless TypeScript handlers. Deploy with `amba_functions_deploy`; schedule with `amba_functions_schedule`.\n- **sites** \u2014 static site hosting at `*.app.amba.host`. Deploy with `amba_sites_deploy`.\n- **media** \u2014 file storage + CDN. Upload via `amba_media_upload`.\n- **secrets** \u2014 env vars for functions. Set via `amba_secrets_set`.\n- **configs** \u2014 remote config flags. Define with `amba_remote_configs_create`.\n- **integrations** \u2014 third-party webhooks (RevenueCat, Superwall, AppsFlyer, etc.). Configure with `amba_integrations_configure`.\n- **ai_prompts** \u2014 versioned LLM prompts callable from SDK. Define with `amba_ai_prompts_create`; call via `amba_ai_prompts_invoke`.\n";
|
|
43
|
+
export declare const AMBA_SETUP_GUIDE_MD = "# Amba\n\n## What Amba is\n\nAmba is the backend your app calls at runtime via the SDK. The MCP tools (`amba_*`) are for provisioning at build time \u2014 call them as the coding agent.\n\nOne Amba project = one isolated tenant database + one set of API keys + a hosted MCP at `mcp.amba.dev` for agent-driven provisioning. The runtime traffic goes through an SDK (`@layers/amba-*` on npm, `Amba` on SPM / Maven / pub.dev). The provisioning traffic \u2014 what you're about to do \u2014 goes through the tools listed below.\n\nYour job is to take the user from \"I have an app\" to \"Amba is wired in and the features are real\" in one pass, without 12 follow-up questions. The journey below is the standard playbook. Don't deviate without a reason.\n\n## Step 0 \u2014 Bootstrap credentials\n\nYou're in an MCP client (Claude.ai web, Claude Desktop, Cursor, Windsurf, etc.) without a local CLI. The MCP server has one tool that works **without** a Bearer token: `amba_developer_signup`. Use it to mint a personal access token (PAT), project, and client/server keys in a single round trip \u2014 no browser, no email verification.\n\nCall:\n\n```\namba_developer_signup({\n email: \"sandbox-<unix-seconds>-<6char-nonce>@layers.com\",\n password: \"<32 random base64url chars>\",\n name: \"amba-sandbox-agent\"\n})\n```\n\nThe response shape:\n\n```json\n{\n \"data\": {\n \"pat\": \"amb_dpat_\u2026\",\n \"developer\": { \"id\": \"\u2026\", \"email\": \"\u2026\", \"tier\": \"agent_sandbox\" },\n \"project\": {\n \"project_id\": \"\u2026\",\n \"client_key\": \"amb_ck_\u2026\",\n \"server_key\": \"amb_sk_\u2026\",\n \"provisioning_status\": \"provisioning\",\n \"verify_url\": \"https://app.amba.dev/verify?token=\u2026\",\n \"verify_token\": \"\u2026\"\n }\n }\n}\n```\n\nAfter signup, the PAT is in your hand \u2014 keep building in THIS session immediately. Pass `pat: \"<the PAT from the response>\"` as an inline argument on every subsequent `amba_*` tool call; the inline arg overrides the inbound Bearer for that one call so no client config change is needed mid-session. For future sessions, write the matching snippet from `mcp_config` to the customer's MCP-client config file so the static Bearer takes over automatically on the next launch.\n\nThe project status is `\"provisioning\"` immediately after signup; the per-tenant DB activates within ~5 seconds. Don't block \u2014 your next call may briefly retry, that's fine. If you want to be polite, call `amba_projects_get_provisioning_status({ project_id })` once and proceed when it returns `\"active\"` (or after 15s, whichever first).\n\nTell the user where their credentials live:\n\n- `pat` \u2014 the Bearer they should configure in this MCP client's settings (and treat like a password).\n- `project_id`, `client_key` \u2014 the values they paste into their app's `.env.local` / `.env`.\n- `server_key` \u2014 never ship to user devices; only into a server `.env` or a secret manager. The `amb_dev_sk_` / `amb_live_sk_` prefix is the marker.\n\n**Already have a PAT?** Skip the signup. Call `amba_developer_me({})` to verify the Bearer; if it succeeds, either reuse the most recent project (`amba_projects_list`) or call `amba_projects_create({ name: \"<app-name>\", platform: \"all\" })` and then `amba_api_keys_create` twice to mint client + server keys for `environment: \"development\"`.\n\n**Can't reach `amba_developer_signup` \u2014 it's not in your tool list, or your harness blocks the call?** Do NOT try to work around it by adding the MCP server yourself, running an installer, or editing your own settings/permissions \u2014 refusing to self-modify or widen your own permissions is the correct call, and you should not circumvent it. Instead, hand the developer **one** command to run in **their own** terminal (which is not subject to your sandbox):\n\n```bash\nnpx @layers/amba init\n```\n\nIt creates the account with no browser and writes `AMBA_PROJECT_ID` + `AMBA_CLIENT_KEY` to `.env.local` (and wires this MCP server up for your next session). Ask them to run it, tell you when it's done, then resume from this Step 0 by reading `.env.local`. Asking the developer to paste an `amb_ck_\u2026` client key is the last-resort lite path \u2014 it's enough to wire `Amba.configure()` + runtime calls and verify events land, but a client key alone can't do admin provisioning (creating achievements, collections, segments, etc.), which needs the PAT or server key.\n\n## Step 1 \u2014 Classify the app\n\nLook at what the user told you and at any files they shared. You're trying to pick one of ten presets in 30 seconds, not write a treatise. Inputs:\n\n- The user's prompt \u2014 \"I'm building a fitness tracker\" / \"a marketplace for\u2026\" / \"a Duolingo for X\".\n- README content if shared.\n- `package.json` / `pubspec.yaml` / `build.gradle.kts` / `Package.swift` \u2014 framework + dependencies.\n- Screen / view names \u2014 `WorkoutScreen`, `MatchView`, `LessonPage`, `CartView`, `ProductDetail`, `ChatThread`.\n\nPick the closest match:\n\n| Preset | When | Default Amba surfaces |\n| --- | --- | --- |\n| **fitness** | health / fitness tracker (workouts, steps, meditation) | identity (Apple+Google), push, XP, achievements, streaks, leaderboards, content (daily tips) |\n| **social** | social network / community (friends, feeds, groups) | identity, push, friends, groups, feeds, messaging, moderation, content |\n| **marketplace** | commerce / marketplace (catalog, stores, payments) | identity, push, catalog, stores, currencies (loyalty), reviews, segments |\n| **productivity** | productivity / SaaS tool (collaboration, milestones) | identity (Apple+Google+OTP), push, collections, achievements, content (changelog), segments |\n| **education** | education / learning app (courses, progress, rewards) | identity, push, XP, achievements, streaks, leaderboards, content (lessons), onboarding |\n| **game** | game / casual gaming | identity (anon-first), push, XP, achievements, currencies, inventory, leaderboards, challenges, stores |\n| **dating** | dating / matching app | identity (phone-OTP), push, friends (matches), messaging, moderation (heavy), reviews |\n| **content_creator** | content platform (feeds, subscriptions, tips) | identity, push, feeds, content, currencies (tips), referrals, stores (subscriptions) |\n| **ai_chatbot** | AI / chatbot / assistant app | identity, push, AI prompts, currencies (credits), content (system prompts), onboarding |\n| **custom** | none of the above | pick features individually |\n\nDetection heuristics, in priority order:\n\n1. The user's own description \u2014 most direct signal.\n2. Filename match in `screens/` or `views/` (high signal).\n3. Dependency in `package.json` \u2014 `react-native-health` \u2192 fitness, `@stream-io/*` \u2192 social or dating, `@stripe/*` \u2192 marketplace, `revenuecat` \u2192 marketplace or content_creator.\n4. README copy \u2014 \"fitness\", \"habit\", \"match\", \"chat\", \"store\", \"subscription\".\n\nIf two presets tie, pick the one the user's filenames match more closely. If still tied or no signal, fall back to **custom** and let them pick.\n\n## Step 2 \u2014 Confirm with the user\n\nUse a single multi-choice. Quote the surfaces from the table above so they know what they're getting.\n\n**Question 1: classification + scope**\n\n> I'm reading this as a **\\{kind\\}** app. I'd wire up: **\\{surfaces\\}**. Sound right?\n>\n> 1. Yes, wire it up as proposed (Recommended)\n> 2. Same kind but I want to pick features individually\n> 3. Wrong kind \u2014 let me pick from the list\n> 4. Custom \u2014 I'll pick features manually\n\nIf the user picks 1, go to Step 3. If 2 or 4, follow up with a multi-select of surfaces. If 3, present the table again and pick a different preset.\n\n**Question 2 (preset-specific):** see the per-surface sub-resources (`amba://setup/<surface>`) for the full \"Common follow-ups\" list. Examples:\n\n- **fitness / game / education** \u2014 leaderboard scope? (all-time, weekly, daily, none)\n- **game / content_creator** \u2014 virtual currency name? (`gold`, `gems`, `coins`, `credits` \u2014 defaults to `coins`)\n- **content_creator** \u2014 monetization? (tips, subscriptions, both)\n- **dating** \u2014 phone OTP or email-only? (phone strongly recommended)\n- **ai_chatbot** \u2014 daily free credit cap?\n\nBatch the follow-ups into one or two multi-choice rounds. Don't drip-feed six separate questions.\n\n## Step 3 \u2014 Wire it up\n\nFor each surface in the confirmed set, read the relevant sub-resource and execute its procedure. Each sub-resource is the full per-surface playbook (MCP tools + SDK init per stack + common follow-ups + re-run behavior):\n\n- **identity** (auth, anonymous/Apple/Google/OTP/magic-link, link/unlink) \u2192 `amba://setup/identity`\n- **engagement** (push, segments, content libraries, onboarding flows, deeplinks, referrals, tracked links) \u2192 `amba://setup/engagement`\n- **gamification** (XP rules, achievements, streaks, leaderboards, challenges) \u2192 `amba://setup/gamification`\n- **economy** (currencies, catalog, stores, inventory) \u2192 `amba://setup/economy`\n- **social** (friends, groups, feeds, messaging, moderation, reviews) \u2192 `amba://setup/social`\n- **infrastructure** (collections / DB tables, functions, analytics, AI prompts, media, secrets, configs, integrations, sites) \u2192 `amba://setup/infrastructure`\n\nThe general flow for every surface:\n\n1. **Detect stack.** Look at `package.json`, `pubspec.yaml`, `build.gradle.kts`, `ios/*.xcodeproj`. The detection rules:\n - `pubspec.yaml` present \u2192 Flutter.\n - `package.json` with `expo` \u2192 Expo.\n - `package.json` with `react-native` (no `expo`) \u2192 bare React Native.\n - `package.json` with `react` (no `react-native`) \u2192 web (or Next.js \u2014 same SDK).\n - `Package.swift` or `*.xcodeproj` only \u2192 iOS Swift.\n - `build.gradle.kts` or `build.gradle` with `com.android.application` \u2192 Android Kotlin.\n - Multiple (e.g. `ios/` + `android/` inside an Expo repo) \u2192 Expo wins.\n\n2. **Create resources via MCP.** Call the `amba_<surface>_create` tools to mint the definitions. Always include `project_id` from the project you created in Step 0. Always show the user the tool call before making destructive changes (creating a resource isn't destructive \u2014 but creating 30 of them is noisy).\n\n3. **Write SDK init code.** Drop the per-stack snippet (from the sub-resource) into the user's entry file. Detection:\n - Expo / React Native: `app/_layout.tsx`, `App.tsx`, `index.js` (in that order)\n - web / Next.js: `app/layout.tsx`, `pages/_app.tsx`, `src/main.tsx`, `src/App.tsx`\n - iOS Swift: `Sources/<App>/<App>App.swift`, `App/AppDelegate.swift`\n - Android Kotlin: `app/src/main/java/.../<App>.kt` (the `Application` subclass \u2014 create one if missing)\n - Flutter: `lib/main.dart`\n\n Always make additive edits \u2014 `await Amba.configure(...)` next to existing init, not replacing it. Never refactor existing auth or storage code; if the user has Firebase Auth or Supabase, leave it. Amba's auth is opt-in per call.\n\n4. **Run the project's existing test command** to confirm nothing broke. Detection:\n - `package.json` `scripts.test` \u2192 `npm test` (or `pnpm test` if `pnpm-lock.yaml` present)\n - `pubspec.yaml` \u2192 `flutter test`\n - `build.gradle.kts` \u2192 `./gradlew test` (skip on first wire-up \u2014 slow)\n - iOS \u2014 skip (need a simulator).\n\n If tests fail because of your edits, undo the offending edit and surface a clear error. If they fail for unrelated reasons (pre-existing red), note it and proceed.\n\n5. **Verify with the SDK.** Tell the user to call `Amba.diagnostics.ping()` (`Amba.Diagnostics.Ping()` on Unity) in their entry file. It returns `{ ok, server_project_id, environment, key_fingerprint, latency_ms }`. `ok: true` with the expected `server_project_id` confirms the wiring.\n\n## Step 4 \u2014 Report\n\nTell the user a structured summary. Use this exact shape so they can skim it fast:\n\n```\nAmba is wired in. Here's what changed:\n\nDONE\n - identity: Apple + Google sign-in available; signInAnonymously() called at app start\n - gamification: 3 achievements, 1 streak, 1 leaderboard created\n resources: first_workout, week_warrior, century_club / daily_workout / weekly_xp\n - engagement: push registration wired; default segment \"active_users\" created\n\nSKIPPED (low signal \u2014 re-run with /amba <feature> if you want them)\n - economy: no in-app currency UI found in your screens\n - social: no friends/feed surfaces found\n\nNEEDS YOUR INPUT\n - Apple Sign In: add the \"Sign in with Apple\" capability in Xcode > Signing & Capabilities.\n - Google Sign In: paste your Google OAuth client ID into amba_projects_update({ google_oauth_client_id: \"...\" }).\n - APNs / FCM: configure credentials with amba_integrations_configure (paste the .p8 / service-account JSON inline) before push delivers.\n\nNEXT STEPS\n - Paste AMBA_CLIENT_KEY into your build env (already shown above)\n - Trigger a workout in your existing flow \u2014 watch the achievement unlock + XP land\n - Open https://app.amba.dev to see users pour in\n```\n\nBe specific. List resources by key, not \"some achievements\". If something needs the user's input (third-party credentials, OAuth client IDs, push certs), say it clearly with the exact next action.\n\n## Stance (read this once)\n\n- **Don't ask which surfaces to use.** Classify, then confirm in one multi-choice. The taxonomy is the whole point.\n- **Default to additive, non-breaking changes.** Don't refactor existing auth, storage, or networking code. Drop in `await Amba.configure(...)` next to whatever the user already has.\n- **Never create resources without the user's confirmation in Step 2.** A 3rd-party \"convenience\" achievement called `first_login` is debt.\n- **If something is genuinely ambiguous** (leaderboard scope, currency real-money vs virtual, dating phone vs email), ask via a follow-up multi-choice. Don't guess and don't paragraph-it.\n- **clientKey vs serverKey.** `AMBA_CLIENT_KEY` (`amb_dev_ck_\u2026` in dev, `amb_live_ck_\u2026` in prod) ships to user devices. `AMBA_SERVER_KEY` (`amb_dev_sk_\u2026` / `amb_live_sk_\u2026`) never does \u2014 only into server `.env` or a secret manager. Mixing them is the #1 security mistake; if you're writing into a file that ships with the app binary, it's the client key, period.\n- **Don't echo the PAT in chat output on every call.** Showing it once after signup is fine; do not repeat it.\n\n## Get credentials (cheat sheet)\n\n- No terminal, in an MCP client: call `amba_developer_signup` (no Bearer required) \u2014 this guide's Step 0.\n- With a terminal: `npx -y @layers/amba init` signs up, mints a project + client/server keys, writes `.env.local` + `AMBA.md`, installs the `/amba` skill, and wires `mcpServers.amba` into every detected MCP-client config in one command. Auto-detects non-TTY invocations (the coding-agent bash-tool case) and runs headlessly.\n- Bind the sandbox account to a real email later: `npx @layers/amba claim me@example.com`. The backend emails a one-click magic link; clicking it lifts the sandbox cap to the Free tier.\n- Hosted MCP endpoint: `https://mcp.amba.dev/mcp` (Streamable HTTP, Bearer auth).\n\n## SDKs\n\n| Stack | Registry | Package |\n|---|---|---|\n| Browser / Node / React / React Native / Expo | npm | `@layers/amba-{web,node,react,react-native,expo}` |\n| Swift | SPM | `https://github.com/layers/amba-sdk-ios` |\n| Kotlin | Maven Central | `com.layers.amba:amba-sdk-android` |\n| Flutter | pub.dev | `amba` |\n| Unity | UPM (git) | `https://github.com/layers/amba-sdk-unity.git` |\n\nAll SDKs expose the same surface: `Amba.configure({ projectId, apiKey })`, then `Amba.events.track(...)`, `Amba.users.*`, `Amba.collections.*`, etc. Per-stack quickstart pages with the exact initialization snippet: `https://docs.amba.dev/sdk/<framework>`.\n\n## What Amba does\n\n### Identity\n- **users** \u2014 app-user registry. Auto-created on first SDK call; admin via `amba_users_*`.\n- **roles + permissions** \u2014 RBAC. Define with `amba_roles_create`; assign via `amba_roles_assign`.\n- **api_keys** \u2014 client + server keys per project. Mint via `amba_api_keys_create`.\n\n### Engagement\n- **onboarding** \u2014 multi-step first-run flows. Define with `amba_onboarding_create`; SDK `Amba.onboarding.next()`.\n- **segments** \u2014 user cohorts. Define with `amba_segments_create`; used as push/feed targets.\n- **push** \u2014 scheduled or triggered notifications. Chain: configure integrations (apns/fcm) \u2192 `amba_push_campaigns_create` \u2192 `amba_push_campaigns_send` (or schedule).\n- **referrals** \u2014 referral codes. Define with `amba_referrals_create`.\n- **deeplinks** \u2014 universal links. Set domain with `amba_deeplinks_set_config`.\n- **tracked_links** \u2014 UTM-tagged outbound links. Define with `amba_tracked_links_create`.\n- **content** \u2014 episodic delivery (lessons, quotes, daily prompts). Chain: `amba_content_libraries_create` \u2192 `amba_content_items_add` \u2192 `amba_content_schedules_create`.\n\n### Gamification\n- **xp** \u2014 experience points + level. Define rules with `amba_xp_rules_create`; SDK `Amba.xp.getBalance`.\n- **achievements** \u2014 earnable badges. Define with `amba_achievements_create`; unlock via xp rules or `amba_inventory_grant_item`.\n- **streaks** \u2014 recurring engagement counters. Define with `amba_streaks_create`; client calls `Amba.streaks.qualify(key)`.\n- **leaderboards** \u2014 ranked user lists. Define with `amba_leaderboards_create`; populated from events.\n- **challenges** \u2014 time-bounded goals. Define with `amba_challenges_create`; progress via SDK.\n\n### Economy\n- **currencies** \u2014 virtual currencies (coins, gems). Define with `amba_currencies_create`; grant via `amba_currencies_grant` or event rules via `amba_currency_grant_rules_create`; debit via `amba_currencies_spend` (atomic, rejects on insufficient funds).\n- **catalog + stores** \u2014 purchasable items + storefronts. Chain: `amba_catalog_items_create` \u2192 `amba_catalog_items_set_price` \u2192 `amba_stores_create` \u2192 `amba_stores_add_listing`. (Define currency first.)\n- **inventory** \u2014 items users own. Read via SDK `Amba.inventory.*`; grant with `amba_inventory_grant_item`.\n\n### Social\n- **friendships** \u2014 friend graph. SDK `Amba.friends.*`; admin via `amba_friendships_*`.\n- **groups** \u2014 guilds/parties/chats. Define with `amba_groups_create`; members managed via SDK + admin tools.\n- **messaging** \u2014 DMs + group chat. Enabled by default; moderate via `amba_messaging_*`.\n- **feeds** \u2014 algorithmic activity feeds. Define ranking with `amba_feeds_rules_create`.\n- **reviews** \u2014 user-submitted reviews. Enabled by default; moderate via `amba_reviews_*`.\n- **moderation** \u2014 content review queue + trust scores. Configure with `amba_moderation_configure`; review via `amba_moderation_queue_list`.\n\n### Analytics\n- **events** \u2014 track user actions. SDK `Amba.events.track()`; query via `amba_events_count`.\n- **sessions** \u2014 session telemetry. Tracked automatically; query via `amba_sessions_list`.\n- **analytics** \u2014 funnels + retention. Query via `amba_analytics_get`.\n\n### Infrastructure\n- **collections** \u2014 your own relational Postgres tables (typed columns, foreign keys, transactions, unique indexes, vector search). Define with `amba_collections_create`; read/write from SDK `Amba.client.*`.\n- **functions** \u2014 serverless TypeScript handlers. Deploy with `amba_functions_deploy`; schedule with `amba_functions_schedule`.\n- **sites** \u2014 static site hosting at `*.app.amba.host`. Deploy with `amba_sites_deploy`.\n- **media** \u2014 file storage + CDN. Upload via `amba_media_upload`.\n- **secrets** \u2014 env vars for functions. Set via `amba_secrets_set`.\n- **configs** \u2014 remote config flags. Define with `amba_remote_configs_create`.\n- **integrations** \u2014 third-party webhooks (RevenueCat, Superwall, AppsFlyer, etc.). Configure with `amba_integrations_configure`.\n- **ai_prompts** \u2014 versioned LLM prompts callable from SDK. Define with `amba_ai_prompts_create`; call via `amba_ai_prompts_invoke`.\n";
|
|
44
44
|
/** Canonical URI for the MCP resource. */
|
|
45
45
|
export declare const AMBA_SETUP_GUIDE_URI = "amba://setup";
|
|
46
46
|
/** Canonical MIME type for the guide body. */
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* so it renders identically as `.md` (the MCP / skill consumers) and
|
|
29
29
|
* as `.mdx` (the docs site).
|
|
30
30
|
*/
|
|
31
|
-
export declare const EXPO_BUILD_PROMPT_MD = "> **Last reviewed:** 2026-05-17. The canonical version of this page lives\n> at [docs.amba.dev/prompts/expo-build](https://docs.amba.dev/prompts/expo-build).\n> If you're reading an inlined snapshot from your\n> `.claude/skills/amba-build/SKILL.md`, check the URL above for updates.\n\nThis is the prompt an AI coding agent runs to build a full Expo app\nwhere Amba is the only backend. It's structured as a single `/goal`\ndirective \u2014 paste it, replace `<DESIGN_HASH>` with whatever describes\nyour design (a URL, a description, a Figma link), and let the agent\nexecute.\n\n## Quick setup\n\nThe CLI handles signup, project provisioning, env-file writes, and MCP\nclient config wiring in one command:\n\n```bash\nnpx -y @layers/amba init\n```\n\nThat's the entire setup. The CLI:\n\n1. Signs up an agent-mode developer account (no browser, no email\n verification needed for sandbox).\n2. Creates an Amba project and mints a client key + admin PAT.\n3. Writes `.env.local` (`AMBA_PROJECT_ID`, `AMBA_CLIENT_KEY`,\n `AMBA_API_URL`).\n4. Writes `AMBA.md` (project-scoped context for the agent).\n5. Auto-wires `mcpServers.amba` into every MCP client config it\n detects on disk \u2014 Claude Code, Cursor, Windsurf.\n6. Verifies the PAT against the API and confirms it's good.\n\nThe Amba MCP toolset (`amba_*` tools \u2014 ~130 of them) is available to\nthe agent immediately: pass the freshly-minted `pat` as an inline\nargument on every `amba_*` call in the current session. The next time\nyour MCP client starts it picks the PAT up from the config as the\ninbound Bearer automatically \u2014 at that point the `pat` arg becomes\noptional. No restart needed; nothing for you to do.\n\nIf you have the `/amba-build` skill installed (via\n`npx -y @layers/amba init`), invoke it directly:\n\n```\n/amba-build <DESIGN_HASH>\n```\n\nOtherwise paste the prompt below.\n\n## Current known gotchas\n\nThree remaining wrinkles you may hit. Everything else from the 2026-05\nDX cascade is fixed.\n\n- **Web CORS** \u2014 the public API does not currently send\n `Access-Control-Allow-Origin` for browser-origin requests. Use the\n agent's circuit-break-on-second-failure rule for web targets; for\n Expo (iOS + Android) you'll never see this.\n- **React Native bundle size** \u2014 the React Native SDK adds ~4 MB to\n the JS bundle today. Functional, just heavier than the long-term\n goal. Tracked separately.\n- **Sandbox MAU cap (100)** \u2014 the agent-mode sandbox tier caps at 100\n monthly active users. If you blow through it during testing, call\n `amba_users_reset_sandbox` to clear the counter \u2014 that tool exists\n specifically for this. Upgrade to the Free tier (1,000 MAU, 500 MB\n DB) by running `amba claim me@example.com` from the terminal \u2014 the\n backend emails a one-click magic link to the address you pass in;\n clicking it binds the account to that email and lifts the cap.\n\n## How to read the design\n\nIf `<DESIGN_HASH>` is a URL to a packaged design (e.g. a download\nlink from your design tool of choice), unpack it before you start:\n\n```bash\nmkdir -p design && cd design\ncurl -L \"<DESIGN_HASH>\" -o design.tar.gz\ngunzip -c design.tar.gz | tar -x\nls\n# Expected: README, chats/, project/ (or equivalent)\n```\n\nRead the README first \u2014 it should tell you what each subdirectory\nholds. The `chats/` directory typically contains conversation logs\nthat capture the design intent in dialog form; treat them as the\nauthoritative source for tone and feature priorities. The `project/`\ndirectory holds the structured asset graph (screens, components,\nstyles).\n\nIf `<DESIGN_HASH>` is a freeform description (not a URL), skip the\nunpacking and treat the description text as the design brief.\n\n## Use Amba for everything\n\nThe rule: any feature that touches data, identity, scheduling,\nnotifications, content, or social \u2014 use Amba. Don't reach for\nAsyncStorage-as-database, don't bring in Firebase / Supabase / your\nown server, don't roll a custom auth layer. The point of this build\nis that Amba covers it all.\n\nSpecifically: every feature in the design that needs a backend maps\nto an Amba primitive. If you can't find a fit, the rule is **escalate\nin the gaps log** (see the verification gate), not \"ship without\nAmba.\" Skipping a primitive needs a written justification \u2014 the\nverification gate enforces this.\n\n## Feature \u2192 Amba primitive map\n\n| App feature | Amba primitive | MCP tools |\n| -------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| User accounts (anonymous + Apple + Google + email) | Auth | `amba_developer_signup` (one-time bootstrap), `Amba.signIn()` SDK calls |\n| Profile data (name, avatar, prefs) | App users | `amba_users_list`, `amba_users_get`, `amba_users_bulk_update` |\n| Daily content (tips, lessons, quotes) | Content libraries + schedules | `amba_content_libraries_create`, `amba_content_items_add`, `amba_content_schedules_create`, `amba_content_list_libraries`, `amba_content_list_items`, `amba_content_list_schedules` |\n| Push notifications | Push campaigns | `amba_push_campaigns_create`, `amba_push_campaigns_send`, `amba_push_send_test`, `amba_push_list_campaigns` |\n| User segments (e.g. inactive 7d, premium) | Segments | `amba_segments_create`, `amba_segments_list`, `amba_segments_evaluate` |\n| Daily streaks | Streaks | `amba_streaks_create`, `amba_streaks_list` (call `streaks.qualify()` from the SDK to record activity) |\n| XP and levels | XP rules | `amba_xp_rules_create`, `amba_xp_rules_list`, `amba_users_get_xp` |\n| Achievements / badges | Achievements | `amba_achievements_create`, `amba_achievements_list`, `amba_achievements_get` |\n| Challenges (time-limited goals) | Challenges | `amba_challenges_create`, `amba_challenges_list`, `amba_challenges_list_participants` |\n| Leaderboards | Leaderboards | `amba_leaderboards_create`, `amba_leaderboards_list`, `amba_leaderboards_get` |\n| In-app currency / virtual goods | Economy (currencies + catalog + stores) | `amba_currencies_create`, `amba_catalog_items_create`, `amba_stores_create`, `amba_currencies_grant`, `amba_users_get_inventory` |\n| Social (friends, groups, feed, DMs) | Social primitives | `amba_create_group`, `amba_groups_list`, `amba_groups_update_member`, `amba_friendships_list` (feeds + messaging via SDK: `Amba.feeds.*`, `Amba.messaging.*`) |\n| Remote feature flags / config | Configs | `amba_configs_create`, `amba_configs_list`, `amba_configs_update` |\n| Entitlements (premium / paywall) | RevenueCat / Superwall integration | `amba_integrations_configure`, `amba_integrations_test` |\n| Custom data (anything not above) | Collections | `amba_collections_create`, `amba_collections_alter`, `amba_collections_list`, `amba_admin_insert_row`, `amba_admin_list_rows`, plus client-side `Amba.collections.*` |\n| Analytics / event tracking | Events | `Amba.events.track(...)` from the SDK; query with `amba_analytics_get`, `amba_users_list_events` |\n\nEvery primitive above has list / read MCP tools you can use to verify\nseed data after creation \u2014 the verification gate uses these to catch\n\"fake implementation\" failure modes (where the app code thinks a thing\nwas created but nothing actually landed in the backend).\n\n## Seed data\n\nBefore writing app code, seed the backend with enough data that every\nscreen in the design has something realistic to render. Order:\n\n1. **Configs** \u2014 feature flags + tunable constants the app reads at\n boot (`amba_configs_create`).\n2. **Segments** \u2014 at least one (e.g. \"new_user\", first 7 days) so\n targeting works downstream.\n3. **Content libraries + schedules** \u2014 daily content for any\n tips/quotes/lessons screen. Seed \u226530 items so the carousel /\n day-stepper doesn't loop visibly.\n4. **Streaks** \u2014 define the streak shape (daily / weekly, grace\n window, freeze policy).\n5. **XP rules** \u2014 events \u2192 XP-award rules so XP accrues from real\n gameplay.\n6. **Achievements** \u2014 unlock criteria for badges.\n7. **Challenges** \u2014 at least one active challenge with rewards.\n8. **Leaderboards** \u2014 XP, streaks, or any custom metric.\n9. **Currencies + catalog + stores** \u2014 virtual currency, catalog\n items, store listings (only if the design has an economy screen).\n10. **Collections** \u2014 schemas + sample rows for any custom data the\n app needs (e.g. user-generated content, journal entries, custom\n list items).\n11. **Push campaigns** \u2014 at least one welcome push + one re-engagement\n push targeting your \"new_user\" segment.\n\nAfter seeding, the verification gate (below) confirms each primitive\nexists by calling the matching `amba_*_list` MCP tool. Empty list \u2192\nfailure.\n\n## Engineering rules\n\nThese are non-negotiable. Violating any one of them fails the build\ngate.\n\n- **Expo Router with typed routes.** Use `expo-router` and enable\n `experiments.typedRoutes` in `app.json`. Every screen is a\n filesystem route; no manual navigation stacks.\n- **TypeScript strict mode.** `strict: true` in `tsconfig.json`. Zero\n `any`. Zero `@ts-ignore`. `tsc --noEmit` must pass.\n- **React Native primitives only.** `View`, `Text`, `Pressable`,\n `ScrollView`, `FlatList`, `Image`. No `div`, no `span`, no DOM-only\n libs. The build target is iOS + Android + Web \u2014 every screen has to\n render on all three.\n- **Fonts via expo-font.** Don't ship system-font-only screens; load\n the design's typography via `useFonts` and gate the splash screen\n on load.\n- **Persistence via AsyncStorage.** Anything you cache client-side\n (theme choice, last-viewed-item, dismissed banners) goes in\n AsyncStorage. Never sprinkle direct file I/O.\n- **Theme system.** A single `theme.ts` exports light + dark token\n maps; consume via a `useTheme()` hook. The verification gate\n toggles light \u2194 dark and screenshots; if any screen has hardcoded\n colors that don't flip, the gate fails.\n- **Circuit-break on second failure.** If two consecutive Amba API\n calls fail with the same error, stop retrying and surface a clean\n empty-state to the user. Don't loop forever; don't crash. The web\n CORS issue (above) is the most likely trigger.\n- **Deterministic offline fallback.** When `fetch` fails (airplane\n mode, network drop), the app renders **deterministic** placeholder\n content \u2014 same content per `userId + day` \u2014 never random. Real data\n swaps in when the network returns.\n- **Three-platform bundle gate.** `expo export --platform web`,\n `expo export --platform ios`, and `expo export --platform android`\n must all succeed. If any one fails, the build fails. No\n \"shipped iOS-only, web is broken\" \u2014 the rule is parity.\n- **Don't name a tab `settings.tsx`.** Use `account.tsx` or\n `preferences.tsx` instead. Expo Router's static web export generates\n `settings.html` correctly but does not resolve direct URL navigation\n to `/settings` \u2014 the client-side router shows an unmatched-route\n error while other tab names work fine. (Observed in dogfood; upstream\n behavior, not an Amba issue.)\n\n## Verification gate\n\nBefore declaring the build done, run every check in this list. Any\nfailure means the build is not done \u2014 fix and re-run.\n\n```bash\n# Type-check\npnpm tsc --noEmit\n\n# Three-platform export\npnpm expo export --platform web\npnpm expo export --platform ios\npnpm expo export --platform android\n```\n\nThen, from inside the agent (use the Amba MCP tools):\n\n- `amba_analytics_get` \u2192 at least one event tracked end-to-end\n through `Amba.events.track()` from the app.\n- `amba_users_list` \u2192 at least one user exists (the agent's own\n anonymous signin counts).\n- For every primitive the seed step created, call the matching\n `amba_*_list` and assert non-empty:\n - `amba_configs_list`\n - `amba_segments_list`\n - `amba_content_list_libraries`, `amba_content_list_items`,\n `amba_content_list_schedules`\n - `amba_streaks_list`\n - `amba_xp_rules_list`\n - `amba_achievements_list`\n - `amba_challenges_list`\n - `amba_leaderboards_list`\n - `amba_currencies_list` (if economy seeded)\n - `amba_catalog_list` (if catalog seeded)\n - `amba_collections_list` + `amba_admin_list_rows` per collection\n - `amba_push_list_campaigns`\n- Empty list for any seeded primitive \u2192 the implementation is fake\n (UI exists but never wrote to the backend). Failure.\n- Manually walk every route in the browser (`expo start --web`),\n screenshot each, and confirm:\n - Light theme renders cleanly.\n - Dark theme renders cleanly (toggle and re-screenshot every\n route).\n - Empty states render when collections are empty (fresh-install\n simulation: wipe AsyncStorage, reload).\n- `amba_users_reset_sandbox` to confirm you can recover from the MAU\n cap if you blew past 50 during testing.\n\nSkipping any primitive's seed step requires a one-line written\njustification in the gaps log (next section). \"We don't need\nstreaks\" is fine; silence is not.\n\n## Final output\n\nWhen done, write a final report to `BUILD_REPORT.md` in the project\nroot. Required sections:\n\n- **Start timestamp** (when the agent started).\n- **End timestamp** (when the verification gate last passed).\n- **MCP call inventory** \u2014 every `amba_*` tool you invoked, with a\n count. Lets a human reviewer audit \"did this agent actually use\n Amba for X\" at a glance.\n- **Primitive coverage table** \u2014 one row per primitive from the\n Feature \u2192 Amba primitive map. Mark each \u2705 (used), \u26A0\uFE0F (used with\n caveats \u2014 explain), or \u23ED (skipped \u2014 justify in one line).\n- **Gaps log** \u2014 every primitive you skipped, every feature you\n couldn't fit cleanly to an Amba primitive, every workaround. One\n line per gap, no marketing language.\n- **`seed-report.json`** \u2014 machine-readable seed summary:\n `{ \"primitive\": \"<name>\", \"created\": <count>, \"listed\": <count> }`\n for every primitive. The `listed` count comes from the\n `amba_*_list` call in the verification gate. `created ===\nlisted` for every row is the success condition.\n\nIf `BUILD_REPORT.md` is missing any required section, or\n`seed-report.json` is missing, the build is not done.\n";
|
|
31
|
+
export declare const EXPO_BUILD_PROMPT_MD = "> **Last reviewed:** 2026-05-17. The canonical version of this page lives\n> at [docs.amba.dev/prompts/expo-build](https://docs.amba.dev/prompts/expo-build).\n> If you're reading an inlined snapshot from your\n> `.claude/skills/amba-build/SKILL.md`, check the URL above for updates.\n\nThis is the prompt an AI coding agent runs to build a full Expo app\nwhere Amba is the only backend. It's structured as a single `/goal`\ndirective \u2014 paste it, replace `<DESIGN_HASH>` with whatever describes\nyour design (a URL, a description, a Figma link), and let the agent\nexecute.\n\n## Quick setup\n\nThe CLI handles signup, project provisioning, env-file writes, and MCP\nclient config wiring in one command:\n\n```bash\nnpx -y @layers/amba init\n```\n\nThat's the entire setup. The CLI:\n\n1. Signs up an agent-mode developer account (no browser, no email\n verification needed for sandbox).\n2. Creates an Amba project and mints a client key + admin PAT.\n3. Writes `.env.local` (`AMBA_PROJECT_ID`, `AMBA_CLIENT_KEY`,\n `AMBA_API_URL`).\n4. Writes `AMBA.md` (project-scoped context for the agent).\n5. Auto-wires `mcpServers.amba` into every MCP client config it\n detects on disk \u2014 Claude Code, Cursor, Windsurf.\n6. Verifies the PAT against the API and confirms it's good.\n\nThe Amba MCP toolset (`amba_*` tools \u2014 500+ of them) is available to\nthe agent immediately: pass the freshly-minted `pat` as an inline\nargument on every `amba_*` call in the current session. The next time\nyour MCP client starts it picks the PAT up from the config as the\ninbound Bearer automatically \u2014 at that point the `pat` arg becomes\noptional. No restart needed; nothing for you to do.\n\nIf you have the `/amba-build` skill installed (via\n`npx -y @layers/amba init`), invoke it directly:\n\n```\n/amba-build <DESIGN_HASH>\n```\n\nOtherwise paste the prompt below.\n\n## Current known gotchas\n\nThree remaining wrinkles you may hit. Everything else from the 2026-05\nDX cascade is fixed.\n\n- **Web CORS** \u2014 the public API does not currently send\n `Access-Control-Allow-Origin` for browser-origin requests. Use the\n agent's circuit-break-on-second-failure rule for web targets; for\n Expo (iOS + Android) you'll never see this.\n- **React Native bundle size** \u2014 the React Native SDK adds ~4 MB to\n the JS bundle today. Functional, just heavier than the long-term\n goal. Tracked separately.\n- **Sandbox MAU cap (100)** \u2014 the agent-mode sandbox tier caps at 100\n monthly active users. If you blow through it during testing, call\n `amba_users_reset_sandbox` to clear the counter \u2014 that tool exists\n specifically for this. Upgrade to the Free tier (1,000 MAU, 500 MB\n DB) by running `amba claim me@example.com` from the terminal \u2014 the\n backend emails a one-click magic link to the address you pass in;\n clicking it binds the account to that email and lifts the cap.\n\n## How to read the design\n\nIf `<DESIGN_HASH>` is a URL to a packaged design (e.g. a download\nlink from your design tool of choice), unpack it before you start:\n\n```bash\nmkdir -p design && cd design\ncurl -L \"<DESIGN_HASH>\" -o design.tar.gz\ngunzip -c design.tar.gz | tar -x\nls\n# Expected: README, chats/, project/ (or equivalent)\n```\n\nRead the README first \u2014 it should tell you what each subdirectory\nholds. The `chats/` directory typically contains conversation logs\nthat capture the design intent in dialog form; treat them as the\nauthoritative source for tone and feature priorities. The `project/`\ndirectory holds the structured asset graph (screens, components,\nstyles).\n\nIf `<DESIGN_HASH>` is a freeform description (not a URL), skip the\nunpacking and treat the description text as the design brief.\n\n## Use Amba for everything\n\nThe rule: any feature that touches data, identity, scheduling,\nnotifications, content, or social \u2014 use Amba. Don't reach for\nAsyncStorage-as-database, don't bring in Firebase / Supabase / your\nown server, don't roll a custom auth layer. The point of this build\nis that Amba covers it all.\n\nSpecifically: every feature in the design that needs a backend maps\nto an Amba primitive. If you can't find a fit, the rule is **escalate\nin the gaps log** (see the verification gate), not \"ship without\nAmba.\" Skipping a primitive needs a written justification \u2014 the\nverification gate enforces this.\n\n## Feature \u2192 Amba primitive map\n\n| App feature | Amba primitive | MCP tools |\n| -------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| User accounts (anonymous + Apple + Google + email) | Auth | `amba_developer_signup` (one-time bootstrap), `Amba.signIn()` SDK calls |\n| Profile data (name, avatar, prefs) | App users | `amba_users_list`, `amba_users_get`, `amba_users_bulk_update` |\n| Daily content (tips, lessons, quotes) | Content libraries + schedules | `amba_content_libraries_create`, `amba_content_items_add`, `amba_content_schedules_create`, `amba_content_list_libraries`, `amba_content_list_items`, `amba_content_list_schedules` |\n| Push notifications | Push campaigns | `amba_push_campaigns_create`, `amba_push_campaigns_send`, `amba_push_send_test`, `amba_push_list_campaigns` |\n| User segments (e.g. inactive 7d, premium) | Segments | `amba_segments_create`, `amba_segments_list`, `amba_segments_evaluate` |\n| Daily streaks | Streaks | `amba_streaks_create`, `amba_streaks_list` (call `streaks.qualify()` from the SDK to record activity) |\n| XP and levels | XP rules | `amba_xp_rules_create`, `amba_xp_rules_list`, `amba_users_get_xp` |\n| Achievements / badges | Achievements | `amba_achievements_create`, `amba_achievements_list`, `amba_achievements_get` |\n| Challenges (time-limited goals) | Challenges | `amba_challenges_create`, `amba_challenges_list`, `amba_challenges_list_participants` |\n| Leaderboards | Leaderboards | `amba_leaderboards_create`, `amba_leaderboards_list`, `amba_leaderboards_get` |\n| In-app currency / virtual goods | Economy (currencies + catalog + stores) | `amba_currencies_create`, `amba_catalog_items_create`, `amba_stores_create`, `amba_currencies_grant`, `amba_users_get_inventory` |\n| Social (friends, groups, feed, DMs) | Social primitives | `amba_create_group`, `amba_groups_list`, `amba_groups_update_member`, `amba_friendships_list` (feeds + messaging via SDK: `Amba.feeds.*`, `Amba.messaging.*`) |\n| Remote feature flags / config | Configs | `amba_configs_create`, `amba_configs_list`, `amba_configs_update` |\n| Entitlements (premium / paywall) | RevenueCat / Superwall integration | `amba_integrations_configure`, `amba_integrations_test` |\n| Custom data (anything not above) | Collections | `amba_collections_create`, `amba_collections_alter`, `amba_collections_list`, `amba_admin_insert_row`, `amba_admin_list_rows`, plus client-side `Amba.collections.*` |\n| Analytics / event tracking | Events | `Amba.events.track(...)` from the SDK; query with `amba_analytics_get`, `amba_users_list_events` |\n\nEvery primitive above has list / read MCP tools you can use to verify\nseed data after creation \u2014 the verification gate uses these to catch\n\"fake implementation\" failure modes (where the app code thinks a thing\nwas created but nothing actually landed in the backend).\n\n## Seed data\n\nBefore writing app code, seed the backend with enough data that every\nscreen in the design has something realistic to render. Order:\n\n1. **Configs** \u2014 feature flags + tunable constants the app reads at\n boot (`amba_configs_create`).\n2. **Segments** \u2014 at least one (e.g. \"new_user\", first 7 days) so\n targeting works downstream.\n3. **Content libraries + schedules** \u2014 daily content for any\n tips/quotes/lessons screen. Seed \u226530 items so the carousel /\n day-stepper doesn't loop visibly.\n4. **Streaks** \u2014 define the streak shape (daily / weekly, grace\n window, freeze policy).\n5. **XP rules** \u2014 events \u2192 XP-award rules so XP accrues from real\n gameplay.\n6. **Achievements** \u2014 unlock criteria for badges.\n7. **Challenges** \u2014 at least one active challenge with rewards.\n8. **Leaderboards** \u2014 XP, streaks, or any custom metric.\n9. **Currencies + catalog + stores** \u2014 virtual currency, catalog\n items, store listings (only if the design has an economy screen).\n10. **Collections** \u2014 schemas + sample rows for any custom data the\n app needs (e.g. user-generated content, journal entries, custom\n list items).\n11. **Push campaigns** \u2014 at least one welcome push + one re-engagement\n push targeting your \"new_user\" segment.\n\nAfter seeding, the verification gate (below) confirms each primitive\nexists by calling the matching `amba_*_list` MCP tool. Empty list \u2192\nfailure.\n\n## Engineering rules\n\nThese are non-negotiable. Violating any one of them fails the build\ngate.\n\n- **Expo Router with typed routes.** Use `expo-router` and enable\n `experiments.typedRoutes` in `app.json`. Every screen is a\n filesystem route; no manual navigation stacks.\n- **TypeScript strict mode.** `strict: true` in `tsconfig.json`. Zero\n `any`. Zero `@ts-ignore`. `tsc --noEmit` must pass.\n- **React Native primitives only.** `View`, `Text`, `Pressable`,\n `ScrollView`, `FlatList`, `Image`. No `div`, no `span`, no DOM-only\n libs. The build target is iOS + Android + Web \u2014 every screen has to\n render on all three.\n- **Fonts via expo-font.** Don't ship system-font-only screens; load\n the design's typography via `useFonts` and gate the splash screen\n on load.\n- **Persistence via AsyncStorage.** Anything you cache client-side\n (theme choice, last-viewed-item, dismissed banners) goes in\n AsyncStorage. Never sprinkle direct file I/O.\n- **Theme system.** A single `theme.ts` exports light + dark token\n maps; consume via a `useTheme()` hook. The verification gate\n toggles light \u2194 dark and screenshots; if any screen has hardcoded\n colors that don't flip, the gate fails.\n- **Circuit-break on second failure.** If two consecutive Amba API\n calls fail with the same error, stop retrying and surface a clean\n empty-state to the user. Don't loop forever; don't crash. The web\n CORS issue (above) is the most likely trigger.\n- **Deterministic offline fallback.** When `fetch` fails (airplane\n mode, network drop), the app renders **deterministic** placeholder\n content \u2014 same content per `userId + day` \u2014 never random. Real data\n swaps in when the network returns.\n- **Three-platform bundle gate.** `expo export --platform web`,\n `expo export --platform ios`, and `expo export --platform android`\n must all succeed. If any one fails, the build fails. No\n \"shipped iOS-only, web is broken\" \u2014 the rule is parity.\n- **Don't name a tab `settings.tsx`.** Use `account.tsx` or\n `preferences.tsx` instead. Expo Router's static web export generates\n `settings.html` correctly but does not resolve direct URL navigation\n to `/settings` \u2014 the client-side router shows an unmatched-route\n error while other tab names work fine. (Observed in dogfood; upstream\n behavior, not an Amba issue.)\n\n## Verification gate\n\nBefore declaring the build done, run every check in this list. Any\nfailure means the build is not done \u2014 fix and re-run.\n\n```bash\n# Type-check\npnpm tsc --noEmit\n\n# Three-platform export\npnpm expo export --platform web\npnpm expo export --platform ios\npnpm expo export --platform android\n```\n\nThen, from inside the agent (use the Amba MCP tools):\n\n- `amba_analytics_get` \u2192 at least one event tracked end-to-end\n through `Amba.events.track()` from the app.\n- `amba_users_list` \u2192 at least one user exists (the agent's own\n anonymous signin counts).\n- For every primitive the seed step created, call the matching\n `amba_*_list` and assert non-empty:\n - `amba_configs_list`\n - `amba_segments_list`\n - `amba_content_list_libraries`, `amba_content_list_items`,\n `amba_content_list_schedules`\n - `amba_streaks_list`\n - `amba_xp_rules_list`\n - `amba_achievements_list`\n - `amba_challenges_list`\n - `amba_leaderboards_list`\n - `amba_currencies_list` (if economy seeded)\n - `amba_catalog_list` (if catalog seeded)\n - `amba_collections_list` + `amba_admin_list_rows` per collection\n - `amba_push_list_campaigns`\n- Empty list for any seeded primitive \u2192 the implementation is fake\n (UI exists but never wrote to the backend). Failure.\n- Manually walk every route in the browser (`expo start --web`),\n screenshot each, and confirm:\n - Light theme renders cleanly.\n - Dark theme renders cleanly (toggle and re-screenshot every\n route).\n - Empty states render when collections are empty (fresh-install\n simulation: wipe AsyncStorage, reload).\n- `amba_users_reset_sandbox` to confirm you can recover from the MAU\n cap if you blew past 50 during testing.\n\nSkipping any primitive's seed step requires a one-line written\njustification in the gaps log (next section). \"We don't need\nstreaks\" is fine; silence is not.\n\n## Final output\n\nWhen done, write a final report to `BUILD_REPORT.md` in the project\nroot. Required sections:\n\n- **Start timestamp** (when the agent started).\n- **End timestamp** (when the verification gate last passed).\n- **MCP call inventory** \u2014 every `amba_*` tool you invoked, with a\n count. Lets a human reviewer audit \"did this agent actually use\n Amba for X\" at a glance.\n- **Primitive coverage table** \u2014 one row per primitive from the\n Feature \u2192 Amba primitive map. Mark each \u2705 (used), \u26A0\uFE0F (used with\n caveats \u2014 explain), or \u23ED (skipped \u2014 justify in one line).\n- **Gaps log** \u2014 every primitive you skipped, every feature you\n couldn't fit cleanly to an Amba primitive, every workaround. One\n line per gap, no marketing language.\n- **`seed-report.json`** \u2014 machine-readable seed summary:\n `{ \"primitive\": \"<name>\", \"created\": <count>, \"listed\": <count> }`\n for every primitive. The `listed` count comes from the\n `amba_*_list` call in the verification gate. `created ===\nlisted` for every row is the success condition.\n\nIf `BUILD_REPORT.md` is missing any required section, or\n`seed-report.json` is missing, the build is not done.\n";
|
|
32
32
|
/** Canonical URI for the MCP resource. */
|
|
33
33
|
export declare const EXPO_BUILD_PROMPT_URI = "amba://prompts/expo-build";
|
|
34
34
|
/** Canonical MIME type for the prompt body. */
|
|
@@ -93,7 +93,7 @@ export declare const AMBA_SETUP_SUB_RESOURCES: readonly [{
|
|
|
93
93
|
readonly name: "amba-setup-infrastructure";
|
|
94
94
|
readonly uri: "amba://setup/infrastructure";
|
|
95
95
|
readonly mime: "text/markdown";
|
|
96
|
-
readonly body: "# Infrastructure\n\nThe plumbing that sits behind every other surface: custom database tables (Collections — schema-first, per-tenant), serverless functions (run server-side code without standing up a backend), analytics (events + sessions), AI prompts (managed LLM templates, callable from the SDK with per-tenant keys), secrets, runtime configs, feature flags, third-party integrations (RevenueCat / Superwall / Resend / Stripe / etc.), media (file storage + CDN), and sites (static asset hosting at `*.app.amba.host`).\n\nIf gamification, economy, and social are the playable surface, **infrastructure is what you build a custom product on top of**. Anything that doesn't fit the canned surfaces lands here.\n\n## MCP tools\n\n### Collections (typed tables)\n\nA 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.\n\nAdmin 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`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `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 }` |\n| `amba_collections_list` | List collections in this project. | `{ project_id }` |\n| `amba_collections_get` | Read one collection's schema. | `{ project_id, name: \"todos\" }` |\n| `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: \"integer\", nullable: true } }` |\n| `amba_collections_delete` | Drop the table (destructive). `confirm` must equal the collection name. | `{ project_id, name: \"todos\", confirm: \"todos\" }` |\n| `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 } }` |\n| `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\" }` |\n| `amba_admin_list_rows` | Read rows as the developer. | `{ project_id, name: \"todos\", limit: 100 }` |\n| `amba_client_insert_row` | Insert as an end-user. Requires `api_key` (+ `session_token`). | `{ api_key, session_token, name: \"todos\", row: {...} }` |\n| `amba_client_list_rows` | Read as an end-user (auto user-scoped). | `{ api_key, session_token, name: \"todos\" }` |\n| `amba_client_get_row` | Get one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `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: {...} }` |\n| `amba_client_delete_row` | Soft-delete one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `amba_client_count_rows` | Count rows matching an optional `where`. | `{ api_key, session_token, name: \"todos\", where: {...} }` |\n| `amba_client_find_rows` | Filter / sort / paginate rows (SDK-shaped `filter`). | `{ api_key, session_token, name: \"todos\", filter: {...}, order: [\"created_at desc\"], limit: 50 }` |\n| `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 }` |\n\nColumn types: `text`, `integer`, `bigint`, `numeric`, `boolean`, `timestamptz`, `date`, `jsonb`, `uuid`, `vector(<dim>)` (e.g. `vector(1536)` for OpenAI embeddings), plus array forms `text[]`, `integer[]`, `bigint[]`, `numeric[]`, `boolean[]`, `uuid[]`. Use `integer` (not `int`), `numeric` (not `float`/`real`/`double`), and `jsonb` (not `json`) — the validator rejects the aliases.\n\n### Functions (serverless code)\n\nRun user code in a sandbox triggered by HTTP, cron, or webhook. The function gets the tenant connection automatically via injected env.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_functions_deploy` | Deploy a function from source. | `{ project_id, name: \"send_welcome_email\", runtime: \"node22\", source: \"export default async (req) => { ... }\", trigger: { type: \"http\" } }` |\n| `amba_functions_list` | List functions. | `{ project_id }` |\n| `amba_functions_get` | Read function metadata. | `{ project_id, function_id }` |\n| `amba_functions_get_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |\n| `amba_functions_delete` | Delete a function. | `{ project_id, function_id }` |\n| `amba_functions_schedule` | Attach a cron schedule. | `{ project_id, function_id, cron: \"0 9 * * *\", timezone: \"America/Los_Angeles\" }` |\n| `amba_functions_pause_schedule` | Pause a scheduled trigger without deleting it. | `{ project_id, function_id }` |\n| `amba_functions_resume_schedule` | Resume. | `{ project_id, function_id }` |\n| `amba_functions_trigger_schedule` | Fire a scheduled function ad-hoc (testing). | `{ project_id, function_id }` |\n\n### AI prompts\n\nManaged LLM templates: a stored prompt with provider + model + system message, invoked by name from the SDK. The actual LLM call is rewritten server-side per-tenant — the customer's provider API key (Anthropic / OpenAI / Mistral / Gemini) stays server-side, never on the device.\n\n**Two steps, in order:** first register the provider key with `amba_ai_providers_set`, then create prompts against it. A prompt registered before its provider has a key still saves, but invocations fail with `provider_not_configured` (424) until the key is set.\n\n> The provider key is **not** a function secret. `amba_secrets_set` writes function-scoped Worker secrets, which the AI gateway never reads. Provider keys live in a separate gateway-owned store and are set **only** via `amba_ai_providers_set`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_ai_providers_set` | Register / rotate the upstream provider API key. **Do this first.** | `{ project_id, provider: \"anthropic\", api_key: \"sk-ant-...\" }` |\n| `amba_ai_providers_list` | List registered providers (`configured` = key set). | `{ project_id }` |\n| `amba_ai_providers_delete` | Revoke a provider key (fails if prompts still reference it). | `{ project_id, provider: \"anthropic\" }` |\n| `amba_ai_prompts_create` | Create a prompt template. `client_invokable: true` lets the device SDK invoke it directly. | `{ project_id, name: \"summarize\", provider: \"anthropic\", model: \"claude-opus-4-5\", system_prompt: \"Summarize the user's text in 2 sentences.\", client_invokable: true }` |\n| `amba_ai_prompts_list` | List prompts. | `{ project_id }` |\n| `amba_ai_prompts_get` | Read one prompt. | `{ project_id, name }` |\n| `amba_ai_prompts_update` | Edit a prompt (replaces all fields; bumps version). | `{ project_id, name, provider, model, system_prompt: \"...\" }` |\n| `amba_ai_prompts_invoke` | Invoke a prompt server-side (admin testing). `messages` is a provider-shaped array. | `{ project_id, name, messages: [{ role: \"user\", content: \"...\" }] }` |\n| `amba_ai_prompts_delete` | Delete. | `{ project_id, name }` |\n\n### Analytics + events + sessions\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_analytics_get` | Top-level metrics dashboard (MAU, DAU, retention). | `{ project_id, period: \"7d\" }` |\n| `amba_events_list` | Browse raw events. | `{ project_id, limit: 100, since: \"2026-05-19T00:00:00Z\" }` |\n| `amba_events_count` | Count events matching a filter. | `{ project_id, event: \"workout_completed\", since: \"...\" }` |\n| `amba_sessions_list` | List user sessions. | `{ project_id, limit: 50 }` |\n| `amba_sessions_analytics` | Session-level metrics. | `{ project_id, period: \"7d\" }` |\n| `amba_users_list_events` | Per-user event history. | `{ project_id, user_id }` |\n| `amba_users_export` | Export the full user list. | `{ project_id, format: \"csv\" }` |\n\n### Secrets + configs + integrations\n\nSecrets here are **function-scoped** — they become environment bindings on your deployed functions. They are NOT where AI provider keys go (use `amba_ai_providers_set` for those — see AI prompts above).\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_secrets_set` | Set a function-scoped secret (encrypted at rest; bound on the next deploy). | `{ project_id, name: \"STRIPE_WEBHOOK_SECRET\", value: \"whsec_...\" }` |\n| `amba_secrets_get` | Read a secret (returns `\"<redacted>\"` unless explicitly requested). | `{ project_id, name }` |\n| `amba_secrets_list` | List secret names. | `{ project_id }` |\n| `amba_secrets_delete` | Delete. | `{ project_id, name }` |\n| `amba_configs_create` | Create a runtime config value (read from SDK as `Amba.config.fetch()`). | `{ project_id, key: \"primary_color\", value: \"#ff0066\", segment_id: null }` |\n| `amba_configs_list` | List configs. | `{ project_id }` |\n| `amba_configs_update` | Edit. | `{ project_id, config_id, value: \"...\" }` |\n| `amba_configs_delete` | Delete. | `{ project_id, config_id }` |\n| `amba_integrations_list` | List third-party integrations. | `{ project_id }` |\n| `amba_integrations_configure` | Configure a provider. | `{ project_id, provider: \"revenuecat\", config: { webhook_secret: \"...\", default_offering: \"...\" } }` |\n| `amba_integrations_set` | Set/replace integration config wholesale. | `{ project_id, provider, config }` |\n| `amba_integrations_patch` | Patch one field. | `{ project_id, provider, patch: { webhook_secret: \"...\" } }` |\n| `amba_integrations_test` | Send a test event to a configured provider. | `{ project_id, provider }` |\n\n### Media (file storage + CDN)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_media_upload` | Upload a file (returns a tenant-scoped URL). | `{ project_id, name: \"logo.png\", content_type: \"image/png\", data: \"<base64>\" }` |\n| `amba_media_list` | List files. | `{ project_id, folder: \"/\", limit: 100 }` |\n| `amba_media_delete` | Delete a file. | `{ project_id, file_id }` |\n| `amba_media_create_folder` | Create a logical folder. | `{ project_id, path: \"/uploads/avatars\" }` |\n| `amba_media_list_folders` | List folders. | `{ project_id }` |\n| `amba_media_delete_folder` | Delete a folder (must be empty). | `{ project_id, path }` |\n\n### Sites (static asset hosting)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_sites_deploy` | Deploy a static site bundle (zip / tar). | `{ project_id, name: \"marketing\", bundle: \"<base64>\", index: \"index.html\" }` |\n| `amba_sites_list` | List sites. | `{ project_id }` |\n| `amba_sites_get` | Read a site. | `{ project_id, site_id }` |\n| `amba_sites_add_domain` | Attach a custom domain. | `{ project_id, site_id, domain: \"marketing.example.com\" }` |\n| `amba_sites_list_domains` | List domains on a site. | `{ project_id, site_id }` |\n| `amba_sites_remove_domain` | Detach a domain. | `{ project_id, site_id, domain }` |\n| `amba_sites_delete` | Delete a site. | `{ project_id, site_id }` |\n\n## SDK init per stack\n\n`Amba.configure(...)` runs first. The infrastructure surfaces — collections, AI, config, flags, events — are SDK-side reads; the snippets below show what the client calls look like.\n\n### Expo / React Native\n\n```tsx\nimport { Amba } from '@layers/amba-expo';\n\n// Collections — typed table, user-scoped reads + writes\ntype Todo = { id: string; title: string; done: boolean; created_at: string };\n\nconst { data: todos } = await Amba.collections.find<Todo>('todos', {\n filter: Amba.collections.where.eq('done', false),\n order: [{ column: 'created_at', direction: 'desc' }],\n limit: 50,\n});\n\nconst newTodo = await Amba.collections.insert('todos', { title: 'Ship the app', done: false });\nawait Amba.collections.update('todos', newTodo.id, { done: true });\nawait Amba.collections.delete('todos', newTodo.id);\n\n// AI — call a managed prompt (prompt_slug names the registered prompt)\nconst response = await Amba.ai.anthropic.messages.create({\n prompt_slug: 'summarize',\n variables: { text: 'A long article about backend services …' },\n});\n\n// Track an analytics event\nawait Amba.events.track('button_clicked', { button: 'cta' });\n\n// Read runtime config\nconst config = await Amba.config.fetch();\n\n// Read a feature flag\nconst showBeta = await Amba.flags.get('beta_feature');\n\n// Diagnostics — wire-verify\nconst ping = await Amba.diagnostics.ping();\nif (!ping.ok) console.error('Amba misconfigured:', ping);\n```\n\n### Web\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nconst { data: todos } = await Amba.collections.find('todos', {\n filter: Amba.collections.where.eq('done', false),\n limit: 50,\n});\nawait Amba.collections.insert('todos', { title: 'Ship', done: false });\nawait Amba.events.track('page_view', { path: location.pathname });\n```\n\nWith `@layers/amba-react`:\n\n```tsx\nimport { useCollection, useFlag } from '@layers/amba-react';\n\nfunction TodoList() {\n const { data: todos, loading, refetch } = useCollection<{ id: string; title: string }>('todos');\n const showArchive = useFlag('archive_todos');\n if (loading) return <Spinner />;\n return (\n <ul>\n {todos?.map(t => <li key={t.id}>{t.title}</li>)}\n {showArchive && <ArchiveButton onArchive={refetch} />}\n </ul>\n );\n}\n```\n\n### iOS (Swift)\n\n```swift\nimport Amba\n\nstruct Todo: Codable {\n let id: String\n let title: String\n let done: Bool\n}\n\nlet response = try await Amba.collections.find(\"todos\", as: Todo.self)\n_ = try await Amba.collections.insert(\"todos\", row: [\"title\": \"Ship\", \"done\": false])\n\nlet config = try await Amba.config.fetch()\nlet showBeta = try await Amba.flags.get(name: \"beta_feature\")\ntry await Amba.events.track(\"app_opened\", properties: [\"source\": \"deep_link\"])\n\nlet reply = try await Amba.ai.anthropic.messages.create(\n request: AiMessageRequest(promptSlug: \"summarize\", variables: [\"text\": \"A long article...\"])\n)\n```\n\n### Android (Kotlin)\n\n```kotlin\ndata class Todo(val id: String, val title: String, val done: Boolean)\n\nval todos = Amba.collections.find<Todo>(\"todos\")\nAmba.collections.insert(\"todos\", mapOf(\"title\" to \"Ship\", \"done\" to false))\n\nval config = Amba.config.fetch()\nval showBeta = Amba.flags.get(\"beta_feature\")\nAmba.events.track(\"app_opened\", mapOf(\"source\" to \"deep_link\"))\n```\n\n### Flutter\n\n```dart\nimport 'package:amba/amba.dart';\n\nfinal response = await Amba.collections.find('todos', limit: 50);\nawait Amba.collections.insert('todos', {'title': 'Ship', 'done': false});\nfinal config = await Amba.config.fetch();\nfinal showBeta = await Amba.flags.get('beta_feature');\nawait Amba.events.track('app_opened', {'source': 'deep_link'});\n```\n\n## Common follow-ups\n\nBatch.\n\n1. **Custom data tables (collections):** any domain-specific tables to create?\n - Yes — I'll list them. (For each: name + columns + types.)\n - No, just use the canned Amba surfaces (auth, push, gamification, etc.)\n - Auto-create from the existing code's models — read `lib/models/`, `src/types/`, `Models/`, infer column lists, confirm with me.\n\n2. **Custom backend logic (functions):** any server-side code to deploy?\n - Yes — describe what it should do. (Then offer to scaffold a function template and deploy.)\n - No\n\n3. **AI features:** want managed LLM prompts?\n - Yes — what's the use case? (summarize, translate, classify, generate, custom)\n - No\n\n4. **Analytics:** which tracker do you want?\n - Only Amba's built-in events (recommended — already wired)\n - Amba + Mixpanel / PostHog / Segment forwarding (configure via `amba_integrations_configure`)\n - None (rarely useful — events drive XP / achievements / streaks; disabling cripples gamification)\n\n5. **Third-party integrations to set up:**\n - [ ] RevenueCat (IAP / subscriptions on iOS + Android)\n - [ ] Superwall (paywall A/B)\n - [ ] Resend (transactional email)\n - [ ] Stripe (web payments / subscriptions)\n - [ ] Mixpanel / PostHog / Segment (analytics forwarding)\n - [ ] OpenAI / Anthropic / Mistral / Gemini LLM keys (required for `Amba.ai.*` — set via `amba_ai_providers_set`, **not** `amba_integrations_configure`)\n\n6. **Feature flags:** seed any starter flags?\n - Yes — wire `beta_feature` (off by default) so I can ship the wiring before the feature exists\n - No\n\n7. **Static site:** want a marketing page hosted under your tenant subdomain?\n - Yes — scaffold and deploy a 1-page index\n - No\n\n## Re-run behavior\n\n1. Before creating:\n - `amba_collections_list` — match on `name`. Collisions: never silently recreate (data loss). Offer `amba_collections_alter` to add new columns instead.\n - `amba_functions_list` — match on `name`. Collisions: ask to redeploy (with the new source) or skip.\n - `amba_ai_prompts_list` — match on `name`. Same. (And `amba_ai_providers_list` — match on `provider`; re-running `amba_ai_providers_set` rotates the key in place.)\n - `amba_integrations_list` — match on `provider`. Same.\n - `amba_configs_list` — match on `key`. Same.\n\n2. **Never call `amba_collections_delete` on re-run unless the user explicitly asks** — this drops the underlying table and every row in it across every user of the tenant.\n\n3. For functions: re-deploying replaces source in place (versioned server-side). It's safe to call `amba_functions_deploy` with the same name + new source.\n\n4. For integrations: if a provider is already configured, prefer `amba_integrations_patch` (partial update) over `amba_integrations_set` (full replace).\n\n5. Secrets: don't list secret values in chat output, even on read. Just confirm \"OPENAI_API_KEY is set\" / \"not set\".\n";
|
|
96
|
+
readonly body: "# Infrastructure\n\nThe plumbing that sits behind every other surface: relational Postgres tables (Collections — schema-first, per-tenant), serverless functions (run server-side code without standing up a backend), analytics (events + sessions), AI prompts (managed LLM templates, callable from the SDK with per-tenant keys), secrets, runtime configs, feature flags, third-party integrations (RevenueCat / Superwall / Stripe / push credentials), media (file storage + CDN), and sites (static asset hosting at `*.app.amba.host`).\n\nIf gamification, economy, and social are the playable surface, **infrastructure is what you build a custom product on top of**. Anything that doesn't fit the canned surfaces lands here.\n\n## MCP tools\n\n### Collections (relational Postgres tables)\n\nA collection is a relational Postgres table inside the project's isolated tenant database — typed columns, foreign keys, transactions, unique indexes, and vector search. 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.\n\nAdmin 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`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `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 }, { name: \"due_at\", type: \"timestamptz\", nullable: true }], shared: false }` |\n| `amba_collections_list` | List collections in this project. | `{ project_id }` |\n| `amba_collections_get` | Read one collection's schema. | `{ project_id, name: \"todos\" }` |\n| `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: \"integer\", nullable: true } }` |\n| `amba_collections_delete` | Drop the table (destructive). `confirm` must equal the collection name. | `{ project_id, name: \"todos\", confirm: \"todos\" }` |\n| `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 } }` |\n| `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\" }` |\n| `amba_admin_list_rows` | Read rows as the developer. | `{ project_id, name: \"todos\", limit: 100 }` |\n| `amba_client_insert_row` | Insert as an end-user. Requires `api_key` (+ `session_token`). | `{ api_key, session_token, name: \"todos\", row: {...} }` |\n| `amba_client_list_rows` | Read as an end-user (auto user-scoped). | `{ api_key, session_token, name: \"todos\" }` |\n| `amba_client_get_row` | Get one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `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: {...} }` |\n| `amba_client_delete_row` | Soft-delete one row by id (end-user). | `{ api_key, session_token, name: \"todos\", id }` |\n| `amba_client_count_rows` | Count rows matching an optional `where`. | `{ api_key, session_token, name: \"todos\", where: {...} }` |\n| `amba_client_find_rows` | Filter / sort / paginate rows (SDK-shaped `filter`). | `{ api_key, session_token, name: \"todos\", filter: {...}, order: [\"created_at desc\"], limit: 50 }` |\n| `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 }` |\n\nColumn types: `text`, `integer`, `bigint`, `numeric`, `boolean`, `timestamptz`, `date`, `jsonb`, `uuid`, `vector` (pass a separate `dimension` field, e.g. `{ name: \"embedding\", type: \"vector\", dimension: 1536 }` for OpenAI embeddings), plus array forms `text[]`, `integer[]`, `bigint[]`, `numeric[]`, `boolean[]`, `uuid[]`. Columns are NOT NULL unless `nullable: true`; column defaults are not supported (set values at insert time). Use `integer` (not `int`), `numeric` (not `float`/`real`/`double`), and `jsonb` (not `json`) — the validator rejects the aliases.\n\n### Functions (serverless code)\n\nRun user code in a sandbox triggered by HTTP, cron, or webhook. The function gets the tenant connection automatically via injected env.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_functions_deploy` | Deploy a function from source. | `{ project_id, name: \"send_welcome_email\", runtime: \"node22\", source: \"export default async (req) => { ... }\", trigger: { type: \"http\" } }` |\n| `amba_functions_list` | List functions. | `{ project_id }` |\n| `amba_functions_get` | Read function metadata. | `{ project_id, function_id }` |\n| `amba_functions_get_logs` | Recent invocation logs. | `{ project_id, function_id, limit: 100 }` |\n| `amba_functions_delete` | Delete a function. | `{ project_id, function_id }` |\n| `amba_functions_schedule` | Attach a cron schedule. | `{ project_id, function_id, cron: \"0 9 * * *\", timezone: \"America/Los_Angeles\" }` |\n| `amba_functions_pause_schedule` | Pause a scheduled trigger without deleting it. | `{ project_id, function_id }` |\n| `amba_functions_resume_schedule` | Resume. | `{ project_id, function_id }` |\n| `amba_functions_trigger_schedule` | Fire a scheduled function ad-hoc (testing). | `{ project_id, function_id }` |\n| `amba_function_domains_attach` | Attach one exact hostname to one function; returns DNS validation instructions. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n| `amba_function_domains_list` | List provider-neutral hostname, ownership, and certificate status. | `{ project_id, name: \"feed\" }` |\n| `amba_function_domains_refresh` | Re-poll DNS ownership and certificate state. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n| `amba_function_domains_remove` | Detach an exact function hostname. | `{ project_id, name: \"feed\", hostname: \"feeds.example.com\" }` |\n\nFunction-domain routing preserves the complete incoming path and query string.\nIt is exact-host only (no wildcard/path rewrite and no automatic `www` for a\nsubdomain). Podcast/feed clients cannot attach an Amba API key, so their\nfunction must be deployed with `public: true` and validate any private token\ninside the handler. Effective-tier caps are free 1, pro 5, scale 20, and\nenterprise/comped 50; attach is limited to five attempts per project per hour.\nUnverified claims become eligible for reclaim after 24 hours, and routing\nresources are allocated only after ownership is active.\n\n### AI prompts\n\nManaged LLM templates: a stored prompt with provider + model + system message, invoked by name from the SDK. The actual LLM call is rewritten server-side per-tenant — the customer's provider API key (Anthropic / OpenAI / Mistral / Gemini) stays server-side, never on the device.\n\n**Two steps, in order:** first register the provider key with `amba_ai_providers_set`, then create prompts against it. A prompt registered before its provider has a key still saves, but invocations fail with `provider_not_configured` (424) until the key is set.\n\n> The provider key is **not** a function secret. `amba_secrets_set` writes function-scoped Worker secrets, which the AI gateway never reads. Provider keys live in a separate gateway-owned store and are set **only** via `amba_ai_providers_set`.\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_ai_providers_set` | Register / rotate the upstream provider API key. **Do this first.** | `{ project_id, provider: \"anthropic\", api_key: \"sk-ant-...\" }` |\n| `amba_ai_providers_list` | List registered providers (`configured` = key set). | `{ project_id }` |\n| `amba_ai_providers_delete` | Revoke a provider key (fails if prompts still reference it). | `{ project_id, provider: \"anthropic\" }` |\n| `amba_ai_prompts_create` | Create a prompt template. `client_invokable: true` lets the device SDK invoke it directly. | `{ project_id, name: \"summarize\", provider: \"anthropic\", model: \"claude-opus-4-5\", system_prompt: \"Summarize the user's text in 2 sentences.\", client_invokable: true }` |\n| `amba_ai_prompts_list` | List prompts. | `{ project_id }` |\n| `amba_ai_prompts_get` | Read one prompt. | `{ project_id, name }` |\n| `amba_ai_prompts_update` | Edit a prompt (replaces all fields; bumps version). | `{ project_id, name, provider, model, system_prompt: \"...\" }` |\n| `amba_ai_prompts_invoke` | Invoke by name server-side (admin testing; works with `client_invokable: false`). Uses the named gateway path, so the prompt budget, rate limit, token cap, and spend attribution are enforced. | `{ project_id, name, messages: [{ role: \"user\", content: \"...\" }] }` |\n| `amba_ai_prompts_delete` | Delete. | `{ project_id, name }` |\n\n### Analytics + events + sessions\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_analytics_get` | Top-level metrics dashboard (MAU, DAU, retention). | `{ project_id, period: \"7d\" }` |\n| `amba_events_list` | Browse raw events. | `{ project_id, limit: 100, since: \"2026-05-19T00:00:00Z\" }` |\n| `amba_events_count` | Count events matching a filter. | `{ project_id, event: \"workout_completed\", since: \"...\" }` |\n| `amba_sessions_list` | List user sessions. | `{ project_id, limit: 50 }` |\n| `amba_sessions_analytics` | Session-level metrics. | `{ project_id, period: \"7d\" }` |\n| `amba_users_list_events` | Per-user event history. | `{ project_id, user_id }` |\n| `amba_users_export` | Export the full user list. | `{ project_id, format: \"csv\" }` |\n\n### Secrets + configs + integrations\n\nSecrets here become environment bindings on deployed functions. Omit `function`\nfor a project-wide secret or pass it to scope the value to one function. Setting\nor rotating a secret queues an asynchronous update for already-deployed\nfunctions; later deployments reconcile the binding too. They are NOT where AI\nprovider keys go (use `amba_ai_providers_set` for those — see AI prompts above).\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_secrets_set` | Set or rotate a function secret; omit `function` for project-wide scope or pass it for one function. Already-deployed functions receive it asynchronously. | `{ project_id, name: \"STRIPE_WEBHOOK_SECRET\", value: \"whsec_...\" }` |\n| `amba_secrets_get` | Read a secret (returns `\"<redacted>\"` unless explicitly requested). | `{ project_id, name }` |\n| `amba_secrets_list` | List secret names. | `{ project_id }` |\n| `amba_secrets_delete` | Delete. | `{ project_id, name }` |\n| `amba_configs_create` | Create a runtime config value (read from SDK as `Amba.config.fetch()`). | `{ project_id, key: \"primary_color\", value: \"#ff0066\", segment_id: null }` |\n| `amba_configs_list` | List configs. | `{ project_id }` |\n| `amba_configs_update` | Edit. | `{ project_id, config_id, value: \"...\" }` |\n| `amba_configs_delete` | Delete. | `{ project_id, config_id }` |\n| `amba_integrations_list` | List third-party integrations. | `{ project_id }` |\n| `amba_integrations_configure` | Configure a provider. | `{ project_id, provider: \"revenuecat\", config: { webhook_secret: \"...\", default_offering: \"...\" } }` |\n| `amba_integrations_set` | Set/replace integration config wholesale. | `{ project_id, provider, config }` |\n| `amba_integrations_patch` | Patch one field. | `{ project_id, provider, patch: { webhook_secret: \"...\" } }` |\n| `amba_integrations_test` | Send a test event to a configured provider. | `{ project_id, provider }` |\n\n### Media (file storage + CDN)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_media_upload` | Upload a file (returns a tenant-scoped URL). | `{ project_id, name: \"logo.png\", content_type: \"image/png\", data: \"<base64>\" }` |\n| `amba_media_list` | List files. | `{ project_id, folder: \"/\", limit: 100 }` |\n| `amba_media_delete` | Delete a file. | `{ project_id, file_id }` |\n| `amba_media_create_folder` | Create a logical folder. | `{ project_id, path: \"/uploads/avatars\" }` |\n| `amba_media_list_folders` | List folders. | `{ project_id }` |\n| `amba_media_delete_folder` | Delete a folder (must be empty). | `{ project_id, path }` |\n\n### Sites (static asset hosting)\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_sites_deploy` | Deploy a static site bundle (zip / tar). | `{ project_id, name: \"marketing\", bundle: \"<base64>\", index: \"index.html\" }` |\n| `amba_sites_list` | List sites. | `{ project_id }` |\n| `amba_sites_get` | Read a site. | `{ project_id, site_id }` |\n| `amba_sites_add_domain` | Attach a custom domain. | `{ project_id, site_id, domain: \"marketing.example.com\" }` |\n| `amba_sites_list_domains` | List domains on a site. | `{ project_id, site_id }` |\n| `amba_sites_remove_domain` | Detach a domain. | `{ project_id, site_id, domain }` |\n| `amba_sites_delete` | Delete a site. | `{ project_id, site_id }` |\n\n### Purchased domains + email forwarding\n\n| Tool | Purpose | Example args |\n| --- | --- | --- |\n| `amba_domains_search` | Search available domains (free). | `{ project_id, query: \"myapp\" }` |\n| `amba_domains_check` | Check authoritative price + availability. | `{ project_id, domains: [\"myapp.com\"] }` |\n| `amba_domains_purchase` | Quote, then confirm, a domain purchase. | `{ project_id, domain: \"myapp.com\", site: \"marketing\" }` |\n| `amba_domains_list` | List purchased domains. | `{ project_id }` |\n| `amba_domains_email_enable` | Enable inbound routing when no MX conflict exists. | `{ project_id, domain: \"myapp.com\" }` |\n| `amba_domains_email_destinations_add` | Add a destination mailbox; returns action-required until verified. | `{ project_id, domain: \"myapp.com\", email: \"owner@example.net\" }` |\n| `amba_domains_email_destinations_get` | Poll destination verification. | `{ project_id, domain: \"myapp.com\", destination_id }` |\n| `amba_domains_email_forwards_set` | Create/update a literal forward. | `{ project_id, domain: \"myapp.com\", source: \"support\", destination: \"owner@example.net\" }` |\n| `amba_domains_email_forwards_list` | List literal forwards. | `{ project_id, domain: \"myapp.com\" }` |\n| `amba_domains_email_forwards_delete` | Delete a literal forward. | `{ project_id, domain: \"myapp.com\", forward_id }` |\n| `amba_domains_email_catch_all_set` | Enable/update/disable catch-all. | `{ project_id, domain: \"myapp.com\", enabled: true, destination: \"owner@example.net\" }` |\n| `amba_domains_email_catch_all_get` | Read catch-all state. | `{ project_id, domain: \"myapp.com\" }` |\n\n## SDK init per stack\n\n`Amba.configure(...)` runs first. The infrastructure surfaces — collections, AI, config, flags, events — are SDK-side reads; the snippets below show what the client calls look like.\n\n### Expo / React Native\n\n```tsx\nimport { Amba } from '@layers/amba-expo';\n\n// Collections — typed table, user-scoped reads + writes\ntype Todo = { id: string; title: string; done: boolean; created_at: string };\n\nconst { data: todos } = await Amba.collections.find<Todo>('todos', {\n filter: Amba.collections.where.eq('done', false),\n order: [{ column: 'created_at', direction: 'desc' }],\n limit: 50,\n});\n\nconst newTodo = await Amba.collections.insert('todos', { title: 'Ship the app', done: false });\nawait Amba.collections.update('todos', newTodo.id, { done: true });\nawait Amba.collections.delete('todos', newTodo.id);\n\n// AI — call a managed prompt (prompt_slug names the registered prompt)\nconst response = await Amba.ai.anthropic.messages.create({\n prompt_slug: 'summarize',\n variables: { text: 'A long article about backend services …' },\n});\n\n// Track an analytics event\nawait Amba.events.track('button_clicked', { button: 'cta' });\n\n// Read runtime config\nconst config = await Amba.config.fetch();\n\n// Read a feature flag\nconst showBeta = await Amba.flags.get('beta_feature');\n\n// Diagnostics — wire-verify\nconst ping = await Amba.diagnostics.ping();\nif (!ping.ok) console.error('Amba misconfigured:', ping);\n```\n\n### Web\n\n```ts\nimport { Amba } from '@layers/amba-web';\n\nconst { data: todos } = await Amba.collections.find('todos', {\n filter: Amba.collections.where.eq('done', false),\n limit: 50,\n});\nawait Amba.collections.insert('todos', { title: 'Ship', done: false });\nawait Amba.events.track('page_view', { path: location.pathname });\n```\n\nWith `@layers/amba-react`:\n\n```tsx\nimport { useCollection, useFlag } from '@layers/amba-react';\n\nfunction TodoList() {\n const { data: todos, loading, refetch } = useCollection<{ id: string; title: string }>('todos');\n const showArchive = useFlag('archive_todos');\n if (loading) return <Spinner />;\n return (\n <ul>\n {todos?.map(t => <li key={t.id}>{t.title}</li>)}\n {showArchive && <ArchiveButton onArchive={refetch} />}\n </ul>\n );\n}\n```\n\n### iOS (Swift)\n\n```swift\nimport Amba\n\nstruct Todo: Codable {\n let id: String\n let title: String\n let done: Bool\n}\n\nlet response = try await Amba.collections.find(\"todos\", as: Todo.self)\n_ = try await Amba.collections.insert(\"todos\", row: [\"title\": \"Ship\", \"done\": false])\n\nlet config = try await Amba.config.fetch()\nlet showBeta = try await Amba.flags.get(name: \"beta_feature\")\ntry await Amba.events.track(\"app_opened\", properties: [\"source\": \"deep_link\"])\n\nlet reply = try await Amba.ai.anthropic.messages.create(\n request: AiMessageRequest(promptSlug: \"summarize\", variables: [\"text\": \"A long article...\"])\n)\n```\n\n### Android (Kotlin)\n\n```kotlin\ndata class Todo(val id: String, val title: String, val done: Boolean)\n\nval todos = Amba.collections.find<Todo>(\"todos\")\nAmba.collections.insert(\"todos\", mapOf(\"title\" to \"Ship\", \"done\" to false))\n\nval config = Amba.config.fetch()\nval showBeta = Amba.flags.get(\"beta_feature\")\nAmba.events.track(\"app_opened\", mapOf(\"source\" to \"deep_link\"))\n```\n\n### Flutter\n\n```dart\nimport 'package:amba/amba.dart';\n\nfinal response = await Amba.collections.find('todos', limit: 50);\nawait Amba.collections.insert('todos', {'title': 'Ship', 'done': false});\nfinal config = await Amba.config.fetch();\nfinal showBeta = await Amba.flags.get('beta_feature');\nawait Amba.events.track('app_opened', {'source': 'deep_link'});\n```\n\n## Common follow-ups\n\nBatch.\n\n1. **Custom data tables (collections):** any domain-specific tables to create?\n - Yes — I'll list them. (For each: name + columns + types.)\n - No, just use the canned Amba surfaces (auth, push, gamification, etc.)\n - Auto-create from the existing code's models — read `lib/models/`, `src/types/`, `Models/`, infer column lists, confirm with me.\n\n2. **Custom backend logic (functions):** any server-side code to deploy?\n - Yes — describe what it should do. (Then offer to scaffold a function template and deploy.)\n - No\n\n3. **AI features:** want managed LLM prompts?\n - Yes — what's the use case? (summarize, translate, classify, generate, custom)\n - No\n\n4. **Analytics:** which tracker do you want?\n - Only Amba's built-in events (recommended — already wired)\n - Amba + your own analytics pipeline (subscribe a webhook to project events via `amba_webhooks_create` and forward server-side)\n - None (rarely useful — events drive XP / achievements / streaks; disabling cripples gamification)\n\n5. **Third-party integrations to set up:**\n - [ ] RevenueCat (IAP / subscriptions on iOS + Android)\n - [ ] Superwall (paywall A/B)\n - [ ] Stripe Billing (web subscriptions through the app's own Stripe account — provider `stripe_billing`)\n - [ ] OpenAI / Anthropic / Mistral / Gemini LLM keys (required for `Amba.ai.*` — set via `amba_ai_providers_set`, **not** `amba_integrations_configure`)\n\n6. **Feature flags:** seed any starter flags?\n - Yes — wire `beta_feature` (off by default) so I can ship the wiring before the feature exists\n - No\n\n7. **Static site:** want a marketing page hosted under your tenant subdomain?\n - Yes — scaffold and deploy a 1-page index\n - No\n\n## Re-run behavior\n\n1. Before creating:\n - `amba_collections_list` — match on `name`. Collisions: never silently recreate (data loss). Offer `amba_collections_alter` to add new columns instead.\n - `amba_functions_list` — match on `name`. Collisions: ask to redeploy (with the new source) or skip.\n - `amba_ai_prompts_list` — match on `name`. Same. (And `amba_ai_providers_list` — match on `provider`; re-running `amba_ai_providers_set` rotates the key in place.)\n - `amba_integrations_list` — match on `provider`. Same.\n - `amba_configs_list` — match on `key`. Same.\n\n2. **Never call `amba_collections_delete` on re-run unless the user explicitly asks** — this drops the underlying table and every row in it across every user of the tenant.\n\n3. For functions: re-deploying replaces source in place (versioned server-side). It's safe to call `amba_functions_deploy` with the same name + new source.\n\n4. For integrations: if a provider is already configured, prefer `amba_integrations_patch` (partial update) over `amba_integrations_set` (full replace).\n\n5. Secrets: don't list secret values in chat output, even on read. Just confirm \"OPENAI_API_KEY is set\" / \"not set\".\n";
|
|
97
97
|
readonly surface: "infrastructure";
|
|
98
98
|
readonly title: "Amba setup — infrastructure";
|
|
99
99
|
readonly description: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Affiliate Programs tools (migration 052). Two personas, structurally scoped:
|
|
5
|
+
* - `amba_affiliate_*` — the program owner (admin); operates over the whole
|
|
6
|
+
* program. PAT must manage the program's owner org.
|
|
7
|
+
* - `amba_affiliate_my_*` — the affiliate (self-service); derives the
|
|
8
|
+
* affiliate from the PAT's own org(s) and takes NO other-affiliate id, so
|
|
9
|
+
* an affiliate cannot read a peer's data.
|
|
10
|
+
* Zero-to-enrolled bootstrap is `amba_affiliate_signup` (public, in auth.ts).
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerTools(server: McpServer, apiClient: ApiClient): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amba Agentic Payments — agent-checkout developer tools
|
|
3
|
+
* (tracker: agentic-payments).
|
|
4
|
+
*
|
|
5
|
+
* The neutral agent-checkout primitive lets an app accept payments INITIATED
|
|
6
|
+
* BY AN AI AGENT — and, with the entitlement cascade, pay-the-agent →
|
|
7
|
+
* unlock-the-thing in one motion. These are the DEVELOPER-facing config tools
|
|
8
|
+
* (enable the surface, inspect which settlement methods a project can offer);
|
|
9
|
+
* the runtime checkout is minted by the SDK / the per-app `pay` tool.
|
|
10
|
+
*
|
|
11
|
+
* Backend routes: POST/GET /v1/admin/projects/:id/agent-checkout/*. Settlement
|
|
12
|
+
* is gated by the platform's AGENT_CHECKOUT_LIVE kill-switch (Phase 2+);
|
|
13
|
+
* enabling a project does not move money.
|
|
14
|
+
*/
|
|
15
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
+
import type { ApiClient } from '../api-client.js';
|
|
17
|
+
export declare function registerTools(server: McpServer, apiClient: ApiClient): void;
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
*
|
|
16
16
|
* `amba_ai_prompts_invoke` is for testing. It looks up the registered
|
|
17
17
|
* prompt's provider + model + system_prompt, then routes the request
|
|
18
|
-
* through the admin
|
|
19
|
-
* identity
|
|
18
|
+
* through the admin named-prompt invoke endpoint. That endpoint preserves
|
|
19
|
+
* the developer's identity and, unlike the raw `/messages` escape hatch,
|
|
20
|
+
* applies the prompt's budget/rate/usage gates. The agent provides the
|
|
20
21
|
* `messages` array (Anthropic/OpenAI shape) explicitly so this stays
|
|
21
22
|
* a thin pass-through and doesn't try to guess the right Messages
|
|
22
23
|
* API shape per provider.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App-MCP (per-app agent surface) configuration tools — auto-MCP Phase 2,
|
|
3
|
+
* slices E + H.
|
|
4
|
+
*
|
|
5
|
+
* Every Amba app has its own agent surface: an MCP endpoint whose tools
|
|
6
|
+
* ARE the app's collections and deployed functions. These platform tools
|
|
7
|
+
* let a building agent configure that surface without leaving the agentic
|
|
8
|
+
* flow:
|
|
9
|
+
*
|
|
10
|
+
* amba_app_mcp_get_config GET /projects/:p/app-mcp/config
|
|
11
|
+
* amba_app_mcp_update_config PATCH /projects/:p/app-mcp/config
|
|
12
|
+
* amba_app_mcp_set_exposure PUT /projects/:p/app-mcp/exposure
|
|
13
|
+
*
|
|
14
|
+
* Exposure is an EXPLICIT allowlist (default: nothing is exposed). The
|
|
15
|
+
* natural flow after creating a collection or deploying a function is to
|
|
16
|
+
* expose it here — one call makes it a typed agent tool for the app's own
|
|
17
|
+
* users and any agent the developer authorizes. Descriptions stay
|
|
18
|
+
* provider-neutral.
|
|
19
|
+
*/
|
|
20
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
21
|
+
import type { ApiClient } from '../api-client.js';
|
|
22
|
+
export declare function registerTools(server: McpServer, apiClient: ApiClient): void;
|
package/dist/tools/domains.d.ts
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* re-call with `confirm: true` + the quoted
|
|
15
15
|
* `accept_price_usd` to execute.
|
|
16
16
|
* - `amba_domains_list` — list domains this project has purchased.
|
|
17
|
+
* - `amba_domains_email_*` — enable inbound routing, verify destinations,
|
|
18
|
+
* and manage literal/catch-all forwards.
|
|
17
19
|
*
|
|
18
20
|
* Money safety: `amba_domains_purchase` never charges on the first call. It
|
|
19
21
|
* returns the price and `confirmation_required: true`; the agent must
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Organization tools (migration 049). Orgs are the ownership + payment-entity
|
|
5
|
+
* boundary; affiliates and app-factory child projects are all orgs. These wrap
|
|
6
|
+
* the `/v1/admin/orgs*` and `/v1/admin/provision` routes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerTools(server: McpServer, apiClient: ApiClient): void;
|
package/dist/tools/payments.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - amba_payments_account_status — onboarding + capability flags
|
|
15
15
|
* - amba_payments_set_fee — set the default platform fee (basis points)
|
|
16
16
|
* - amba_payments_charge — destination charge with application fee
|
|
17
|
+
* - amba_payments_refund — refund a charge (full or partial)
|
|
17
18
|
* - amba_payments_balance — connected-account balance
|
|
18
19
|
* - amba_payments_payouts — connected-account payouts
|
|
19
20
|
*
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
* applies that bundle to the prod project.
|
|
13
13
|
*
|
|
14
14
|
* The bundle is configuration only — no secrets, no per-user data, no
|
|
15
|
-
* end-user rows.
|
|
15
|
+
* end-user rows. Integrations export their non-secret config with
|
|
16
|
+
* credentials stripped; on import they land as `pending_credentials`, or —
|
|
17
|
+
* with `include_integration_credentials: true` and a same-developer source —
|
|
18
|
+
* the stored credentials are copied server-side and the integrations
|
|
19
|
+
* activate. Import is idempotent: `skip_existing` (default) no-ops on a
|
|
16
20
|
* name/key/code conflict; `merge` refreshes the matching definition. Safe to
|
|
17
21
|
* run twice.
|
|
18
22
|
*
|
package/dist/tools/secrets.d.ts
CHANGED
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
* - Function name: /^[a-z][a-z0-9_-]{0,57}$/ (matches the function
|
|
15
15
|
* deploy validator one-for-one).
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* 400 RESERVED_BINDING. Don't try to shadow them.
|
|
17
|
+
* The complete AMBA_ and EDGE_ namespaces, plus the exact names STORAGE
|
|
18
|
+
* and EDGE_DB_PROXY, are platform-reserved. MCP rejects these before an
|
|
19
|
+
* API/provider call; the API independently enforces the same shared rule.
|
|
21
20
|
*
|
|
22
21
|
* Plaintext values are NEVER returned. `amba_secrets_list` returns
|
|
23
22
|
* name + version + sync_status (pending/syncing/synced/failed) only.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Service-account lifecycle tools for Fork C delegated operator tokens.
|
|
5
|
+
*
|
|
6
|
+
* These tools intentionally cover only SA lifecycle. Delegated token minting is
|
|
7
|
+
* an HTTP backend-to-backend call made by the partner app with the `amb_dsvc_`
|
|
8
|
+
* secret; it is not exposed as an MCP tool.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerTools(server: McpServer, apiClient: ApiClient): void;
|