@genlobe/mcp-server 3.7.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +168 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3731,18 +3731,44 @@ End-to-end recipe to wire a chatbot agent that answers from a knowledge base
3731
3731
  populated by a snapshot of your custom-entity catalog (typical "store
3732
3732
  assistant" / "product Q&A" / "support deflection" use case).
3733
3733
 
3734
- ## Auth context clarification (frequent confusion)
3734
+ ## Public chatbots: use service-mode (ADR-0013) — the simple path
3735
3735
 
3736
- The end-user chat endpoint \`POST /v1/user/agents/{agent_id}/chat\` requires
3737
- an end-user JWT. **There are two legitimate ways to obtain that JWT**, and a
3738
- public-facing bot needs option (b):
3736
+ For a **public chatbot** (anonymous visitors, no human login), call the
3737
+ service-mode endpoint with your \`sk_live_*\` ONLY no end-user JWT, no bot
3738
+ user, no email verification:
3739
+
3740
+ \`\`\`http
3741
+ POST /v1/server/agents/{agent_id}/chat
3742
+ X-API-Key: <your sk_live_*> ← server-side only
3743
+ X-Organization-Id: <org_id> (or organization_id in the body)
3744
+ \`\`\`
3745
+
3746
+ Body:
3747
+ \`\`\`json
3748
+ { "messages": [{"role":"user","content":"do you have rice?"}], "session_id": "anon-visitor-abc" }
3749
+ \`\`\`
3750
+
3751
+ Usage is billed to the Tenant + Organization; \`user_id\` on the execution is
3752
+ NULL (no human). \`session_id\` (optional) groups one anonymous conversation.
3753
+ This is the **recommended path** for storefront/support bots — it removes the
3754
+ old bot-user + email-verification wall entirely.
3755
+
3756
+ Your Next.js \`/api/chat\` route handler calls this server-side; the browser
3757
+ only talks to \`/api/chat\`, never to Genlobe directly.
3758
+
3759
+ ## Per-user chat (when there IS a logged-in human)
3760
+
3761
+ If the chat belongs to an authenticated end-user (e.g. an in-app assistant),
3762
+ use \`POST /v1/user/agents/{agent_id}/chat\` with that user's JWT instead — it
3763
+ attributes usage per user and applies their plan limits. Two ways to get the
3764
+ JWT:
3739
3765
 
3740
3766
  - **(a) Real human end-user**: customer signs up via \`POST /v1/auth/register\`,
3741
3767
  logs in, and chats with the agent. The JWT is theirs.
3742
- - **(b) Bot service account**: register **one real bot user** for the chat
3743
- surface (use a real email you control, e.g. \`bot@yourshop.com\`), keep its
3744
- credentials in your server env, log in server-side, attach the resulting
3745
- JWT to incoming chat requests. This is what "register a bot user" means.
3768
+ - **(b) Bot service account (legacy fallback)**: register **one real bot user**
3769
+ (real email you control), log in server-side, attach the JWT. Only needed if
3770
+ you specifically want per-"user" attribution for the bot. For public bots,
3771
+ prefer service-mode above it is simpler.
3746
3772
 
3747
3773
  **What is NOT allowed** (and what some agents mis-read as "no LLM bot is
3748
3774
  possible"): inventing/synthesizing fake email addresses to bulk-create
@@ -4299,6 +4325,87 @@ ${s.notes ? `## Notes\n\n${s.notes}\n` : ""}
4299
4325
  - For the auth shape (sk_live_* / pk_live_* + cookies), call \`get_authentication_flow()\` and \`get_security_guide()\`.`;
4300
4326
  }
4301
4327
  // =============================================================================
