@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.
- package/dist/index.js +168 -8
- 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
|
-
##
|
|
3734
|
+
## ⭐ Public chatbots: use service-mode (ADR-0013) — the simple path
|
|
3735
3735
|
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
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**
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
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