@genlobe/mcp-server 3.6.1 → 3.7.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 +790 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2069,6 +2069,30 @@ const END_USER_ENDPOINTS = {
|
|
|
2069
2069
|
},
|
|
2070
2070
|
notes: "Three modes (#178 + #350): (1) sk_live_* alone -> tenant-scoped record, created_by_id is NULL (catalog/blog/global config). (2) sk_live_* + end-user JWT -> user-scoped record, created_by_id = user.id (server-side caller acting on behalf of a user). (3) pk_live_* + end-user JWT -> user-scoped record, created_by_id = user.id (canonical 'two-phase' frontend pattern: pk in the browser, JWT after login). pk_live_* WITHOUT a JWT is rejected with 401 — a public key alone is anonymous and can't be the creator of a record."
|
|
2071
2071
|
},
|
|
2072
|
+
{
|
|
2073
|
+
method: "POST",
|
|
2074
|
+
path: "/v1/entity/records/bulk",
|
|
2075
|
+
summary: "Bulk-insert up to 500 records into one schema in one transaction (migration-friendly)",
|
|
2076
|
+
auth: { api_key: "sk_live_* or pk_live_*", jwt: "optional (same modes as POST /v1/entity/records)", headers: "X-Organization-Id required when key is not organization-scoped" },
|
|
2077
|
+
request_body: {
|
|
2078
|
+
schema_id: "uuid (required) - all records in the batch target this schema",
|
|
2079
|
+
records: "array (required) - up to 500 JSON objects. Each is validated against the schema's fields_definition and reference fields (ADR-0009)."
|
|
2080
|
+
},
|
|
2081
|
+
notes: "All-or-nothing transaction: if ANY record fails schema or reference validation, the entire batch is rejected and nothing is persisted. Per-record errors include `record_index` so the agent can fix specific rows and retry. Capped at 500 records per call — for larger migrations, chunk the source dataset. Designed for Supabase → Genlobe table migrations (one source table → one custom_data_schema → one or more bulk chunks). Plan limits are checked once against the batch size; the whole batch fits or the whole batch fails. created_by_id follows the same rule as POST /v1/entity/records (NULL for sk_live_* alone, user.id when an end-user JWT is attached) and is identical for every row in the batch.",
|
|
2082
|
+
example_request: `{
|
|
2083
|
+
"schema_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
2084
|
+
"records": [
|
|
2085
|
+
{ "name": "Sugar 1kg", "price": 2.5, "stock": 100 },
|
|
2086
|
+
{ "name": "Rice 1kg", "price": 3.0, "stock": 50 },
|
|
2087
|
+
{ "name": "Coffee 250g", "price": 6.75, "stock": 30 }
|
|
2088
|
+
]
|
|
2089
|
+
}`,
|
|
2090
|
+
example_response: `{
|
|
2091
|
+
"schema_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
2092
|
+
"inserted_count": 3,
|
|
2093
|
+
"items": [ { "id": "...", "schema_id": "...", "organization_id": "...", "data": {...} }, ... ]
|
|
2094
|
+
}`
|
|
2095
|
+
},
|
|
2072
2096
|
{
|
|
2073
2097
|
method: "GET",
|
|
2074
2098
|
path: "/v1/entity/records/mine",
|
|
@@ -3473,6 +3497,720 @@ has a different shape because of where the API key can safely live.`,
|
|
|
3473
3497
|
const body = sections[appType] ?? sections["unsure"];
|
|
3474
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.`;
|
|
3475
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: "number", required: true, min: 0 },
|
|
3508
|
+
stock: { type: "number", 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: "string", 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: "string", required: false, max_length: 200 },
|
|
3529
|
+
notes: { type: "string", required: false },
|
|
3530
|
+
tags: { type: "array", 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.",
|
|
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: {
|
|
3550
|
+
type: "array",
|
|
3551
|
+
required: true,
|
|
3552
|
+
item_schema: {
|
|
3553
|
+
product_id: {
|
|
3554
|
+
type: "reference",
|
|
3555
|
+
target_schema_slug: "product",
|
|
3556
|
+
required: true,
|
|
3557
|
+
on_delete: "restrict",
|
|
3558
|
+
},
|
|
3559
|
+
name_snapshot: { type: "string", required: true },
|
|
3560
|
+
price_snapshot: { type: "number", required: true, min: 0 },
|
|
3561
|
+
qty: { type: "number", required: true, min: 1 },
|
|
3562
|
+
},
|
|
3563
|
+
},
|
|
3564
|
+
total: { type: "number", required: true, min: 0 },
|
|
3565
|
+
currency: { type: "string", required: false, default: "USD" },
|
|
3566
|
+
paid: { type: "boolean", required: true, default: false },
|
|
3567
|
+
paid_at: { type: "datetime", required: false },
|
|
3568
|
+
notes: { type: "string", required: false },
|
|
3569
|
+
},
|
|
3570
|
+
notes: "Always snapshot `name` and `price` into `items[]` at write time. Reading a 6-month-old order should show the price the customer paid, not the current product price. Cross-schema reads (customer name on the order list) are 1 EXISTS sub-select via POST /v1/entity/records/search with `join` (ADR-0009).",
|
|
3571
|
+
},
|
|
3572
|
+
post: {
|
|
3573
|
+
slug: "post",
|
|
3574
|
+
name: "Blog post / Article",
|
|
3575
|
+
description: "Authored content with title, body, status, tags.",
|
|
3576
|
+
fields_definition: {
|
|
3577
|
+
title: { type: "string", required: true, max_length: 200 },
|
|
3578
|
+
slug: { type: "string", required: true, max_length: 200 },
|
|
3579
|
+
body: { type: "string", required: true },
|
|
3580
|
+
status: {
|
|
3581
|
+
type: "string",
|
|
3582
|
+
required: true,
|
|
3583
|
+
enum: ["draft", "published", "archived"],
|
|
3584
|
+
default: "draft",
|
|
3585
|
+
},
|
|
3586
|
+
tags: { type: "array", required: false },
|
|
3587
|
+
published_at: { type: "datetime", required: false },
|
|
3588
|
+
cover_image_url: { type: "string", required: false },
|
|
3589
|
+
},
|
|
3590
|
+
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.",
|
|
3591
|
+
},
|
|
3592
|
+
comment: {
|
|
3593
|
+
slug: "comment",
|
|
3594
|
+
name: "Comment",
|
|
3595
|
+
description: "User-authored comment that points at another record (post, product, order, anything).",
|
|
3596
|
+
fields_definition: {
|
|
3597
|
+
target_id: {
|
|
3598
|
+
type: "string",
|
|
3599
|
+
required: true,
|
|
3600
|
+
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.",
|
|
3601
|
+
},
|
|
3602
|
+
target_schema: { type: "string", required: true, max_length: 100 },
|
|
3603
|
+
body: { type: "string", required: true },
|
|
3604
|
+
is_deleted: { type: "boolean", required: false, default: false },
|
|
3605
|
+
},
|
|
3606
|
+
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.",
|
|
3607
|
+
},
|
|
3608
|
+
message: {
|
|
3609
|
+
slug: "message",
|
|
3610
|
+
name: "Chat / direct message",
|
|
3611
|
+
description: "A single message in a conversation. Pair with the native `conversations` table (Genlobe AI agents) or roll your own thread entity.",
|
|
3612
|
+
fields_definition: {
|
|
3613
|
+
conversation_id: { type: "string", required: true },
|
|
3614
|
+
sender_user_id: { type: "string", required: false },
|
|
3615
|
+
sender_role: {
|
|
3616
|
+
type: "string",
|
|
3617
|
+
required: true,
|
|
3618
|
+
enum: ["user", "assistant", "system"],
|
|
3619
|
+
},
|
|
3620
|
+
body: { type: "string", required: true },
|
|
3621
|
+
read_at: { type: "datetime", required: false },
|
|
3622
|
+
},
|
|
3623
|
+
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.",
|
|
3624
|
+
},
|
|
3625
|
+
task: {
|
|
3626
|
+
slug: "task",
|
|
3627
|
+
name: "Task / To-do",
|
|
3628
|
+
description: "Single task with title, status, due date, assignee.",
|
|
3629
|
+
fields_definition: {
|
|
3630
|
+
title: { type: "string", required: true, max_length: 200 },
|
|
3631
|
+
description: { type: "string", required: false },
|
|
3632
|
+
status: {
|
|
3633
|
+
type: "string",
|
|
3634
|
+
required: true,
|
|
3635
|
+
enum: ["pending", "in_progress", "completed", "cancelled"],
|
|
3636
|
+
default: "pending",
|
|
3637
|
+
},
|
|
3638
|
+
priority: {
|
|
3639
|
+
type: "string",
|
|
3640
|
+
required: false,
|
|
3641
|
+
enum: ["low", "medium", "high"],
|
|
3642
|
+
default: "medium",
|
|
3643
|
+
},
|
|
3644
|
+
assigned_to_id: {
|
|
3645
|
+
type: "reference",
|
|
3646
|
+
target_schema_slug: "users",
|
|
3647
|
+
required: false,
|
|
3648
|
+
on_delete: "set_null",
|
|
3649
|
+
},
|
|
3650
|
+
due_date: { type: "datetime", required: false },
|
|
3651
|
+
},
|
|
3652
|
+
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.",
|
|
3653
|
+
},
|
|
3654
|
+
note: {
|
|
3655
|
+
slug: "note",
|
|
3656
|
+
name: "Personal note",
|
|
3657
|
+
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.",
|
|
3658
|
+
fields_definition: {
|
|
3659
|
+
title: { type: "string", required: false, max_length: 200 },
|
|
3660
|
+
body: { type: "string", required: true },
|
|
3661
|
+
tags: { type: "array", required: false },
|
|
3662
|
+
is_pinned: { type: "boolean", required: false, default: false },
|
|
3663
|
+
},
|
|
3664
|
+
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).",
|
|
3665
|
+
},
|
|
3666
|
+
};
|
|
3667
|
+
function ENTITY_SCHEMA_RECIPE(entityType) {
|
|
3668
|
+
const recipe = ENTITY_SCHEMA_RECIPES[entityType];
|
|
3669
|
+
if (!recipe) {
|
|
3670
|
+
return `# Unknown entity_type "${entityType}"\n\nAvailable: ${Object.keys(ENTITY_SCHEMA_RECIPES).join(", ")}.`;
|
|
3671
|
+
}
|
|
3672
|
+
const postBody = {
|
|
3673
|
+
name: recipe.name,
|
|
3674
|
+
slug: recipe.slug,
|
|
3675
|
+
description: recipe.description,
|
|
3676
|
+
fields_definition: recipe.fields_definition,
|
|
3677
|
+
};
|
|
3678
|
+
const bulkBody = recipe.bulk_seed_example
|
|
3679
|
+
? {
|
|
3680
|
+
schema_id: "<paste the id returned by POST /v1/entity/schemas above>",
|
|
3681
|
+
records: recipe.bulk_seed_example,
|
|
3682
|
+
}
|
|
3683
|
+
: null;
|
|
3684
|
+
return `# Entity schema recipe — ${recipe.name}
|
|
3685
|
+
|
|
3686
|
+
${recipe.description}
|
|
3687
|
+
|
|
3688
|
+
## Step 1 — Create the schema
|
|
3689
|
+
|
|
3690
|
+
\`\`\`http
|
|
3691
|
+
POST /v1/entity/schemas
|
|
3692
|
+
Content-Type: application/json
|
|
3693
|
+
X-API-Key: <your sk_live_*>
|
|
3694
|
+
X-Organization-Id: <your organization_id>
|
|
3695
|
+
\`\`\`
|
|
3696
|
+
|
|
3697
|
+
Body:
|
|
3698
|
+
\`\`\`json
|
|
3699
|
+
${JSON.stringify(postBody, null, 2)}
|
|
3700
|
+
\`\`\`
|
|
3701
|
+
|
|
3702
|
+
${bulkBody
|
|
3703
|
+
? `## Step 2 — Seed sample rows (bulk)
|
|
3704
|
+
|
|
3705
|
+
\`\`\`http
|
|
3706
|
+
POST /v1/entity/records/bulk
|
|
3707
|
+
Content-Type: application/json
|
|
3708
|
+
X-API-Key: <your sk_live_*>
|
|
3709
|
+
X-Organization-Id: <your organization_id>
|
|
3710
|
+
\`\`\`
|
|
3711
|
+
|
|
3712
|
+
Body:
|
|
3713
|
+
\`\`\`json
|
|
3714
|
+
${JSON.stringify(bulkBody, null, 2)}
|
|
3715
|
+
\`\`\`
|
|
3716
|
+
|
|
3717
|
+
Bulk insert is capped at 500 records per call; chunk larger migrations.
|
|
3718
|
+
All-or-nothing: any validation failure rolls back the whole batch.
|
|
3719
|
+
`
|
|
3720
|
+
: ""}
|
|
3721
|
+
${recipe.notes ? `## Notes\n\n${recipe.notes}\n` : ""}
|
|
3722
|
+
|
|
3723
|
+
---
|
|
3724
|
+
|
|
3725
|
+
**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.`;
|
|
3726
|
+
}
|
|
3727
|
+
// =============================================================================
|
|
3728
|
+
// Chatbot setup recipe (v3.7.0) — closes G2.
|
|
3729
|
+
// =============================================================================
|
|
3730
|
+
function CHATBOT_SETUP_RECIPE() {
|
|
3731
|
+
return `# Chatbot setup recipe — agent + KB bound by \`rag_role\`
|
|
3732
|
+
|
|
3733
|
+
End-to-end recipe to wire a chatbot agent that answers from a knowledge base
|
|
3734
|
+
populated by a snapshot of your custom-entity catalog (typical "store
|
|
3735
|
+
assistant" / "product Q&A" / "support deflection" use case).
|
|
3736
|
+
|
|
3737
|
+
## Architecture in one diagram
|
|
3738
|
+
|
|
3739
|
+
\`\`\`
|
|
3740
|
+
Custom-entity catalog
|
|
3741
|
+
(e.g. products)
|
|
3742
|
+
│
|
|
3743
|
+
│ 1. snapshot to plain text
|
|
3744
|
+
▼
|
|
3745
|
+
KnowledgeBase (per-Org)
|
|
3746
|
+
│
|
|
3747
|
+
│ 2. uploaded as a Document
|
|
3748
|
+
▼
|
|
3749
|
+
Agent (rag_role bound to KB) ←─ end-user chats
|
|
3750
|
+
/v1/user/agents/{id}/chat
|
|
3751
|
+
\`\`\`
|
|
3752
|
+
|
|
3753
|
+
## Step-by-step
|
|
3754
|
+
|
|
3755
|
+
### 1. Create the KnowledgeBase
|
|
3756
|
+
|
|
3757
|
+
\`\`\`http
|
|
3758
|
+
POST /v1/organization-admin/{org_id}/knowledge-bases
|
|
3759
|
+
\`\`\`
|
|
3760
|
+
|
|
3761
|
+
Body:
|
|
3762
|
+
\`\`\`json
|
|
3763
|
+
{
|
|
3764
|
+
"name": "Vendy catalog",
|
|
3765
|
+
"description": "Snapshot of products, updated on demand",
|
|
3766
|
+
"rag_role": "product_catalog"
|
|
3767
|
+
}
|
|
3768
|
+
\`\`\`
|
|
3769
|
+
|
|
3770
|
+
Save the returned \`kb_id\`. The \`rag_role\` value is the binding key — the
|
|
3771
|
+
agent will reference the same role string to find this KB at chat time.
|
|
3772
|
+
|
|
3773
|
+
### 2. Snapshot your catalog and upload it as a Document
|
|
3774
|
+
|
|
3775
|
+
Pull every product record:
|
|
3776
|
+
|
|
3777
|
+
\`\`\`http
|
|
3778
|
+
POST /v1/entity/records/search
|
|
3779
|
+
\`\`\`
|
|
3780
|
+
|
|
3781
|
+
Body: \`{ "schema_id": "<your product schema id>", "query": { "is_active": true } }\`
|
|
3782
|
+
|
|
3783
|
+
Format each row as one paragraph the LLM can read:
|
|
3784
|
+
|
|
3785
|
+
\`\`\`text
|
|
3786
|
+
Product: Sugar 1kg
|
|
3787
|
+
Price: $2.50
|
|
3788
|
+
Stock: 100
|
|
3789
|
+
Category: groceries
|
|
3790
|
+
Description: white sugar in 1kg bags
|
|
3791
|
+
---
|
|
3792
|
+
Product: Rice 1kg
|
|
3793
|
+
...
|
|
3794
|
+
\`\`\`
|
|
3795
|
+
|
|
3796
|
+
Upload as a document:
|
|
3797
|
+
|
|
3798
|
+
\`\`\`http
|
|
3799
|
+
POST /v1/organization-admin/{org_id}/knowledge-bases/{kb_id}/documents
|
|
3800
|
+
\`\`\`
|
|
3801
|
+
|
|
3802
|
+
Body:
|
|
3803
|
+
\`\`\`json
|
|
3804
|
+
{
|
|
3805
|
+
"title": "Catalog snapshot 2026-05-26",
|
|
3806
|
+
"content": "<the multi-paragraph text from above>",
|
|
3807
|
+
"metadata": { "source": "catalog_snapshot", "snapshotted_at": "<ISO timestamp>" }
|
|
3808
|
+
}
|
|
3809
|
+
\`\`\`
|
|
3810
|
+
|
|
3811
|
+
### 3. Create the Agent
|
|
3812
|
+
|
|
3813
|
+
\`\`\`http
|
|
3814
|
+
POST /v1/agents
|
|
3815
|
+
\`\`\`
|
|
3816
|
+
|
|
3817
|
+
Body:
|
|
3818
|
+
\`\`\`json
|
|
3819
|
+
{
|
|
3820
|
+
"name": "Vendy Assistant",
|
|
3821
|
+
"description": "Answers customer questions about the store catalog.",
|
|
3822
|
+
"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.",
|
|
3823
|
+
"is_public": true,
|
|
3824
|
+
"rag_role": "product_catalog"
|
|
3825
|
+
}
|
|
3826
|
+
\`\`\`
|
|
3827
|
+
|
|
3828
|
+
\`is_public: true\` lets every end-user of the Org talk to the same agent (no
|
|
3829
|
+
per-user fork). \`rag_role: "product_catalog"\` is the binding to the KB
|
|
3830
|
+
created in step 1.
|
|
3831
|
+
|
|
3832
|
+
### 4. End-user chat (frontend)
|
|
3833
|
+
|
|
3834
|
+
\`\`\`http
|
|
3835
|
+
POST /v1/user/agents/{agent_id}/chat
|
|
3836
|
+
\`\`\`
|
|
3837
|
+
|
|
3838
|
+
Body:
|
|
3839
|
+
\`\`\`json
|
|
3840
|
+
{
|
|
3841
|
+
"message": "do you have rice?",
|
|
3842
|
+
"conversation_id": "<optional, persist in sessionStorage to keep context>"
|
|
3843
|
+
}
|
|
3844
|
+
\`\`\`
|
|
3845
|
+
|
|
3846
|
+
Returns the assistant's response plus a \`conversation_id\` you should pass on
|
|
3847
|
+
subsequent messages to keep the same thread.
|
|
3848
|
+
|
|
3849
|
+
## Refresh policy
|
|
3850
|
+
|
|
3851
|
+
When products change (price update, new SKU), regenerate the snapshot text
|
|
3852
|
+
and either:
|
|
3853
|
+
|
|
3854
|
+
- **Replace the document** (\`DELETE /v1/organization-admin/{org_id}/knowledge-bases/{kb_id}/documents/{doc_id}\` then re-upload) — simplest.
|
|
3855
|
+
- **Append a delta document** (\`POST\` a new document with only the changes) — faster but you need to manage stale entries.
|
|
3856
|
+
|
|
3857
|
+
For an MVP the first option is what we recommend.
|
|
3858
|
+
|
|
3859
|
+
## Server-side TypeScript snippet
|
|
3860
|
+
|
|
3861
|
+
\`\`\`typescript
|
|
3862
|
+
// lib/genlobe.server.ts
|
|
3863
|
+
import 'server-only';
|
|
3864
|
+
|
|
3865
|
+
const API = process.env.SAAS_API_URL!;
|
|
3866
|
+
const KEY = process.env.SAAS_API_KEY!; // sk_live_*
|
|
3867
|
+
const ORG = process.env.SAAS_ORGANIZATION_ID!;
|
|
3868
|
+
|
|
3869
|
+
async function api(path: string, body: any, jwt?: string) {
|
|
3870
|
+
const res = await fetch(\`\${API}\${path}\`, {
|
|
3871
|
+
method: 'POST',
|
|
3872
|
+
headers: {
|
|
3873
|
+
'Content-Type': 'application/json',
|
|
3874
|
+
'X-API-Key': KEY,
|
|
3875
|
+
'X-Organization-Id': ORG,
|
|
3876
|
+
...(jwt ? { 'Authorization': \`Bearer \${jwt}\` } : {}),
|
|
3877
|
+
},
|
|
3878
|
+
body: JSON.stringify(body),
|
|
3879
|
+
});
|
|
3880
|
+
if (!res.ok) throw new Error(\`\${res.status} \${await res.text()}\`);
|
|
3881
|
+
return res.json();
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
export async function refreshCatalogKB(productSchemaId: string, kbId: string, existingDocId?: string) {
|
|
3885
|
+
const { records } = await api('/v1/entity/records/search', {
|
|
3886
|
+
schema_id: productSchemaId,
|
|
3887
|
+
query: { is_active: true },
|
|
3888
|
+
});
|
|
3889
|
+
const content = records
|
|
3890
|
+
.map((r: any) =>
|
|
3891
|
+
\`Product: \${r.data.name}\\nPrice: $\${r.data.price}\\nStock: \${r.data.stock}\\nCategory: \${r.data.category ?? 'n/a'}\\nDescription: \${r.data.description ?? ''}\`
|
|
3892
|
+
)
|
|
3893
|
+
.join('\\n---\\n');
|
|
3894
|
+
|
|
3895
|
+
if (existingDocId) {
|
|
3896
|
+
await fetch(\`\${API}/v1/organization-admin/\${ORG}/knowledge-bases/\${kbId}/documents/\${existingDocId}\`, {
|
|
3897
|
+
method: 'DELETE',
|
|
3898
|
+
headers: { 'X-API-Key': KEY, 'X-Organization-Id': ORG },
|
|
3899
|
+
});
|
|
3900
|
+
}
|
|
3901
|
+
return api(\`/v1/organization-admin/\${ORG}/knowledge-bases/\${kbId}/documents\`, {
|
|
3902
|
+
title: \`Catalog snapshot \${new Date().toISOString()}\`,
|
|
3903
|
+
content,
|
|
3904
|
+
metadata: { source: 'catalog_snapshot' },
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
export async function chatWithAgent(agentId: string, message: string, conversationId?: string) {
|
|
3909
|
+
return api(\`/v1/user/agents/\${agentId}/chat\`, {
|
|
3910
|
+
message,
|
|
3911
|
+
...(conversationId ? { conversation_id: conversationId } : {}),
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
\`\`\`
|
|
3915
|
+
|
|
3916
|
+
## Notes
|
|
3917
|
+
|
|
3918
|
+
- \`POST /v1/agents\` is the Tenant Dashboard endpoint — when configuring from
|
|
3919
|
+
your scaffolding script, use \`sk_live_*\`. End-user chat (\`/v1/user/agents/{id}/chat\`)
|
|
3920
|
+
is reached with \`pk_live_*\` + the end-user's JWT.
|
|
3921
|
+
- Tool calling vs RAG: for MVP / low-frequency catalog updates, RAG via KB is
|
|
3922
|
+
simpler. Switch to tool calling when the catalog updates faster than the
|
|
3923
|
+
refresh cadence you can sustain.
|
|
3924
|
+
- The bot does not write to the catalog and does not take orders unless you
|
|
3925
|
+
add a separate tool / API path. Keep the system prompt strict.`;
|
|
3926
|
+
}
|
|
3927
|
+
const APP_SCAFFOLDS = {
|
|
3928
|
+
pos: {
|
|
3929
|
+
template: "pos",
|
|
3930
|
+
title: "POS / small-store register",
|
|
3931
|
+
description: "A storefront with a chatbot for product questions and an owner dashboard for products, customers, sales.",
|
|
3932
|
+
entities: [
|
|
3933
|
+
{ slug: "product", recipe_key: "product", purpose: "Catalog the owner manages." },
|
|
3934
|
+
{
|
|
3935
|
+
slug: "customer",
|
|
3936
|
+
recipe_key: "customer",
|
|
3937
|
+
purpose: "Store-side customer record (NOT a Genlobe end-user).",
|
|
3938
|
+
},
|
|
3939
|
+
{
|
|
3940
|
+
slug: "order",
|
|
3941
|
+
recipe_key: "order",
|
|
3942
|
+
purpose: "Completed sales with snapshotted line items (FK to product + customer).",
|
|
3943
|
+
},
|
|
3944
|
+
],
|
|
3945
|
+
agents: [
|
|
3946
|
+
{
|
|
3947
|
+
name: "Store Assistant",
|
|
3948
|
+
rag_role: "product_catalog",
|
|
3949
|
+
purpose: "Answers customer questions about the product catalog. Read-only.",
|
|
3950
|
+
},
|
|
3951
|
+
],
|
|
3952
|
+
kbs: [{ rag_role: "product_catalog", source: "Snapshot of `product` records" }],
|
|
3953
|
+
kpis: [
|
|
3954
|
+
{
|
|
3955
|
+
name: "Today's revenue",
|
|
3956
|
+
how: "POST /v1/entity/records/search with `query: { paid: true, paid_at: { operator: 'gte', value: <today_iso> } }`, sum `data.total` client-side.",
|
|
3957
|
+
},
|
|
3958
|
+
{
|
|
3959
|
+
name: "Top product this month",
|
|
3960
|
+
how: "Search orders, flatten `items[]`, group by `product_id`, sum `qty`. Cross-schema `join` with `product` to get the name in the same call.",
|
|
3961
|
+
},
|
|
3962
|
+
{
|
|
3963
|
+
name: "New customers this week",
|
|
3964
|
+
how: "Search customers with `created_at` filter; show count + names.",
|
|
3965
|
+
},
|
|
3966
|
+
],
|
|
3967
|
+
build_order: [
|
|
3968
|
+
"1. Next.js 16 app router + Tailwind + shadcn. Add `lib/genlobe.server.ts` with `import 'server-only'`.",
|
|
3969
|
+
"2. Bootstrap script: create `product`, `customer`, `order` schemas via `get_entity_schema_recipe`. Bulk-seed 5-10 sample products.",
|
|
3970
|
+
"3. Owner login: `/admin/login` → POST /v1/auth/login server-side → cookie httpOnly with JWT.",
|
|
3971
|
+
"4. Owner CRUD products (single end-to-end vertical slice first).",
|
|
3972
|
+
"5. Sales form (`/admin/sales/new`): customer autocomplete, multi-product line items, total auto-calc, POST one `order` record.",
|
|
3973
|
+
"6. Sales list + detail.",
|
|
3974
|
+
"7. Customers list + per-customer purchase history (cross-schema join from order → customer).",
|
|
3975
|
+
"8. Dashboard KPIs (search + sum client-side; switch to server-side aggregates later if data grows).",
|
|
3976
|
+
"9. Public storefront `/` + `/chat`. Bot via `get_chatbot_setup_recipe`.",
|
|
3977
|
+
"10. Catalog refresh button in `/admin` (regenerates the KB document).",
|
|
3978
|
+
],
|
|
3979
|
+
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.",
|
|
3980
|
+
},
|
|
3981
|
+
crm: {
|
|
3982
|
+
template: "crm",
|
|
3983
|
+
title: "Lightweight CRM",
|
|
3984
|
+
description: "Track contacts, deals, notes, and a sales pipeline. No marketing automation, no email integration — that's later.",
|
|
3985
|
+
entities: [
|
|
3986
|
+
{ slug: "customer", recipe_key: "customer", purpose: "Contact records." },
|
|
3987
|
+
{
|
|
3988
|
+
slug: "deal",
|
|
3989
|
+
recipe_key: "order",
|
|
3990
|
+
purpose: "Reuse the `order` recipe but rename to `deal`; line items become deliverables and `paid` becomes `closed_won`.",
|
|
3991
|
+
},
|
|
3992
|
+
{
|
|
3993
|
+
slug: "note",
|
|
3994
|
+
recipe_key: "note",
|
|
3995
|
+
purpose: "Free-text notes per contact (rename `note` → `interaction` if you want to scope it to the contact).",
|
|
3996
|
+
},
|
|
3997
|
+
],
|
|
3998
|
+
agents: [
|
|
3999
|
+
{
|
|
4000
|
+
name: "CRM Helper",
|
|
4001
|
+
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.",
|
|
4002
|
+
},
|
|
4003
|
+
],
|
|
4004
|
+
kbs: [],
|
|
4005
|
+
kpis: [
|
|
4006
|
+
{ name: "Open pipeline value", how: "Search deals where `closed_won: false`, sum `total`." },
|
|
4007
|
+
{
|
|
4008
|
+
name: "Conversion rate",
|
|
4009
|
+
how: "Count deals `closed_won: true` / total deals over a period.",
|
|
4010
|
+
},
|
|
4011
|
+
{
|
|
4012
|
+
name: "Stale contacts",
|
|
4013
|
+
how: "Customers with no `note` records in the last 30 days (cross-schema NOT-EXISTS).",
|
|
4014
|
+
},
|
|
4015
|
+
],
|
|
4016
|
+
build_order: [
|
|
4017
|
+
"1. Same bootstrap as POS.",
|
|
4018
|
+
"2. Customer CRUD (list + detail + edit).",
|
|
4019
|
+
"3. Notes per customer (reference field to customer).",
|
|
4020
|
+
"4. Deals pipeline view (kanban by `status`).",
|
|
4021
|
+
"5. CRM Helper agent (no KB) for drafting follow-ups.",
|
|
4022
|
+
],
|
|
4023
|
+
notes: "If you want incoming email parsing, that's a Stage-3 deploy capability — not in MVP.",
|
|
4024
|
+
},
|
|
4025
|
+
blog: {
|
|
4026
|
+
template: "blog",
|
|
4027
|
+
title: "Multi-author blog",
|
|
4028
|
+
description: "Posts, comments, tags. Public read, authenticated write.",
|
|
4029
|
+
entities: [
|
|
4030
|
+
{ slug: "post", recipe_key: "post", purpose: "Articles." },
|
|
4031
|
+
{ slug: "comment", recipe_key: "comment", purpose: "User comments on posts." },
|
|
4032
|
+
],
|
|
4033
|
+
agents: [
|
|
4034
|
+
{
|
|
4035
|
+
name: "Blog Recommender",
|
|
4036
|
+
rag_role: "blog_archive",
|
|
4037
|
+
purpose: "Suggests related posts based on what the reader is browsing.",
|
|
4038
|
+
},
|
|
4039
|
+
],
|
|
4040
|
+
kbs: [{ rag_role: "blog_archive", source: "Snapshot of published `post` records" }],
|
|
4041
|
+
kpis: [
|
|
4042
|
+
{ name: "Posts published this month", how: "Search posts where `status: published`, count." },
|
|
4043
|
+
{
|
|
4044
|
+
name: "Most commented post",
|
|
4045
|
+
how: "Search comments, group by `target_id`, count, cross-schema join with `post` for the title.",
|
|
4046
|
+
},
|
|
4047
|
+
],
|
|
4048
|
+
build_order: [
|
|
4049
|
+
"1. Bootstrap.",
|
|
4050
|
+
"2. Post CRUD (admin only — gate with role check).",
|
|
4051
|
+
"3. Public reader view (`/blog/[slug]`) with comments form.",
|
|
4052
|
+
"4. Comments moderation page.",
|
|
4053
|
+
"5. Recommender bot via KB.",
|
|
4054
|
+
],
|
|
4055
|
+
},
|
|
4056
|
+
task_manager: {
|
|
4057
|
+
template: "task_manager",
|
|
4058
|
+
title: "Personal / team task manager",
|
|
4059
|
+
description: "To-do list with assignees, due dates, and status. Per-user view by default (ADR-0007 row-level scope).",
|
|
4060
|
+
entities: [{ slug: "task", recipe_key: "task", purpose: "Tasks." }],
|
|
4061
|
+
agents: [
|
|
4062
|
+
{
|
|
4063
|
+
name: "Task Coach",
|
|
4064
|
+
purpose: "Suggests next-best task based on due dates and priorities (no KB; uses context).",
|
|
4065
|
+
},
|
|
4066
|
+
],
|
|
4067
|
+
kbs: [],
|
|
4068
|
+
kpis: [
|
|
4069
|
+
{
|
|
4070
|
+
name: "My open tasks",
|
|
4071
|
+
how: "GET /v1/entity/records/mine?schema_id=<task> — returns only the caller's tasks via row-level scope, no extra filter.",
|
|
4072
|
+
},
|
|
4073
|
+
{
|
|
4074
|
+
name: "Overdue tasks (team)",
|
|
4075
|
+
how: "Search tasks where `due_date < now` AND `status != completed`. Elevated role only.",
|
|
4076
|
+
},
|
|
4077
|
+
],
|
|
4078
|
+
build_order: [
|
|
4079
|
+
"1. Bootstrap.",
|
|
4080
|
+
"2. End-user signup + login (`pk_live_*` + JWT flow).",
|
|
4081
|
+
"3. `/tasks` list using `GET /v1/entity/records/mine`.",
|
|
4082
|
+
"4. Create / edit task.",
|
|
4083
|
+
"5. Coach bot.",
|
|
4084
|
+
],
|
|
4085
|
+
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.",
|
|
4086
|
+
},
|
|
4087
|
+
notes: {
|
|
4088
|
+
template: "notes",
|
|
4089
|
+
title: "Personal notes app",
|
|
4090
|
+
description: "Free-text notes per user, scoped to the author. Inspired by Apple Notes / Bear.",
|
|
4091
|
+
entities: [{ slug: "note", recipe_key: "note", purpose: "Notes." }],
|
|
4092
|
+
agents: [
|
|
4093
|
+
{
|
|
4094
|
+
name: "Notes Search",
|
|
4095
|
+
rag_role: "user_notes",
|
|
4096
|
+
purpose: "Semantic search across the user's notes. Per-user KB scoped via row-level scope on the search side.",
|
|
4097
|
+
},
|
|
4098
|
+
],
|
|
4099
|
+
kbs: [
|
|
4100
|
+
{
|
|
4101
|
+
rag_role: "user_notes",
|
|
4102
|
+
source: "Each user's notes uploaded as one document per note. Could also be one consolidated doc per user, refreshed on each save.",
|
|
4103
|
+
},
|
|
4104
|
+
],
|
|
4105
|
+
kpis: [
|
|
4106
|
+
{ name: "Total notes", how: "GET /v1/entity/records/mine — count." },
|
|
4107
|
+
{ name: "Pinned", how: "GET /v1/entity/records/mine with `is_pinned: true` filter." },
|
|
4108
|
+
],
|
|
4109
|
+
build_order: [
|
|
4110
|
+
"1. Bootstrap + end-user auth.",
|
|
4111
|
+
"2. Note CRUD (`/v1/entity/records/mine` for the list).",
|
|
4112
|
+
"3. Search bot per user.",
|
|
4113
|
+
"4. Pin / tag filters.",
|
|
4114
|
+
],
|
|
4115
|
+
},
|
|
4116
|
+
support_inbox: {
|
|
4117
|
+
template: "support_inbox",
|
|
4118
|
+
title: "Customer support inbox",
|
|
4119
|
+
description: "Capture support tickets, route to agents, let an AI first-responder handle FAQs before escalation.",
|
|
4120
|
+
entities: [
|
|
4121
|
+
{
|
|
4122
|
+
slug: "ticket",
|
|
4123
|
+
recipe_key: "task",
|
|
4124
|
+
purpose: "Reuse `task` recipe. Rename `task` → `ticket`. `assigned_to_id` becomes the support agent (human end-user).",
|
|
4125
|
+
},
|
|
4126
|
+
{
|
|
4127
|
+
slug: "message",
|
|
4128
|
+
recipe_key: "message",
|
|
4129
|
+
purpose: "Conversation thread per ticket.",
|
|
4130
|
+
},
|
|
4131
|
+
],
|
|
4132
|
+
agents: [
|
|
4133
|
+
{
|
|
4134
|
+
name: "First Responder",
|
|
4135
|
+
rag_role: "support_kb",
|
|
4136
|
+
purpose: "Answers from the support knowledge base before escalating to a human.",
|
|
4137
|
+
},
|
|
4138
|
+
],
|
|
4139
|
+
kbs: [
|
|
4140
|
+
{
|
|
4141
|
+
rag_role: "support_kb",
|
|
4142
|
+
source: "Help docs / FAQs. Upload manually or sync from your existing help center.",
|
|
4143
|
+
},
|
|
4144
|
+
],
|
|
4145
|
+
kpis: [
|
|
4146
|
+
{ name: "Open tickets", how: "Search tickets where `status != completed`." },
|
|
4147
|
+
{
|
|
4148
|
+
name: "Avg time to first response",
|
|
4149
|
+
how: "For each ticket, find the first `message` where `sender_role != 'user'`, compute delta. Aggregate client-side.",
|
|
4150
|
+
},
|
|
4151
|
+
],
|
|
4152
|
+
build_order: [
|
|
4153
|
+
"1. Bootstrap.",
|
|
4154
|
+
"2. Public form (`/contact`) that creates a `ticket` + an initial `message`.",
|
|
4155
|
+
"3. Internal queue view (`/support/inbox`).",
|
|
4156
|
+
"4. First Responder bot, with escalation flag.",
|
|
4157
|
+
"5. SLA dashboard.",
|
|
4158
|
+
],
|
|
4159
|
+
},
|
|
4160
|
+
};
|
|
4161
|
+
function APP_SCAFFOLD(template) {
|
|
4162
|
+
const s = APP_SCAFFOLDS[template];
|
|
4163
|
+
if (!s) {
|
|
4164
|
+
return `# Unknown template "${template}"\n\nAvailable: ${Object.keys(APP_SCAFFOLDS).join(", ")}.`;
|
|
4165
|
+
}
|
|
4166
|
+
const entitySection = s.entities
|
|
4167
|
+
.map((e) => `- **\`${e.slug}\`** — ${e.purpose} Get the schema recipe with \`get_entity_schema_recipe({ entity_type: "${e.recipe_key}" })\`.`)
|
|
4168
|
+
.join("\n");
|
|
4169
|
+
const agentSection = s.agents
|
|
4170
|
+
.map((a) => `- **${a.name}**${a.rag_role ? ` (\`rag_role: "${a.rag_role}"\`)` : ""} — ${a.purpose}`)
|
|
4171
|
+
.join("\n");
|
|
4172
|
+
const kbSection = s.kbs.length
|
|
4173
|
+
? s.kbs
|
|
4174
|
+
.map((kb) => `- \`rag_role: "${kb.rag_role}"\` ← ${kb.source}`)
|
|
4175
|
+
.join("\n")
|
|
4176
|
+
: "_(none — agents work from conversation context)_";
|
|
4177
|
+
const kpiSection = s.kpis
|
|
4178
|
+
.map((k) => `- **${k.name}** — ${k.how}`)
|
|
4179
|
+
.join("\n");
|
|
4180
|
+
const orderSection = s.build_order.map((step) => step).join("\n");
|
|
4181
|
+
return `# App scaffold — ${s.title}
|
|
4182
|
+
|
|
4183
|
+
${s.description}
|
|
4184
|
+
|
|
4185
|
+
## Custom entities to create
|
|
4186
|
+
|
|
4187
|
+
${entitySection}
|
|
4188
|
+
|
|
4189
|
+
## Agents to create
|
|
4190
|
+
|
|
4191
|
+
${agentSection}
|
|
4192
|
+
|
|
4193
|
+
## Knowledge bases
|
|
4194
|
+
|
|
4195
|
+
${kbSection}
|
|
4196
|
+
|
|
4197
|
+
## Typical KPIs and how to compute them
|
|
4198
|
+
|
|
4199
|
+
${kpiSection}
|
|
4200
|
+
|
|
4201
|
+
## Suggested build order
|
|
4202
|
+
|
|
4203
|
+
${orderSection}
|
|
4204
|
+
|
|
4205
|
+
${s.notes ? `## Notes\n\n${s.notes}\n` : ""}
|
|
4206
|
+
|
|
4207
|
+
---
|
|
4208
|
+
|
|
4209
|
+
**Next calls**:
|
|
4210
|
+
- For each entity above, call \`get_entity_schema_recipe({ entity_type: "..." })\` and POST the result.
|
|
4211
|
+
- For the chatbot wiring, call \`get_chatbot_setup_recipe()\`.
|
|
4212
|
+
- For the auth shape (sk_live_* / pk_live_* + cookies), call \`get_authentication_flow()\` and \`get_security_guide()\`.`;
|
|
4213
|
+
}
|
|
3476
4214
|
// =============================================================================
|
|
3477
4215
|
// MCP Server Implementation
|
|
3478
4216
|
// =============================================================================
|
|
@@ -3776,6 +4514,41 @@ developer says "I want to build X" before writing code.`,
|
|
|
3776
4514
|
required: ["app_type"],
|
|
3777
4515
|
},
|
|
3778
4516
|
},
|
|
4517
|
+
{
|
|
4518
|
+
name: "get_entity_schema_recipe",
|
|
4519
|
+
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.`,
|
|
4520
|
+
inputSchema: {
|
|
4521
|
+
type: "object",
|
|
4522
|
+
properties: {
|
|
4523
|
+
entity_type: {
|
|
4524
|
+
type: "string",
|
|
4525
|
+
enum: ["product", "customer", "order", "post", "comment", "message", "task", "note"],
|
|
4526
|
+
description: "Which common entity pattern do you want a recipe for?",
|
|
4527
|
+
},
|
|
4528
|
+
},
|
|
4529
|
+
required: ["entity_type"],
|
|
4530
|
+
},
|
|
4531
|
+
},
|
|
4532
|
+
{
|
|
4533
|
+
name: "get_chatbot_setup_recipe",
|
|
4534
|
+
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.`,
|
|
4535
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
4536
|
+
},
|
|
4537
|
+
{
|
|
4538
|
+
name: "get_app_scaffold",
|
|
4539
|
+
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.`,
|
|
4540
|
+
inputSchema: {
|
|
4541
|
+
type: "object",
|
|
4542
|
+
properties: {
|
|
4543
|
+
template: {
|
|
4544
|
+
type: "string",
|
|
4545
|
+
enum: ["pos", "crm", "blog", "task_manager", "notes", "support_inbox"],
|
|
4546
|
+
description: "Which app template do you want to scaffold?",
|
|
4547
|
+
},
|
|
4548
|
+
},
|
|
4549
|
+
required: ["template"],
|
|
4550
|
+
},
|
|
4551
|
+
},
|
|
3779
4552
|
],
|
|
3780
4553
|
};
|
|
3781
4554
|
});
|
|
@@ -4829,6 +5602,23 @@ type to pick.`,
|
|
|
4829
5602
|
],
|
|
4830
5603
|
};
|
|
4831
5604
|
}
|
|
5605
|
+
case "get_entity_schema_recipe": {
|
|
5606
|
+
const entityType = args.entity_type;
|
|
5607
|
+
return {
|
|
5608
|
+
content: [{ type: "text", text: ENTITY_SCHEMA_RECIPE(entityType) }],
|
|
5609
|
+
};
|
|
5610
|
+
}
|
|
5611
|
+
case "get_chatbot_setup_recipe": {
|
|
5612
|
+
return {
|
|
5613
|
+
content: [{ type: "text", text: CHATBOT_SETUP_RECIPE() }],
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
case "get_app_scaffold": {
|
|
5617
|
+
const template = args.template;
|
|
5618
|
+
return {
|
|
5619
|
+
content: [{ type: "text", text: APP_SCAFFOLD(template) }],
|
|
5620
|
+
};
|
|
5621
|
+
}
|
|
4832
5622
|
default:
|
|
4833
5623
|
throw new Error(`Unknown tool: ${name}`);
|
|
4834
5624
|
}
|
package/package.json
CHANGED