4328
+ // Supabase → Genlobe migration recipe (G10 from the DX audit).
4329
+ // =============================================================================
4330
+ function SUPABASE_MIGRATION_RECIPE() {
4331
+ return `# Supabase → Genlobe migration recipe
4332
+
4333
+ Move a Supabase project's data into Genlobe. The tricky part is **users +
4334
+ foreign keys**; data tables are the easy part (bulk insert). Do it in this
4335
+ order so foreign keys never dangle.
4336
+
4337
+ ## Mental model (ADR-0012)
4338
+
4339
+ - Genlobe \`users\` (native, auth) ← Supabase \`auth.users\`. Auth stays in the
4340
+ native table; do NOT model users as a custom entity.
4341
+ - Genlobe custom entities ← Supabase \`public.*\` data tables.
4342
+ - One Genlobe **Organization = one namespace** (≈ a Supabase project). Your
4343
+ app's *customers* are rows in a custom entity, NOT Organizations.
4344
+
4345
+ ## Step 1 — Import users FIRST (preserve UUIDs)
4346
+
4347
+ \`\`\`http
4348
+ POST /v1/server/users/import (sk_live_* — server/agent callable)
4349
+ \`\`\`
4350
+ \`\`\`json
4351
+ {
4352
+ "organization_id": "<target org>",
4353
+ "users": [
4354
+ {
4355
+ "id": "<the Supabase auth.users.id — PRESERVE IT>",
4356
+ "email": "ada@shop.com",
4357
+ "password_hash": "<Supabase encrypted_password — bcrypt, imported as-is>",
4358
+ "profile_data": { "...": "from raw_user_meta_data" },
4359
+ "is_email_verified": true,
4360
+ "status": "active"
4361
+ }
4362
+ ]
4363
+ }
4364
+ \`\`\`
4365
+
4366
+ - **Preserve \`id\`**: pass the Supabase UUID so every foreign key that pointed
4367
+ at it stays valid. This is the single most important step.
4368
+ - **\`password_hash\`**: Supabase and Genlobe both use bcrypt, so the hash is
4369
+ imported as-is — users keep their password, NO reset.
4370
+ - Capped at 500 per call, all-or-nothing. Chunk larger sets.
4371
+
4372
+ ## Step 2 — Create the entity schemas for the data tables
4373
+
4374
+ For each Supabase \`public.<table>\`, define a custom entity schema. Use
4375
+ \`get_entity_schema_recipe\` for common shapes. Map a column that was a FK to
4376
+ \`auth.users\` as a \`reference\` field with \`target_schema_slug: "users"\`
4377
+ (ADR-0009) — it validates against the native users table.
4378
+
4379
+ \`POST /v1/entity/schemas\` (one per table). Valid field types: string, text,
4380
+ integer, float, boolean, datetime, enum, email, url, phone, reference.
4381
+ (No \`array\`, no \`number\` — see get_entity_schema_recipe.)
4382
+
4383
+ ## Step 3 — Bulk-insert the rows
4384
+
4385
+ \`\`\`http
4386
+ POST /v1/entity/records/bulk
4387
+ \`\`\`
4388
+ Because you preserved user UUIDs in Step 1, any \`reference→users\` field in
4389
+ these rows already points at a valid user. For FKs between data tables,
4390
+ preserve those source ids too (store them in a field) or rewrite them with a
4391
+ mapping you keep while importing.
4392
+
4393
+ ## Step 4 — Verify
4394
+
4395
+ - Spot-check: a migrated user can log in with their old password.
4396
+ - A migrated row's \`reference→users\` resolves (the user exists).
4397
+ - Counts match the Supabase source.
4398
+
4399
+ ## Auth for the import endpoints
4400
+
4401
+ All three steps accept \`sk_live_*\` from a server/agent:
4402
+ - Users: \`POST /v1/server/users/import\` (server-scoped, sk_live_*). The
4403
+ dashboard variant \`POST /v1/dashboard/organizations/users/import\` (TenantMember
4404
+ auth) does the same thing for a human in the dashboard.
4405
+ - Schemas: \`POST /v1/entity/schemas\`.
4406
+ - Rows: \`POST /v1/entity/records/bulk\`.`;
4407
+ }
4408
+ // =============================================================================
4302
4409
  // MCP Server Implementation
4303
4410
  // =============================================================================
