@genlobe/mcp-server 3.6.2 → 3.7.1
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 +787 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3497,6 +3497,741 @@ has a different shape because of where the API key can safely live.`,
|
|
|
3497
3497
|
const body = sections[appType] ?? sections["unsure"];
|
|
3498
3498
|
return `# Stack recommendation\n\n${keyLine}\n\n${body}\n\n---\n\nRun \`get_security_guide\` for the full security cheat-sheet that matches your key type.`;
|
|
3499
3499
|
}
|
|
3500
|
+
const ENTITY_SCHEMA_RECIPES = {
|
|
3501
|
+
product: {
|
|
3502
|
+
slug: "product",
|
|
3503
|
+
name: "Product",
|
|
3504
|
+
description: "Catalog product with price, stock, category, description.",
|
|
3505
|
+
fields_definition: {
|
|
3506
|
+
name: { type: "string", required: true, max_length: 200 },
|
|
3507
|
+
price: { type: "float", required: true, min: 0 },
|
|
3508
|
+
stock: { type: "integer", required: true, min: 0, default: 0 },
|
|
3509
|
+
currency: { type: "string", required: false, default: "USD", max_length: 3 },
|
|
3510
|
+
category: { type: "string", required: false, max_length: 100 },
|
|
3511
|
+
description: { type: "text", required: false },
|
|
3512
|
+
is_active: { type: "boolean", required: false, default: true },
|
|
3513
|
+
},
|
|
3514
|
+
bulk_seed_example: [
|
|
3515
|
+
{ name: "Sugar 1kg", price: 2.5, stock: 100, category: "groceries" },
|
|
3516
|
+
{ name: "Rice 1kg", price: 3.0, stock: 50, category: "groceries" },
|
|
3517
|
+
{ name: "Coffee 250g", price: 6.75, stock: 30, category: "groceries" },
|
|
3518
|
+
],
|
|
3519
|
+
notes: "If you need product variants (size/color), keep `name` for the base product and add a separate `variant` entity with a `product_id` reference (type=reference, target_schema_slug='product').",
|
|
3520
|
+
},
|
|
3521
|
+
customer: {
|
|
3522
|
+
slug: "customer",
|
|
3523
|
+
name: "Customer",
|
|
3524
|
+
description: "Store-side customer record (NOT a Genlobe end-user — those go through /v1/auth/register). Use this when you want to track purchases without forcing email signup.",
|
|
3525
|
+
fields_definition: {
|
|
3526
|
+
name: { type: "string", required: true, max_length: 200 },
|
|
3527
|
+
phone: { type: "string", required: false, max_length: 50 },
|
|
3528
|
+
email: { type: "email", required: false, max_length: 200 },
|
|
3529
|
+
notes: { type: "text", required: false },
|
|
3530
|
+
tags: { type: "text", required: false },
|
|
3531
|
+
},
|
|
3532
|
+
bulk_seed_example: [
|
|
3533
|
+
{ name: "Ana Pérez", phone: "+1-809-555-0100" },
|
|
3534
|
+
{ name: "Walk-in", notes: "no-data anonymous buyer" },
|
|
3535
|
+
],
|
|
3536
|
+
notes: "Common mistake: registering each store customer as a Genlobe end-user. Don't — that requires real email + SES delivery. Keep store customers as this custom entity unless they need to log into the storefront. `tags` is a comma-separated string (the schema validator does NOT support `array` as a field type — see the global notes at the top of every recipe).",
|
|
3537
|
+
},
|
|
3538
|
+
order: {
|
|
3539
|
+
slug: "order",
|
|
3540
|
+
name: "Order / Sale",
|
|
3541
|
+
description: "A completed sale with line items (price + name snapshotted at the time of sale, so editing a product later does NOT mutate past orders).",
|
|
3542
|
+
fields_definition: {
|
|
3543
|
+
customer_id: {
|
|
3544
|
+
type: "reference",
|
|
3545
|
+
target_schema_slug: "customer",
|
|
3546
|
+
required: false,
|
|
3547
|
+
on_delete: "set_null",
|
|
3548
|
+
},
|
|
3549
|
+
items_json: {
|
|
3550
|
+
type: "text",
|
|
3551
|
+
required: true,
|
|
3552
|
+
},
|
|
3553
|
+
total: { type: "float", required: true, min: 0 },
|
|
3554
|
+
currency: { type: "string", required: false, default: "USD" },
|
|
3555
|
+
paid: { type: "boolean", required: true, default: false },
|
|
3556
|
+
paid_at: { type: "datetime", required: false },
|
|
3557
|
+
notes: { type: "text", required: false },
|
|
3558
|
+
},
|
|
3559
|
+
notes: "**Line items modeling — important.** Genlobe's schema validator does NOT support `array` as a field type (validated types: string, integer, float, boolean, datetime, text, enum, email, url, phone, reference). Two ways to model line items:\n\n**Option A — simple, recommended for MVP**: store the items as a JSON string in `items_json` (type=text). Parse client-side. Each item should already include `product_id`, `name_snapshot`, `price_snapshot`, `qty`. Trade-off: you cannot search/filter by items via `POST /v1/entity/records/search` — items are opaque to the engine.\n\n**Option B — queryable, for production**: create a separate `order_item` schema with `order_id: reference->order`, `product_id: reference->product`, `name_snapshot`, `price_snapshot`, `qty`. Cross-schema joins (ADR-0009) make `\"top 5 products sold this month\"` a single search call.\n\nEither way: **always snapshot `name` and `price` at write time** so editing the product later does NOT mutate past orders.",
|
|
3560
|
+
},
|
|
3561
|
+
post: {
|
|
3562
|
+
slug: "post",
|
|
3563
|
+
name: "Blog post / Article",
|
|
3564
|
+
description: "Authored content with title, body, status, tags.",
|
|
3565
|
+
fields_definition: {
|
|
3566
|
+
title: { type: "string", required: true, max_length: 200 },
|
|
3567
|
+
slug: { type: "string", required: true, max_length: 200 },
|
|
3568
|
+
body: { type: "string", required: true },
|
|
3569
|
+
status: {
|
|
3570
|
+
type: "string",
|
|
3571
|
+
required: true,
|
|
3572
|
+
enum: ["draft", "published", "archived"],
|
|
3573
|
+
default: "draft",
|
|
3574
|
+
},
|
|
3575
|
+
tags: { type: "text", required: false },
|
|
3576
|
+
published_at: { type: "datetime", required: false },
|
|
3577
|
+
cover_image_url: { type: "string", required: false },
|
|
3578
|
+
},
|
|
3579
|
+
notes: "`created_by_id` is the author (auto-stamped by the API from the JWT, no need to include in records). For multi-author orgs the elevated-role bypass on /v1/entity/records/search lets editors see everyone's posts.",
|
|
3580
|
+
},
|
|
3581
|
+
comment: {
|
|
3582
|
+
slug: "comment",
|
|
3583
|
+
name: "Comment",
|
|
3584
|
+
description: "User-authored comment that points at another record (post, product, order, anything).",
|
|
3585
|
+
fields_definition: {
|
|
3586
|
+
target_id: {
|
|
3587
|
+
type: "string",
|
|
3588
|
+
required: true,
|
|
3589
|
+
notes: "UUID of the commented record. Not a reference field because the target schema varies — store the target schema slug in `target_schema` if you need polymorphism.",
|
|
3590
|
+
},
|
|
3591
|
+
target_schema: { type: "string", required: true, max_length: 100 },
|
|
3592
|
+
body: { type: "string", required: true },
|
|
3593
|
+
is_deleted: { type: "boolean", required: false, default: false },
|
|
3594
|
+
},
|
|
3595
|
+
notes: "Polymorphic targets defeat the ADR-0009 `reference` validator. If your comments only ever point at ONE schema (e.g. `post`), drop `target_schema` and convert `target_id` to a proper reference for FK integrity.",
|
|
3596
|
+
},
|
|
3597
|
+
message: {
|
|
3598
|
+
slug: "message",
|
|
3599
|
+
name: "Chat / direct message",
|
|
3600
|
+
description: "A single message in a conversation. Pair with the native `conversations` table (Genlobe AI agents) or roll your own thread entity.",
|
|
3601
|
+
fields_definition: {
|
|
3602
|
+
conversation_id: { type: "string", required: true },
|
|
3603
|
+
sender_user_id: { type: "string", required: false },
|
|
3604
|
+
sender_role: {
|
|
3605
|
+
type: "string",
|
|
3606
|
+
required: true,
|
|
3607
|
+
enum: ["user", "assistant", "system"],
|
|
3608
|
+
},
|
|
3609
|
+
body: { type: "string", required: true },
|
|
3610
|
+
read_at: { type: "datetime", required: false },
|
|
3611
|
+
},
|
|
3612
|
+
notes: "If you're building a chatbot powered by a Genlobe agent, you do NOT need this entity — agent conversations are persisted automatically (see `/v1/user/agents/{id}/conversations`). Use this only for human-to-human messaging.",
|
|
3613
|
+
},
|
|
3614
|
+
task: {
|
|
3615
|
+
slug: "task",
|
|
3616
|
+
name: "Task / To-do",
|
|
3617
|
+
description: "Single task with title, status, due date, assignee.",
|
|
3618
|
+
fields_definition: {
|
|
3619
|
+
title: { type: "string", required: true, max_length: 200 },
|
|
3620
|
+
description: { type: "string", required: false },
|
|
3621
|
+
status: {
|
|
3622
|
+
type: "string",
|
|
3623
|
+
required: true,
|
|
3624
|
+
enum: ["pending", "in_progress", "completed", "cancelled"],
|
|
3625
|
+
default: "pending",
|
|
3626
|
+
},
|
|
3627
|
+
priority: {
|
|
3628
|
+
type: "string",
|
|
3629
|
+
required: false,
|
|
3630
|
+
enum: ["low", "medium", "high"],
|
|
3631
|
+
default: "medium",
|
|
3632
|
+
},
|
|
3633
|
+
assigned_to_id: {
|
|
3634
|
+
type: "reference",
|
|
3635
|
+
target_schema_slug: "users",
|
|
3636
|
+
required: false,
|
|
3637
|
+
on_delete: "set_null",
|
|
3638
|
+
},
|
|
3639
|
+
due_date: { type: "datetime", required: false },
|
|
3640
|
+
},
|
|
3641
|
+
notes: "`assigned_to_id` references the native `users` table (Genlobe end-users), not a custom entity. The reference validator enforces `OrganizationMember` active membership server-side.",
|
|
3642
|
+
},
|
|
3643
|
+
note: {
|
|
3644
|
+
slug: "note",
|
|
3645
|
+
name: "Personal note",
|
|
3646
|
+
description: "A free-text note owned by the creator. Row-level scope means each end-user only sees their own notes via /v1/entity/records/mine.",
|
|
3647
|
+
fields_definition: {
|
|
3648
|
+
title: { type: "string", required: false, max_length: 200 },
|
|
3649
|
+
body: { type: "string", required: true },
|
|
3650
|
+
tags: { type: "text", required: false },
|
|
3651
|
+
is_pinned: { type: "boolean", required: false, default: false },
|
|
3652
|
+
},
|
|
3653
|
+
notes: "Perfect for ADR-0007 row-level scope: regular end-users see only their own notes via `GET /v1/entity/records/mine` (no extra filter needed in the frontend).",
|
|
3654
|
+
},
|
|
3655
|
+
};
|
|
3656
|
+
function ENTITY_SCHEMA_RECIPE(entityType) {
|
|
3657
|
+
const recipe = ENTITY_SCHEMA_RECIPES[entityType];
|
|
3658
|
+
if (!recipe) {
|
|
3659
|
+
return `# Unknown entity_type "${entityType}"\n\nAvailable: ${Object.keys(ENTITY_SCHEMA_RECIPES).join(", ")}.`;
|
|
3660
|
+
}
|
|
3661
|
+
const postBody = {
|
|
3662
|
+
name: recipe.name,
|
|
3663
|
+
slug: recipe.slug,
|
|
3664
|
+
description: recipe.description,
|
|
3665
|
+
fields_definition: recipe.fields_definition,
|
|
3666
|
+
};
|
|
3667
|
+
const bulkBody = recipe.bulk_seed_example
|
|
3668
|
+
? {
|
|
3669
|
+
schema_id: "<paste the id returned by POST /v1/entity/schemas above>",
|
|
3670
|
+
records: recipe.bulk_seed_example,
|
|
3671
|
+
}
|
|
3672
|
+
: null;
|
|
3673
|
+
return `# Entity schema recipe — ${recipe.name}
|
|
3674
|
+
|
|
3675
|
+
${recipe.description}
|
|
3676
|
+
|
|
3677
|
+
## Schema field types — what the backend actually accepts
|
|
3678
|
+
|
|
3679
|
+
The Custom Entities validator only accepts these types in \`fields_definition\`:
|
|
3680
|
+
|
|
3681
|
+
\`string\`, \`text\`, \`integer\`, \`float\`, \`boolean\`, \`datetime\`, \`enum\`, \`email\`, \`url\`, \`phone\`, \`reference\` (ADR-0009).
|
|
3682
|
+
|
|
3683
|
+
**There is no \`array\` type and no \`number\` type.** Use \`integer\`/\`float\` instead of \`number\`. For list-shaped data either (a) store as a comma-separated \`text\` (simple, not queryable), or (b) model as a separate entity with a \`reference\` field pointing back (queryable, ADR-0009 cross-schema search).
|
|
3684
|
+
|
|
3685
|
+
## Step 1 — Create the schema
|
|
3686
|
+
|
|
3687
|
+
\`\`\`http
|
|
3688
|
+
POST /v1/entity/schemas
|
|
3689
|
+
Content-Type: application/json
|
|
3690
|
+
X-API-Key: <your sk_live_*>
|
|
3691
|
+
X-Organization-Id: <your organization_id>
|
|
3692
|
+
\`\`\`
|
|
3693
|
+
|
|
3694
|
+
Body:
|
|
3695
|
+
\`\`\`json
|
|
3696
|
+
${JSON.stringify(postBody, null, 2)}
|
|
3697
|
+
\`\`\`
|
|
3698
|
+
|
|
3699
|
+
${bulkBody
|
|
3700
|
+
? `## Step 2 — Seed sample rows (bulk)
|
|
3701
|
+
|
|
3702
|
+
\`\`\`http
|
|
3703
|
+
POST /v1/entity/records/bulk
|
|
3704
|
+
Content-Type: application/json
|
|
3705
|
+
X-API-Key: <your sk_live_*>
|
|
3706
|
+
X-Organization-Id: <your organization_id>
|
|
3707
|
+
\`\`\`
|
|
3708
|
+
|
|
3709
|
+
Body:
|
|
3710
|
+
\`\`\`json
|
|
3711
|
+
${JSON.stringify(bulkBody, null, 2)}
|
|
3712
|
+
\`\`\`
|
|
3713
|
+
|
|
3714
|
+
Bulk insert is capped at 500 records per call; chunk larger migrations.
|
|
3715
|
+
All-or-nothing: any validation failure rolls back the whole batch.
|
|
3716
|
+
`
|
|
3717
|
+
: ""}
|
|
3718
|
+
${recipe.notes ? `## Notes\n\n${recipe.notes}\n` : ""}
|
|
3719
|
+
|
|
3720
|
+
---
|
|
3721
|
+
|
|
3722
|
+
**Before posting**, call \`get_reserved_schema_slugs()\` to confirm \`${recipe.slug}\` is not on the reserved list. It is not, today, but slug-collision checking is part of the contract.`;
|
|
3723
|
+
}
|
|
3724
|
+
// =============================================================================
|
|
3725
|
+
// Chatbot setup recipe (v3.7.0) — closes G2.
|
|
3726
|
+
// =============================================================================
|
|
3727
|
+
function CHATBOT_SETUP_RECIPE() {
|
|
3728
|
+
return `# Chatbot setup recipe — agent + KB bound by \`rag_role\`
|
|
3729
|
+
|
|
3730
|
+
End-to-end recipe to wire a chatbot agent that answers from a knowledge base
|
|
3731
|
+
populated by a snapshot of your custom-entity catalog (typical "store
|
|
3732
|
+
assistant" / "product Q&A" / "support deflection" use case).
|
|
3733
|
+
|
|
3734
|
+
## Auth context — clarification (frequent confusion)
|
|
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):
|
|
3739
|
+
|
|
3740
|
+
- **(a) Real human end-user**: customer signs up via \`POST /v1/auth/register\`,
|
|
3741
|
+
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.
|
|
3746
|
+
|
|
3747
|
+
**What is NOT allowed** (and what some agents mis-read as "no LLM bot is
|
|
3748
|
+
possible"): inventing/synthesizing fake email addresses to bulk-create
|
|
3749
|
+
end-users on the fly. That bypass would route through SES and burn the
|
|
3750
|
+
sender reputation — exactly the incident pattern of #168. Registering one
|
|
3751
|
+
or a few real bot accounts that you actually own is fine.
|
|
3752
|
+
|
|
3753
|
+
If you cannot or do not want to register a bot user, an acceptable fallback
|
|
3754
|
+
for MVP is a deterministic keyword search over the catalog (no LLM, no JWT
|
|
3755
|
+
required) — useful when the storefront has to be live before you set up the
|
|
3756
|
+
LLM layer. The bot user path is still the canonical way to get a real LLM bot.
|
|
3757
|
+
|
|
3758
|
+
## Architecture in one diagram
|
|
3759
|
+
|
|
3760
|
+
\`\`\`
|
|
3761
|
+
Custom-entity catalog
|
|
3762
|
+
(e.g. products)
|
|
3763
|
+
│
|
|
3764
|
+
│ 1. snapshot to plain text
|
|
3765
|
+
▼
|
|
3766
|
+
KnowledgeBase (per-Org)
|
|
3767
|
+
│
|
|
3768
|
+
│ 2. uploaded as a Document
|
|
3769
|
+
▼
|
|
3770
|
+
Agent (rag_role bound to KB) ←─ end-user chats
|
|
3771
|
+
/v1/user/agents/{id}/chat
|
|
3772
|
+
\`\`\`
|
|
3773
|
+
|
|
3774
|
+
## Step-by-step
|
|
3775
|
+
|
|
3776
|
+
### 1. Create the KnowledgeBase
|
|
3777
|
+
|
|
3778
|
+
\`\`\`http
|
|
3779
|
+
POST /v1/organization-admin/{org_id}/knowledge-bases
|
|
3780
|
+
\`\`\`
|
|
3781
|
+
|
|
3782
|
+
Body:
|
|
3783
|
+
\`\`\`json
|
|
3784
|
+
{
|
|
3785
|
+
"name": "Vendy catalog",
|
|
3786
|
+
"description": "Snapshot of products, updated on demand",
|
|
3787
|
+
"rag_role": "product_catalog"
|
|
3788
|
+
}
|
|
3789
|
+
\`\`\`
|
|
3790
|
+
|
|
3791
|
+
Save the returned \`kb_id\`. The \`rag_role\` value is the binding key — the
|
|
3792
|
+
agent will reference the same role string to find this KB at chat time.
|
|
3793
|
+
|
|
3794
|
+
### 2. Snapshot your catalog and upload it as a Document
|
|
3795
|
+
|
|
3796
|
+
Pull every product record:
|
|
3797
|
+
|
|
3798
|
+
\`\`\`http
|
|
3799
|
+
POST /v1/entity/records/search
|
|
3800
|
+
\`\`\`
|
|
3801
|
+
|
|
3802
|
+
Body: \`{ "schema_id": "<your product schema id>", "query": { "is_active": true } }\`
|
|
3803
|
+
|
|
3804
|
+
Format each row as one paragraph the LLM can read:
|
|
3805
|
+
|
|
3806
|
+
\`\`\`text
|
|
3807
|
+
Product: Sugar 1kg
|
|
3808
|
+
Price: $2.50
|
|
3809
|
+
Stock: 100
|
|
3810
|
+
Category: groceries
|
|
3811
|
+
Description: white sugar in 1kg bags
|
|
3812
|
+
---
|
|
3813
|
+
Product: Rice 1kg
|
|
3814
|
+
...
|
|
3815
|
+
\`\`\`
|
|
3816
|
+
|
|
3817
|
+
Upload as a document:
|
|
3818
|
+
|
|
3819
|
+
\`\`\`http
|
|
3820
|
+
POST /v1/organization-admin/{org_id}/knowledge-bases/{kb_id}/documents
|
|
3821
|
+
\`\`\`
|
|
3822
|
+
|
|
3823
|
+
Body:
|
|
3824
|
+
\`\`\`json
|
|
3825
|
+
{
|
|
3826
|
+
"title": "Catalog snapshot 2026-05-26",
|
|
3827
|
+
"content": "<the multi-paragraph text from above>",
|
|
3828
|
+
"metadata": { "source": "catalog_snapshot", "snapshotted_at": "<ISO timestamp>" }
|
|
3829
|
+
}
|
|
3830
|
+
\`\`\`
|
|
3831
|
+
|
|
3832
|
+
### 3. Create the Agent
|
|
3833
|
+
|
|
3834
|
+
\`\`\`http
|
|
3835
|
+
POST /v1/agents
|
|
3836
|
+
\`\`\`
|
|
3837
|
+
|
|
3838
|
+
Body:
|
|
3839
|
+
\`\`\`json
|
|
3840
|
+
{
|
|
3841
|
+
"name": "Vendy Assistant",
|
|
3842
|
+
"description": "Answers customer questions about the store catalog.",
|
|
3843
|
+
"system_prompt": "You are a helpful store assistant for Vendy. Answer ONLY about products in the catalog you have access to. If the customer asks about anything else (delivery, billing, hours), reply that you can only help with product questions. Never invent products or prices.",
|
|
3844
|
+
"is_public": true,
|
|
3845
|
+
"rag_role": "product_catalog"
|
|
3846
|
+
}
|
|
3847
|
+
\`\`\`
|
|
3848
|
+
|
|
3849
|
+
\`is_public: true\` lets every end-user of the Org talk to the same agent (no
|
|
3850
|
+
per-user fork). \`rag_role: "product_catalog"\` is the binding to the KB
|
|
3851
|
+
created in step 1.
|
|
3852
|
+
|
|
3853
|
+
### 4. End-user chat (frontend)
|
|
3854
|
+
|
|
3855
|
+
\`\`\`http
|
|
3856
|
+
POST /v1/user/agents/{agent_id}/chat
|
|
3857
|
+
\`\`\`
|
|
3858
|
+
|
|
3859
|
+
Body:
|
|
3860
|
+
\`\`\`json
|
|
3861
|
+
{
|
|
3862
|
+
"message": "do you have rice?",
|
|
3863
|
+
"conversation_id": "<optional, persist in sessionStorage to keep context>"
|
|
3864
|
+
}
|
|
3865
|
+
\`\`\`
|
|
3866
|
+
|
|
3867
|
+
Returns the assistant's response plus a \`conversation_id\` you should pass on
|
|
3868
|
+
subsequent messages to keep the same thread.
|
|
3869
|
+
|
|
3870
|
+
## Refresh policy
|
|
3871
|
+
|
|
3872
|
+
When products change (price update, new SKU), regenerate the snapshot text
|
|
3873
|
+
and either:
|
|
3874
|
+
|
|
3875
|
+
- **Replace the document** (\`DELETE /v1/organization-admin/{org_id}/knowledge-bases/{kb_id}/documents/{doc_id}\` then re-upload) — simplest.
|
|
3876
|
+
- **Append a delta document** (\`POST\` a new document with only the changes) — faster but you need to manage stale entries.
|
|
3877
|
+
|
|
3878
|
+
For an MVP the first option is what we recommend.
|
|
3879
|
+
|
|
3880
|
+
## Server-side TypeScript snippet
|
|
3881
|
+
|
|
3882
|
+
\`\`\`typescript
|
|
3883
|
+
// lib/genlobe.server.ts
|
|
3884
|
+
import 'server-only';
|
|
3885
|
+
|
|
3886
|
+
const API = process.env.SAAS_API_URL!;
|
|
3887
|
+
const KEY = process.env.SAAS_API_KEY!; // sk_live_*
|
|
3888
|
+
const ORG = process.env.SAAS_ORGANIZATION_ID!;
|
|
3889
|
+
|
|
3890
|
+
async function api(path: string, body: any, jwt?: string) {
|
|
3891
|
+
const res = await fetch(\`\${API}\${path}\`, {
|
|
3892
|
+
method: 'POST',
|
|
3893
|
+
headers: {
|
|
3894
|
+
'Content-Type': 'application/json',
|
|
3895
|
+
'X-API-Key': KEY,
|
|
3896
|
+
'X-Organization-Id': ORG,
|
|
3897
|
+
...(jwt ? { 'Authorization': \`Bearer \${jwt}\` } : {}),
|
|
3898
|
+
},
|
|
3899
|
+
body: JSON.stringify(body),
|
|
3900
|
+
});
|
|
3901
|
+
if (!res.ok) throw new Error(\`\${res.status} \${await res.text()}\`);
|
|
3902
|
+
return res.json();
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
export async function refreshCatalogKB(productSchemaId: string, kbId: string, existingDocId?: string) {
|
|
3906
|
+
const { records } = await api('/v1/entity/records/search', {
|
|
3907
|
+
schema_id: productSchemaId,
|
|
3908
|
+
query: { is_active: true },
|
|
3909
|
+
});
|
|
3910
|
+
const content = records
|
|
3911
|
+
.map((r: any) =>
|
|
3912
|
+
\`Product: \${r.data.name}\\nPrice: $\${r.data.price}\\nStock: \${r.data.stock}\\nCategory: \${r.data.category ?? 'n/a'}\\nDescription: \${r.data.description ?? ''}\`
|
|
3913
|
+
)
|
|
3914
|
+
.join('\\n---\\n');
|
|
3915
|
+
|
|
3916
|
+
if (existingDocId) {
|
|
3917
|
+
await fetch(\`\${API}/v1/organization-admin/\${ORG}/knowledge-bases/\${kbId}/documents/\${existingDocId}\`, {
|
|
3918
|
+
method: 'DELETE',
|
|
3919
|
+
headers: { 'X-API-Key': KEY, 'X-Organization-Id': ORG },
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3922
|
+
return api(\`/v1/organization-admin/\${ORG}/knowledge-bases/\${kbId}/documents\`, {
|
|
3923
|
+
title: \`Catalog snapshot \${new Date().toISOString()}\`,
|
|
3924
|
+
content,
|
|
3925
|
+
metadata: { source: 'catalog_snapshot' },
|
|
3926
|
+
});
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
export async function chatWithAgent(agentId: string, message: string, conversationId?: string) {
|
|
3930
|
+
return api(\`/v1/user/agents/\${agentId}/chat\`, {
|
|
3931
|
+
message,
|
|
3932
|
+
...(conversationId ? { conversation_id: conversationId } : {}),
|
|
3933
|
+
});
|
|
3934
|
+
}
|
|
3935
|
+
\`\`\`
|
|
3936
|
+
|
|
3937
|
+
## Notes
|
|
3938
|
+
|
|
3939
|
+
- \`POST /v1/agents\` is the Tenant Dashboard endpoint — when configuring from
|
|
3940
|
+
your scaffolding script, use \`sk_live_*\`. End-user chat (\`/v1/user/agents/{id}/chat\`)
|
|
3941
|
+
is reached with \`pk_live_*\` + the end-user's JWT.
|
|
3942
|
+
- Tool calling vs RAG: for MVP / low-frequency catalog updates, RAG via KB is
|
|
3943
|
+
simpler. Switch to tool calling when the catalog updates faster than the
|
|
3944
|
+
refresh cadence you can sustain.
|
|
3945
|
+
- The bot does not write to the catalog and does not take orders unless you
|
|
3946
|
+
add a separate tool / API path. Keep the system prompt strict.`;
|
|
3947
|
+
}
|
|
3948
|
+
const APP_SCAFFOLDS = {
|
|
3949
|
+
pos: {
|
|
3950
|
+
template: "pos",
|
|
3951
|
+
title: "POS / small-store register",
|
|
3952
|
+
description: "A storefront with a chatbot for product questions and an owner dashboard for products, customers, sales.",
|
|
3953
|
+
entities: [
|
|
3954
|
+
{ slug: "product", recipe_key: "product", purpose: "Catalog the owner manages." },
|
|
3955
|
+
{
|
|
3956
|
+
slug: "customer",
|
|
3957
|
+
recipe_key: "customer",
|
|
3958
|
+
purpose: "Store-side customer record (NOT a Genlobe end-user).",
|
|
3959
|
+
},
|
|
3960
|
+
{
|
|
3961
|
+
slug: "order",
|
|
3962
|
+
recipe_key: "order",
|
|
3963
|
+
purpose: "Completed sales with snapshotted line items (FK to product + customer).",
|
|
3964
|
+
},
|
|
3965
|
+
],
|
|
3966
|
+
agents: [
|
|
3967
|
+
{
|
|
3968
|
+
name: "Store Assistant",
|
|
3969
|
+
rag_role: "product_catalog",
|
|
3970
|
+
purpose: "Answers customer questions about the product catalog. Read-only.",
|
|
3971
|
+
},
|
|
3972
|
+
],
|
|
3973
|
+
kbs: [{ rag_role: "product_catalog", source: "Snapshot of `product` records" }],
|
|
3974
|
+
kpis: [
|
|
3975
|
+
{
|
|
3976
|
+
name: "Today's revenue",
|
|
3977
|
+
how: "POST /v1/entity/records/search with `query: { paid: true, paid_at: { operator: 'gte', value: <today_iso> } }`, sum `data.total` client-side.",
|
|
3978
|
+
},
|
|
3979
|
+
{
|
|
3980
|
+
name: "Top product this month",
|
|
3981
|
+
how: "Search orders, flatten `items[]`, group by `product_id`, sum `qty`. Cross-schema `join` with `product` to get the name in the same call.",
|
|
3982
|
+
},
|
|
3983
|
+
{
|
|
3984
|
+
name: "New customers this week",
|
|
3985
|
+
how: "Search customers with `created_at` filter; show count + names.",
|
|
3986
|
+
},
|
|
3987
|
+
],
|
|
3988
|
+
build_order: [
|
|
3989
|
+
"1. Next.js 16 app router + Tailwind + shadcn. Add `lib/genlobe.server.ts` with `import 'server-only'`.",
|
|
3990
|
+
"2. Bootstrap script: create `product`, `customer`, `order` schemas via `get_entity_schema_recipe`. Bulk-seed 5-10 sample products.",
|
|
3991
|
+
"3. Owner login: `/admin/login` → POST /v1/auth/login server-side → cookie httpOnly with JWT.",
|
|
3992
|
+
"4. Owner CRUD products (single end-to-end vertical slice first).",
|
|
3993
|
+
"5. Sales form (`/admin/sales/new`): customer autocomplete, multi-product line items, total auto-calc, POST one `order` record.",
|
|
3994
|
+
"6. Sales list + detail.",
|
|
3995
|
+
"7. Customers list + per-customer purchase history (cross-schema join from order → customer).",
|
|
3996
|
+
"8. Dashboard KPIs (search + sum client-side; switch to server-side aggregates later if data grows).",
|
|
3997
|
+
"9. Public storefront `/` + `/chat`. Bot via `get_chatbot_setup_recipe`.",
|
|
3998
|
+
"10. Catalog refresh button in `/admin` (regenerates the KB document).",
|
|
3999
|
+
],
|
|
4000
|
+
notes: "Stock decrement on sale is intentionally NOT automatic — for MVP let the owner adjust stock manually after a sale, to avoid double-decrement bugs.",
|
|
4001
|
+
},
|
|
4002
|
+
crm: {
|
|
4003
|
+
template: "crm",
|
|
4004
|
+
title: "Lightweight CRM",
|
|
4005
|
+
description: "Track contacts, deals, notes, and a sales pipeline. No marketing automation, no email integration — that's later.",
|
|
4006
|
+
entities: [
|
|
4007
|
+
{ slug: "customer", recipe_key: "customer", purpose: "Contact records." },
|
|
4008
|
+
{
|
|
4009
|
+
slug: "deal",
|
|
4010
|
+
recipe_key: "order",
|
|
4011
|
+
purpose: "Reuse the `order` recipe but rename to `deal`; line items become deliverables and `paid` becomes `closed_won`.",
|
|
4012
|
+
},
|
|
4013
|
+
{
|
|
4014
|
+
slug: "note",
|
|
4015
|
+
recipe_key: "note",
|
|
4016
|
+
purpose: "Free-text notes per contact (rename `note` → `interaction` if you want to scope it to the contact).",
|
|
4017
|
+
},
|
|
4018
|
+
],
|
|
4019
|
+
agents: [
|
|
4020
|
+
{
|
|
4021
|
+
name: "CRM Helper",
|
|
4022
|
+
purpose: "Internal-only agent that drafts follow-up emails based on the most recent notes on a contact. No KB needed; works with conversation context.",
|
|
4023
|
+
},
|
|
4024
|
+
],
|
|
4025
|
+
kbs: [],
|
|
4026
|
+
kpis: [
|
|
4027
|
+
{ name: "Open pipeline value", how: "Search deals where `closed_won: false`, sum `total`." },
|
|
4028
|
+
{
|
|
4029
|
+
name: "Conversion rate",
|
|
4030
|
+
how: "Count deals `closed_won: true` / total deals over a period.",
|
|
4031
|
+
},
|
|
4032
|
+
{
|
|
4033
|
+
name: "Stale contacts",
|
|
4034
|
+
how: "Customers with no `note` records in the last 30 days (cross-schema NOT-EXISTS).",
|
|
4035
|
+
},
|
|
4036
|
+
],
|
|
4037
|
+
build_order: [
|
|
4038
|
+
"1. Same bootstrap as POS.",
|
|
4039
|
+
"2. Customer CRUD (list + detail + edit).",
|
|
4040
|
+
"3. Notes per customer (reference field to customer).",
|
|
4041
|
+
"4. Deals pipeline view (kanban by `status`).",
|
|
4042
|
+
"5. CRM Helper agent (no KB) for drafting follow-ups.",
|
|
4043
|
+
],
|
|
4044
|
+
notes: "If you want incoming email parsing, that's a Stage-3 deploy capability — not in MVP.",
|
|
4045
|
+
},
|
|
4046
|
+
blog: {
|
|
4047
|
+
template: "blog",
|
|
4048
|
+
title: "Multi-author blog",
|
|
4049
|
+
description: "Posts, comments, tags. Public read, authenticated write.",
|
|
4050
|
+
entities: [
|
|
4051
|
+
{ slug: "post", recipe_key: "post", purpose: "Articles." },
|
|
4052
|
+
{ slug: "comment", recipe_key: "comment", purpose: "User comments on posts." },
|
|
4053
|
+
],
|
|
4054
|
+
agents: [
|
|
4055
|
+
{
|
|
4056
|
+
name: "Blog Recommender",
|
|
4057
|
+
rag_role: "blog_archive",
|
|
4058
|
+
purpose: "Suggests related posts based on what the reader is browsing.",
|
|
4059
|
+
},
|
|
4060
|
+
],
|
|
4061
|
+
kbs: [{ rag_role: "blog_archive", source: "Snapshot of published `post` records" }],
|
|
4062
|
+
kpis: [
|
|
4063
|
+
{ name: "Posts published this month", how: "Search posts where `status: published`, count." },
|
|
4064
|
+
{
|
|
4065
|
+
name: "Most commented post",
|
|
4066
|
+
how: "Search comments, group by `target_id`, count, cross-schema join with `post` for the title.",
|
|
4067
|
+
},
|
|
4068
|
+
],
|
|
4069
|
+
build_order: [
|
|
4070
|
+
"1. Bootstrap.",
|
|
4071
|
+
"2. Post CRUD (admin only — gate with role check).",
|
|
4072
|
+
"3. Public reader view (`/blog/[slug]`) with comments form.",
|
|
4073
|
+
"4. Comments moderation page.",
|
|
4074
|
+
"5. Recommender bot via KB.",
|
|
4075
|
+
],
|
|
4076
|
+
},
|
|
4077
|
+
task_manager: {
|
|
4078
|
+
template: "task_manager",
|
|
4079
|
+
title: "Personal / team task manager",
|
|
4080
|
+
description: "To-do list with assignees, due dates, and status. Per-user view by default (ADR-0007 row-level scope).",
|
|
4081
|
+
entities: [{ slug: "task", recipe_key: "task", purpose: "Tasks." }],
|
|
4082
|
+
agents: [
|
|
4083
|
+
{
|
|
4084
|
+
name: "Task Coach",
|
|
4085
|
+
purpose: "Suggests next-best task based on due dates and priorities (no KB; uses context).",
|
|
4086
|
+
},
|
|
4087
|
+
],
|
|
4088
|
+
kbs: [],
|
|
4089
|
+
kpis: [
|
|
4090
|
+
{
|
|
4091
|
+
name: "My open tasks",
|
|
4092
|
+
how: "GET /v1/entity/records/mine?schema_id=<task> — returns only the caller's tasks via row-level scope, no extra filter.",
|
|
4093
|
+
},
|
|
4094
|
+
{
|
|
4095
|
+
name: "Overdue tasks (team)",
|
|
4096
|
+
how: "Search tasks where `due_date < now` AND `status != completed`. Elevated role only.",
|
|
4097
|
+
},
|
|
4098
|
+
],
|
|
4099
|
+
build_order: [
|
|
4100
|
+
"1. Bootstrap.",
|
|
4101
|
+
"2. End-user signup + login (`pk_live_*` + JWT flow).",
|
|
4102
|
+
"3. `/tasks` list using `GET /v1/entity/records/mine`.",
|
|
4103
|
+
"4. Create / edit task.",
|
|
4104
|
+
"5. Coach bot.",
|
|
4105
|
+
],
|
|
4106
|
+
notes: "Use `GET /v1/entity/records/mine` instead of POST /search with `created_by_id` filter — row-level scope is enforced server-side, the frontend MUST NOT be the filter.",
|
|
4107
|
+
},
|
|
4108
|
+
notes: {
|
|
4109
|
+
template: "notes",
|
|
4110
|
+
title: "Personal notes app",
|
|
4111
|
+
description: "Free-text notes per user, scoped to the author. Inspired by Apple Notes / Bear.",
|
|
4112
|
+
entities: [{ slug: "note", recipe_key: "note", purpose: "Notes." }],
|
|
4113
|
+
agents: [
|
|
4114
|
+
{
|
|
4115
|
+
name: "Notes Search",
|
|
4116
|
+
rag_role: "user_notes",
|
|
4117
|
+
purpose: "Semantic search across the user's notes. Per-user KB scoped via row-level scope on the search side.",
|
|
4118
|
+
},
|
|
4119
|
+
],
|
|
4120
|
+
kbs: [
|
|
4121
|
+
{
|
|
4122
|
+
rag_role: "user_notes",
|
|
4123
|
+
source: "Each user's notes uploaded as one document per note. Could also be one consolidated doc per user, refreshed on each save.",
|
|
4124
|
+
},
|
|
4125
|
+
],
|
|
4126
|
+
kpis: [
|
|
4127
|
+
{ name: "Total notes", how: "GET /v1/entity/records/mine — count." },
|
|
4128
|
+
{ name: "Pinned", how: "GET /v1/entity/records/mine with `is_pinned: true` filter." },
|
|
4129
|
+
],
|
|
4130
|
+
build_order: [
|
|
4131
|
+
"1. Bootstrap + end-user auth.",
|
|
4132
|
+
"2. Note CRUD (`/v1/entity/records/mine` for the list).",
|
|
4133
|
+
"3. Search bot per user.",
|
|
4134
|
+
"4. Pin / tag filters.",
|
|
4135
|
+
],
|
|
4136
|
+
},
|
|
4137
|
+
support_inbox: {
|
|
4138
|
+
template: "support_inbox",
|
|
4139
|
+
title: "Customer support inbox",
|
|
4140
|
+
description: "Capture support tickets, route to agents, let an AI first-responder handle FAQs before escalation.",
|
|
4141
|
+
entities: [
|
|
4142
|
+
{
|
|
4143
|
+
slug: "ticket",
|
|
4144
|
+
recipe_key: "task",
|
|
4145
|
+
purpose: "Reuse `task` recipe. Rename `task` → `ticket`. `assigned_to_id` becomes the support agent (human end-user).",
|
|
4146
|
+
},
|
|
4147
|
+
{
|
|
4148
|
+
slug: "message",
|
|
4149
|
+
recipe_key: "message",
|
|
4150
|
+
purpose: "Conversation thread per ticket.",
|
|
4151
|
+
},
|
|
4152
|
+
],
|
|
4153
|
+
agents: [
|
|
4154
|
+
{
|
|
4155
|
+
name: "First Responder",
|
|
4156
|
+
rag_role: "support_kb",
|
|
4157
|
+
purpose: "Answers from the support knowledge base before escalating to a human.",
|
|
4158
|
+
},
|
|
4159
|
+
],
|
|
4160
|
+
kbs: [
|
|
4161
|
+
{
|
|
4162
|
+
rag_role: "support_kb",
|
|
4163
|
+
source: "Help docs / FAQs. Upload manually or sync from your existing help center.",
|
|
4164
|
+
},
|
|
4165
|
+
],
|
|
4166
|
+
kpis: [
|
|
4167
|
+
{ name: "Open tickets", how: "Search tickets where `status != completed`." },
|
|
4168
|
+
{
|
|
4169
|
+
name: "Avg time to first response",
|
|
4170
|
+
how: "For each ticket, find the first `message` where `sender_role != 'user'`, compute delta. Aggregate client-side.",
|
|
4171
|
+
},
|
|
4172
|
+
],
|
|
4173
|
+
build_order: [
|
|
4174
|
+
"1. Bootstrap.",
|
|
4175
|
+
"2. Public form (`/contact`) that creates a `ticket` + an initial `message`.",
|
|
4176
|
+
"3. Internal queue view (`/support/inbox`).",
|
|
4177
|
+
"4. First Responder bot, with escalation flag.",
|
|
4178
|
+
"5. SLA dashboard.",
|
|
4179
|
+
],
|
|
4180
|
+
},
|
|
4181
|
+
};
|
|
4182
|
+
function APP_SCAFFOLD(template) {
|
|
4183
|
+
const s = APP_SCAFFOLDS[template];
|
|
4184
|
+
if (!s) {
|
|
4185
|
+
return `# Unknown template "${template}"\n\nAvailable: ${Object.keys(APP_SCAFFOLDS).join(", ")}.`;
|
|
4186
|
+
}
|
|
4187
|
+
const entitySection = s.entities
|
|
4188
|
+
.map((e) => `- **\`${e.slug}\`** — ${e.purpose} Get the schema recipe with \`get_entity_schema_recipe({ entity_type: "${e.recipe_key}" })\`.`)
|
|
4189
|
+
.join("\n");
|
|
4190
|
+
const agentSection = s.agents
|
|
4191
|
+
.map((a) => `- **${a.name}**${a.rag_role ? ` (\`rag_role: "${a.rag_role}"\`)` : ""} — ${a.purpose}`)
|
|
4192
|
+
.join("\n");
|
|
4193
|
+
const kbSection = s.kbs.length
|
|
4194
|
+
? s.kbs
|
|
4195
|
+
.map((kb) => `- \`rag_role: "${kb.rag_role}"\` ← ${kb.source}`)
|
|
4196
|
+
.join("\n")
|
|
4197
|
+
: "_(none — agents work from conversation context)_";
|
|
4198
|
+
const kpiSection = s.kpis
|
|
4199
|
+
.map((k) => `- **${k.name}** — ${k.how}`)
|
|
4200
|
+
.join("\n");
|
|
4201
|
+
const orderSection = s.build_order.map((step) => step).join("\n");
|
|
4202
|
+
return `# App scaffold — ${s.title}
|
|
4203
|
+
|
|
4204
|
+
${s.description}
|
|
4205
|
+
|
|
4206
|
+
## Custom entities to create
|
|
4207
|
+
|
|
4208
|
+
${entitySection}
|
|
4209
|
+
|
|
4210
|
+
## Agents to create
|
|
4211
|
+
|
|
4212
|
+
${agentSection}
|
|
4213
|
+
|
|
4214
|
+
## Knowledge bases
|
|
4215
|
+
|
|
4216
|
+
${kbSection}
|
|
4217
|
+
|
|
4218
|
+
## Typical KPIs and how to compute them
|
|
4219
|
+
|
|
4220
|
+
${kpiSection}
|
|
4221
|
+
|
|
4222
|
+
## Suggested build order
|
|
4223
|
+
|
|
4224
|
+
${orderSection}
|
|
4225
|
+
|
|
4226
|
+
${s.notes ? `## Notes\n\n${s.notes}\n` : ""}
|
|
4227
|
+
|
|
4228
|
+
---
|
|
4229
|
+
|
|
4230
|
+
**Next calls**:
|
|
4231
|
+
- For each entity above, call \`get_entity_schema_recipe({ entity_type: "..." })\` and POST the result.
|
|
4232
|
+
- For the chatbot wiring, call \`get_chatbot_setup_recipe()\`.
|
|
4233
|
+
- For the auth shape (sk_live_* / pk_live_* + cookies), call \`get_authentication_flow()\` and \`get_security_guide()\`.`;
|
|
4234
|
+
}
|
|
3500
4235
|
// =============================================================================
|
|
3501
4236
|
// MCP Server Implementation
|
|
3502
4237
|
// =============================================================================
|
|
@@ -3800,6 +4535,41 @@ developer says "I want to build X" before writing code.`,
|
|
|
3800
4535
|
required: ["app_type"],
|
|
3801
4536
|
},
|
|
3802
4537
|
},
|
|
4538
|
+
{
|
|
4539
|
+
name: "get_entity_schema_recipe",
|
|
4540
|
+
description: `Return a ready-to-POST custom entity schema for a common entity type. Removes the guesswork of building \`fields_definition\` JSON by hand and ensures the agent picks the right field types (\`string\`, \`number\`, \`boolean\`, \`reference\` per ADR-0009, \`datetime\`, ...). Pure data, no network call. Pair with \`get_reserved_schema_slugs\` to avoid native-table collisions, and with \`POST /v1/entity/records/bulk\` for a fast seed of sample rows. Available entity types: product, customer, order, post, comment, message, task, note. Each recipe ships with the matching POST body, a sample \`fields_definition\`, and \`bulk_seed_example\` if applicable.`,
|
|
4541
|
+
inputSchema: {
|
|
4542
|
+
type: "object",
|
|
4543
|
+
properties: {
|
|
4544
|
+
entity_type: {
|
|
4545
|
+
type: "string",
|
|
4546
|
+
enum: ["product", "customer", "order", "post", "comment", "message", "task", "note"],
|
|
4547
|
+
description: "Which common entity pattern do you want a recipe for?",
|
|
4548
|
+
},
|
|
4549
|
+
},
|
|
4550
|
+
required: ["entity_type"],
|
|
4551
|
+
},
|
|
4552
|
+
},
|
|
4553
|
+
{
|
|
4554
|
+
name: "get_chatbot_setup_recipe",
|
|
4555
|
+
description: `End-to-end recipe for wiring a chatbot agent to a knowledge base populated from your custom-entity catalog. Returns the ordered list of API calls (create agent → create KB → upload doc → bind via rag_role) plus a TypeScript snippet (server-side, sk_live_* required). Aimed at the canonical "store assistant" / "product Q&A bot" / "support deflection" use case. Pure data; no network call.`,
|
|
4556
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
4557
|
+
},
|
|
4558
|
+
{
|
|
4559
|
+
name: "get_app_scaffold",
|
|
4560
|
+
description: `Return a complete app scaffold for a common product template (\`pos\`, \`crm\`, \`blog\`, \`task_manager\`, \`notes\`, \`support_inbox\`). Each scaffold emits: the custom-entity schemas to create (with field types pre-decided), the agent(s) + KBs to set up, the typical KPIs and how to compute them (cross-schema search with \`join\` per ADR-0009), and the suggested build order. Designed to short-circuit the agent's "what should I build first" deliberation when the prompt is vibecoder-style ("armame un POS para una tiendita"). Pure data; no network call.`,
|
|
4561
|
+
inputSchema: {
|
|
4562
|
+
type: "object",
|
|
4563
|
+
properties: {
|
|
4564
|
+
template: {
|
|
4565
|
+
type: "string",
|
|
4566
|
+
enum: ["pos", "crm", "blog", "task_manager", "notes", "support_inbox"],
|
|
4567
|
+
description: "Which app template do you want to scaffold?",
|
|
4568
|
+
},
|
|
4569
|
+
},
|
|
4570
|
+
required: ["template"],
|
|
4571
|
+
},
|
|
4572
|
+
},
|
|
3803
4573
|
],
|
|
3804
4574
|
};
|
|
3805
4575
|
});
|
|
@@ -4853,6 +5623,23 @@ type to pick.`,
|
|
|
4853
5623
|
],
|
|
4854
5624
|
};
|
|
4855
5625
|
}
|
|
5626
|
+
case "get_entity_schema_recipe": {
|
|
5627
|
+
const entityType = args.entity_type;
|
|
5628
|
+
return {
|
|
5629
|
+
content: [{ type: "text", text: ENTITY_SCHEMA_RECIPE(entityType) }],
|
|
5630
|
+
};
|
|
5631
|
+
}
|
|
5632
|
+
case "get_chatbot_setup_recipe": {
|
|
5633
|
+
return {
|
|
5634
|
+
content: [{ type: "text", text: CHATBOT_SETUP_RECIPE() }],
|
|
5635
|
+
};
|
|
5636
|
+
}
|
|
5637
|
+
case "get_app_scaffold": {
|
|
5638
|
+
const template = args.template;
|
|
5639
|
+
return {
|
|
5640
|
+
content: [{ type: "text", text: APP_SCAFFOLD(template) }],
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
4856
5643
|
default:
|
|
4857
5644
|
throw new Error(`Unknown tool: ${name}`);
|
|
4858
5645
|
}
|
package/package.json
CHANGED