4304
4411
  const server = new Server({
@@ -4636,6 +4743,24 @@ developer says "I want to build X" before writing code.`,
4636
4743
  required: ["template"],
4637
4744
  },
4638
4745
  },
4746
+ {
4747
+ name: "get_supabase_migration_recipe",
4748
+ description: `Step-by-step recipe to migrate a Supabase project into Genlobe (G10). Covers the tricky part — users + foreign keys — in the correct order: import users FIRST preserving their UUIDs (so FKs stay valid) and their bcrypt password hash (Supabase + Genlobe both use bcrypt — no password reset), then create entity schemas mapping FK columns to reference fields (target_schema_slug: "users"), then bulk-insert rows. Explains the ADR-0012 mental model (Organization = namespace; your app's customers are rows, not Organizations). Pure data; no network call.`,
4749
+ inputSchema: { type: "object", properties: {}, required: [] },
4750
+ },
4751
+ {
4752
+ name: "invite_organization_owner",
4753
+ description: `Invite the owner of an Organization (F2/F4). Creates the owner end-user and EMAILS them their sign-in credentials. The agent NEVER sees the password — it is delivered by email only (MCP "no raw secrets" invariant). Ask the human for the owner's REAL email; never invent one (a bounced address gets SES-suppressed — incident #168). Requires sk_live_*. Returns { user_id, email, email_sent }. If email_sent is false, tell the human to grab the temporary credential from the Tenant Dashboard. Calls POST /v1/server/organizations/invite-owner.`,
4754
+ inputSchema: {
4755
+ type: "object",
4756
+ properties: {
4757
+ organization_id: { type: "string", description: "UUID of the Organization the owner will administer." },
4758
+ email: { type: "string", description: "The owner's REAL email address. Ask the human — never fabricate it." },
4759
+ display_name: { type: "string", description: "Optional display name for the owner." },
4760
+ },
4761
+ required: ["organization_id", "email"],
4762
+ },
4763
+ },
4639
4764
  ],
4640
4765
  };
4641
4766
  });
@@ -5827,6 +5952,41 @@ type to pick.`,
5827
5952
  content: [{ type: "text", text: APP_SCAFFOLD(template) }],
5828
5953
  };
5829
5954
  }
5955
+ case "get_supabase_migration_recipe": {
5956
+ return {
5957
+ content: [{ type: "text", text: SUPABASE_MIGRATION_RECIPE() }],
5958
+ };
5959
+ }
5960
+ case "invite_organization_owner": {
5961
+ if (!API_KEY) {
5962
+ return {
5963
+ content: [{ type: "text", text: `❌ Cannot invite owner: SAAS_API_KEY is not configured. A secret key (sk_live_*) is required.` }],
5964
+ };
5965
+ }
5966
+ const { organization_id, email, display_name } = args;
5967
+ try {
5968
+ const r = await fetch(`${API_URL}/v1/server/organizations/invite-owner`, {
5969
+ method: "POST",
5970
+ headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
5971
+ body: JSON.stringify({ organization_id, email, display_name }),
5972
+ });
5973
+ const body = await r.json();
5974
+ if (!r.ok) {
5975
+ return {
5976
+ content: [{ type: "text", text: `HTTP ${r.status} from /v1/server/organizations/invite-owner — ${body.detail ?? JSON.stringify(body)}.\n\nNeeds a secret key (sk_live_*). The org must belong to this key's tenant.` }],
5977
+ };
5978
+ }
5979
+ const note = body.email_sent
5980
+ ? "✅ The owner has been emailed their sign-in credentials. They can now sign in at the Org Owner Dashboard and change their password."
5981
+ : "⚠️ Owner created, but the credentials email could NOT be sent (SMTP not configured). Ask the human to retrieve the temporary credential from the Tenant Dashboard → Organizations → invite owner. The password is never exposed here.";
5982
+ return {
5983
+ content: [{ type: "text", text: `Owner invited.\n${JSON.stringify(body, null, 2)}\n\n${note}` }],
5984
+ };
5985
+ }
5986
+ catch (err) {
5987
+ return { content: [{ type: "text", text: `Network error: ${err}` }] };
5988
+ }
5989
+ }
5830
5990
  default:
5831
5991
  throw new Error(`Unknown tool: ${name}`);
5832
5992
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genlobe/mcp-server",
3
- "version": "3.7.2",
3
+ "version": "3.8.0",
4
4
  "description": "MCP Server for GenLobe SaaS API - Provides AI assistants with comprehensive API documentation for building frontend applications",
5
5
  "main": "dist/index.js",
6
6
  "bin": {