@centrali-io/centrali-mcp 4.2.0 → 4.2.2
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/README.md +25 -1
- package/dist/index.js +4 -0
- package/dist/tools/compute.js +323 -0
- package/dist/tools/describe.d.ts +10 -0
- package/dist/tools/describe.js +1485 -0
- package/dist/tools/orchestrations.js +146 -0
- package/dist/tools/pages.d.ts +3 -0
- package/dist/tools/pages.js +477 -0
- package/dist/tools/smart-queries.js +152 -0
- package/dist/tools/structures.js +117 -0
- package/package.json +2 -2
- package/src/index.ts +4 -0
- package/src/tools/compute.ts +371 -0
- package/src/tools/describe.ts +1798 -0
- package/src/tools/orchestrations.ts +167 -0
- package/src/tools/pages.ts +573 -0
- package/src/tools/smart-queries.ts +175 -0
- package/src/tools/structures.ts +123 -0
|
@@ -0,0 +1,1485 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerDescribeTools = registerDescribeTools;
|
|
13
|
+
/**
|
|
14
|
+
* Introspection tools that teach AI assistants the valid shapes, schemas,
|
|
15
|
+
* and domain concepts for every Centrali API. These tools return static
|
|
16
|
+
* documentation — no network calls, no auth required.
|
|
17
|
+
*
|
|
18
|
+
* Pattern inspired by Supabase MCP (list_tables / get_schemas) and
|
|
19
|
+
* Stripe MCP (search_stripe_documentation).
|
|
20
|
+
*/
|
|
21
|
+
function registerDescribeTools(server) {
|
|
22
|
+
// ── Master describe tool ────────────────────────────────────────────
|
|
23
|
+
server.tool("describe_centrali", "Get an overview of all Centrali platform capabilities and API domains. Call this first to understand what Centrali can do and which describe_* tool to call for deeper schema details.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
return ({
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: JSON.stringify({
|
|
29
|
+
platform: "Centrali",
|
|
30
|
+
description: "Centrali is a low-code backend platform. You define data structures (collections), write compute functions, build orchestration workflows, and publish pages — all through APIs.",
|
|
31
|
+
domains: {
|
|
32
|
+
collections: {
|
|
33
|
+
summary: "Data schemas (formerly 'structures'). Define the shape of your data — fields, types, constraints, references.",
|
|
34
|
+
describeWith: "describe_collections",
|
|
35
|
+
tools: [
|
|
36
|
+
"list_collections",
|
|
37
|
+
"get_collection",
|
|
38
|
+
"create_collection",
|
|
39
|
+
"update_collection",
|
|
40
|
+
"delete_collection",
|
|
41
|
+
"list_structures (deprecated)",
|
|
42
|
+
"get_structure (deprecated)",
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
records: {
|
|
46
|
+
summary: "CRUD operations on data stored in collections. Supports filtering, sorting, pagination, field expansion, upsert, and soft/hard delete.",
|
|
47
|
+
describeWith: "describe_records",
|
|
48
|
+
tools: [
|
|
49
|
+
"query_records",
|
|
50
|
+
"get_record",
|
|
51
|
+
"create_record",
|
|
52
|
+
"update_record",
|
|
53
|
+
"delete_record",
|
|
54
|
+
"upsert_record",
|
|
55
|
+
"get_records_by_ids",
|
|
56
|
+
"restore_record",
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
search: {
|
|
60
|
+
summary: "Full-text search across records powered by Meilisearch. Can filter by collection.",
|
|
61
|
+
describeWith: "describe_search",
|
|
62
|
+
tools: ["search_records"],
|
|
63
|
+
},
|
|
64
|
+
compute: {
|
|
65
|
+
summary: "Server-side JavaScript functions with triggers (on-demand, event-driven, scheduled, webhook).",
|
|
66
|
+
describeWith: "describe_compute",
|
|
67
|
+
tools: [
|
|
68
|
+
"list_functions",
|
|
69
|
+
"get_function",
|
|
70
|
+
"create_function",
|
|
71
|
+
"update_function",
|
|
72
|
+
"delete_function",
|
|
73
|
+
"test_function",
|
|
74
|
+
"list_triggers",
|
|
75
|
+
"get_trigger",
|
|
76
|
+
"create_trigger",
|
|
77
|
+
"update_trigger",
|
|
78
|
+
"delete_trigger",
|
|
79
|
+
"invoke_trigger",
|
|
80
|
+
"pause_trigger",
|
|
81
|
+
"resume_trigger",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
smart_queries: {
|
|
85
|
+
summary: "Reusable, parameterized queries defined in the console. Support {{variable}} substitution.",
|
|
86
|
+
describeWith: "describe_smart_queries",
|
|
87
|
+
tools: [
|
|
88
|
+
"list_smart_queries",
|
|
89
|
+
"get_smart_query",
|
|
90
|
+
"create_smart_query",
|
|
91
|
+
"update_smart_query",
|
|
92
|
+
"delete_smart_query",
|
|
93
|
+
"execute_smart_query",
|
|
94
|
+
"test_smart_query",
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
orchestrations: {
|
|
98
|
+
summary: "Multi-step workflows that chain compute functions. Support sequential execution with input/output passing between steps.",
|
|
99
|
+
describeWith: "describe_orchestrations",
|
|
100
|
+
tools: [
|
|
101
|
+
"list_orchestrations",
|
|
102
|
+
"get_orchestration",
|
|
103
|
+
"create_orchestration",
|
|
104
|
+
"update_orchestration",
|
|
105
|
+
"delete_orchestration",
|
|
106
|
+
"activate_orchestration",
|
|
107
|
+
"pause_orchestration",
|
|
108
|
+
"trigger_orchestration",
|
|
109
|
+
"list_orchestration_runs",
|
|
110
|
+
"get_orchestration_run",
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
insights: {
|
|
114
|
+
summary: "AI-powered anomaly detection. Scans data for unusual patterns (spikes, drops, outliers) and generates actionable insights.",
|
|
115
|
+
describeWith: "describe_insights",
|
|
116
|
+
tools: [
|
|
117
|
+
"list_insights",
|
|
118
|
+
"get_insight",
|
|
119
|
+
"acknowledge_insight",
|
|
120
|
+
"dismiss_insight",
|
|
121
|
+
"get_insights_summary",
|
|
122
|
+
"trigger_anomaly_analysis",
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
validation: {
|
|
126
|
+
summary: "AI-powered data quality scanning. Detects typos, format issues, duplicates, semantic errors, and type mismatches.",
|
|
127
|
+
describeWith: "describe_validation",
|
|
128
|
+
tools: [
|
|
129
|
+
"trigger_validation_scan",
|
|
130
|
+
"list_validation_suggestions",
|
|
131
|
+
"get_validation_summary",
|
|
132
|
+
"accept_validation_suggestion",
|
|
133
|
+
"reject_validation_suggestion",
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
pages: {
|
|
137
|
+
summary: "UI page builder. Create list views, detail views, forms, and dashboards backed by collection data. Supports publishing, versioning, access control, and theming.",
|
|
138
|
+
describeWith: "describe_pages",
|
|
139
|
+
tools: [
|
|
140
|
+
"list_pages",
|
|
141
|
+
"get_page",
|
|
142
|
+
"create_page",
|
|
143
|
+
"update_page",
|
|
144
|
+
"delete_page",
|
|
145
|
+
"save_page_draft",
|
|
146
|
+
"get_page_draft",
|
|
147
|
+
"list_page_versions",
|
|
148
|
+
"validate_page",
|
|
149
|
+
"publish_page",
|
|
150
|
+
"unpublish_page",
|
|
151
|
+
"set_page_access_policy",
|
|
152
|
+
"get_page_theme",
|
|
153
|
+
"set_page_theme",
|
|
154
|
+
"get_navigation",
|
|
155
|
+
"set_navigation",
|
|
156
|
+
"delete_navigation",
|
|
157
|
+
"generate_starter_pages",
|
|
158
|
+
"accept_page_proposal",
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
workflow: "Typical workflow: 1) Define collections → 2) Create records → 3) Write compute functions → 4) Wire orchestrations → 5) Build pages to surface data → 6) Publish pages for end users.",
|
|
163
|
+
}, null, 2),
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
}));
|
|
168
|
+
// ── Collections ────────────────────────────────────────────────────
|
|
169
|
+
server.tool("describe_collections", "Get the schema reference for Centrali collections (data structures). Explains field types, property shapes, constraints, and how to define data models.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
return ({
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
type: "text",
|
|
174
|
+
text: JSON.stringify({
|
|
175
|
+
domain: "Collections",
|
|
176
|
+
description: "Collections (formerly 'structures') define the schema for your data. Each collection has a name, a recordSlug (used in API calls), and an array of properties that define the fields.",
|
|
177
|
+
collection_shape: {
|
|
178
|
+
id: "UUID — auto-generated",
|
|
179
|
+
name: "string — display name (e.g., 'Customers')",
|
|
180
|
+
recordSlug: "string — URL-safe identifier used in all record API calls (e.g., 'customers')",
|
|
181
|
+
description: "string | null — optional description",
|
|
182
|
+
status: "'active' | 'archived'",
|
|
183
|
+
properties: "Property[] — array of field definitions (see below)",
|
|
184
|
+
},
|
|
185
|
+
property_shape: {
|
|
186
|
+
name: "string — field name (e.g., 'email', 'totalAmount')",
|
|
187
|
+
type: "string — the field data type (see field_types below)",
|
|
188
|
+
required: "boolean — whether the field is mandatory",
|
|
189
|
+
unique: "boolean — whether values must be unique across records",
|
|
190
|
+
defaultValue: "any | null — default value for new records",
|
|
191
|
+
description: "string | null — field description",
|
|
192
|
+
config: "object | null — type-specific configuration (e.g., min/max for numbers, options for select)",
|
|
193
|
+
},
|
|
194
|
+
field_types: {
|
|
195
|
+
text: "Short text string",
|
|
196
|
+
long_text: "Multi-line text / rich text",
|
|
197
|
+
number: "Numeric value (integer or decimal). Config: { min, max, precision }",
|
|
198
|
+
boolean: "True/false toggle",
|
|
199
|
+
date: "Date value (ISO 8601)",
|
|
200
|
+
datetime: "Date and time value (ISO 8601 with time)",
|
|
201
|
+
email: "Email address (validated format)",
|
|
202
|
+
url: "URL (validated format)",
|
|
203
|
+
select: "Single choice from predefined options. Config: { options: [{ label, value }] }",
|
|
204
|
+
multi_select: "Multiple choices from predefined options. Config: { options: [{ label, value }] }",
|
|
205
|
+
reference: "Foreign key to another collection. Config: { referenceSlug: 'other-collection-slug' }",
|
|
206
|
+
file: "File attachment (stored in Azure Blob Storage)",
|
|
207
|
+
json: "Arbitrary JSON object",
|
|
208
|
+
auto_increment: "Auto-incrementing integer (read-only)",
|
|
209
|
+
},
|
|
210
|
+
crud_tools: {
|
|
211
|
+
create_collection: {
|
|
212
|
+
description: "Create a new collection with name, slug, properties, and optional settings",
|
|
213
|
+
required_params: ["name", "slug"],
|
|
214
|
+
optional_params: ["description", "properties", "enableVersioning", "schemaDiscoveryMode", "tags"],
|
|
215
|
+
},
|
|
216
|
+
update_collection: {
|
|
217
|
+
description: "Update an existing collection by ID. Partial updates supported.",
|
|
218
|
+
required_params: ["collectionId"],
|
|
219
|
+
optional_params: ["name", "description", "properties", "enableVersioning", "tags", "defaultTtlSeconds"],
|
|
220
|
+
},
|
|
221
|
+
delete_collection: {
|
|
222
|
+
description: "Delete a collection by ID. Permanently removes the schema and all records.",
|
|
223
|
+
required_params: ["collectionId"],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
tips: [
|
|
227
|
+
"Use list_collections to see all collections in the workspace",
|
|
228
|
+
"Use get_collection with a recordSlug to see the full property definitions",
|
|
229
|
+
"The recordSlug is what you use in query_records, create_record, etc.",
|
|
230
|
+
"Reference fields create relationships between collections — use 'expand' parameter in queries to join them",
|
|
231
|
+
"Use create_collection to define new data schemas with typed properties",
|
|
232
|
+
"Properties array defines the fields — each needs at minimum a name and type",
|
|
233
|
+
"Use schemaDiscoveryMode: 'flexible' to auto-add new fields when records are created",
|
|
234
|
+
],
|
|
235
|
+
}, null, 2),
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
}));
|
|
240
|
+
// ── Records ────────────────────────────────────────────────────────
|
|
241
|
+
server.tool("describe_records", "Get the schema reference for Centrali record operations. Explains filter syntax, sort syntax, pagination, expand, and the data. prefix convention.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
242
|
+
return ({
|
|
243
|
+
content: [
|
|
244
|
+
{
|
|
245
|
+
type: "text",
|
|
246
|
+
text: JSON.stringify({
|
|
247
|
+
domain: "Records",
|
|
248
|
+
description: "Records are rows of data stored in collections. All custom field values live under the 'data' namespace.",
|
|
249
|
+
record_shape: {
|
|
250
|
+
id: "UUID — auto-generated",
|
|
251
|
+
structureId: "UUID — the collection this record belongs to",
|
|
252
|
+
data: "object — all custom field values keyed by field name",
|
|
253
|
+
createdAt: "ISO 8601 datetime",
|
|
254
|
+
updatedAt: "ISO 8601 datetime",
|
|
255
|
+
createdBy: "UUID — user who created the record",
|
|
256
|
+
updatedBy: "UUID — user who last updated",
|
|
257
|
+
isDeleted: "boolean — true if soft-deleted",
|
|
258
|
+
},
|
|
259
|
+
filter_syntax: {
|
|
260
|
+
description: "Filters are passed as key-value pairs. Keys use the 'data.' prefix for custom fields and bracket notation for operators.",
|
|
261
|
+
examples: {
|
|
262
|
+
"Exact match": { "data.status": "active" },
|
|
263
|
+
"Greater than": { "data.price[gt]": 100 },
|
|
264
|
+
"Less than or equal": { "data.age[lte]": 30 },
|
|
265
|
+
"Not equal": { "data.type[ne]": "draft" },
|
|
266
|
+
"In list": { "data.category[in]": "electronics,books" },
|
|
267
|
+
"Not in list": { "data.status[nin]": "deleted,archived" },
|
|
268
|
+
"Contains (text)": { "data.name[contains]": "john" },
|
|
269
|
+
"Starts with": { "data.email[startswith]": "admin" },
|
|
270
|
+
"Ends with": { "data.domain[endswith]": ".com" },
|
|
271
|
+
},
|
|
272
|
+
operators: [
|
|
273
|
+
"eq (default, no bracket needed)",
|
|
274
|
+
"ne (not equal)",
|
|
275
|
+
"gt (greater than)",
|
|
276
|
+
"gte (greater than or equal)",
|
|
277
|
+
"lt (less than)",
|
|
278
|
+
"lte (less than or equal)",
|
|
279
|
+
"in (comma-separated list)",
|
|
280
|
+
"nin (not in comma-separated list)",
|
|
281
|
+
"contains (substring match)",
|
|
282
|
+
"startswith (prefix match)",
|
|
283
|
+
"endswith (suffix match)",
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
sort_syntax: {
|
|
287
|
+
description: "Sort by a single field. Prefix with '-' for descending order.",
|
|
288
|
+
examples: {
|
|
289
|
+
"Ascending by date": "createdAt",
|
|
290
|
+
"Descending by date": "-createdAt",
|
|
291
|
+
"By custom field asc": "data.name",
|
|
292
|
+
"By custom field desc": "-data.price",
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
pagination: {
|
|
296
|
+
page: "1-indexed page number (default: 1)",
|
|
297
|
+
pageSize: "Records per page (default: 50, max: 500)",
|
|
298
|
+
response_includes: "total, page, pageSize, totalPages in the response metadata",
|
|
299
|
+
},
|
|
300
|
+
expand: {
|
|
301
|
+
description: "Comma-separated list of reference field names to expand (join). Returns the full referenced record instead of just the ID.",
|
|
302
|
+
example: "expand: 'customer,product'",
|
|
303
|
+
},
|
|
304
|
+
upsert: {
|
|
305
|
+
description: "Atomic create-or-update. Provide 'match' fields to find existing record and 'data' for the full record body.",
|
|
306
|
+
example: {
|
|
307
|
+
match: { sku: "WIDGET-001" },
|
|
308
|
+
data: { sku: "WIDGET-001", name: "Widget", price: 9.99 },
|
|
309
|
+
},
|
|
310
|
+
result: "Returns { operation: 'created' | 'updated', data: record }",
|
|
311
|
+
},
|
|
312
|
+
tips: [
|
|
313
|
+
"Always use the 'data.' prefix when filtering on custom fields",
|
|
314
|
+
"System fields (id, createdAt, updatedAt) don't need the 'data.' prefix",
|
|
315
|
+
"Use upsert_record for idempotent imports — it won't create duplicates",
|
|
316
|
+
"Soft-deleted records can be restored with restore_record",
|
|
317
|
+
"Use get_records_by_ids for batch lookups (more efficient than individual get_record calls)",
|
|
318
|
+
],
|
|
319
|
+
}, null, 2),
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
});
|
|
323
|
+
}));
|
|
324
|
+
// ── Search ─────────────────────────────────────────────────────────
|
|
325
|
+
server.tool("describe_search", "Get the schema reference for Centrali full-text search. Explains search behavior, filtering, and result format.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
326
|
+
return ({
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
domain: "Search",
|
|
332
|
+
description: "Full-text search across all records in the workspace, powered by Meilisearch. Searches across all indexed text fields in records.",
|
|
333
|
+
parameters: {
|
|
334
|
+
query: "string — the search query. Supports natural language, partial matches, and typo tolerance.",
|
|
335
|
+
collections: "string | string[] — optional filter to limit search to specific collection(s) by slug",
|
|
336
|
+
limit: "number — max results (default: 20, max: 100)",
|
|
337
|
+
},
|
|
338
|
+
result_shape: {
|
|
339
|
+
hits: "Array of matching records with _rankingScore",
|
|
340
|
+
query: "The original search query",
|
|
341
|
+
processingTimeMs: "Search duration in milliseconds",
|
|
342
|
+
},
|
|
343
|
+
tips: [
|
|
344
|
+
"Search is typo-tolerant — 'custmer' will match 'customer'",
|
|
345
|
+
"Use the collections filter to narrow results to a specific data type",
|
|
346
|
+
"Results are ranked by relevance — most relevant first",
|
|
347
|
+
"Search only works on text/string fields — numeric fields are not full-text indexed",
|
|
348
|
+
],
|
|
349
|
+
}, null, 2),
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
}));
|
|
354
|
+
// ── Compute ────────────────────────────────────────────────────────
|
|
355
|
+
server.tool("describe_compute", "Get the schema reference for Centrali compute functions and triggers. Explains function types, trigger execution modes, and invocation patterns.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
356
|
+
return ({
|
|
357
|
+
content: [
|
|
358
|
+
{
|
|
359
|
+
type: "text",
|
|
360
|
+
text: JSON.stringify({
|
|
361
|
+
domain: "Compute",
|
|
362
|
+
description: "Compute functions are server-side JavaScript code blocks. They are executed via triggers, which define when and how a function runs.",
|
|
363
|
+
function_shape: {
|
|
364
|
+
id: "UUID",
|
|
365
|
+
name: "string — display name",
|
|
366
|
+
slug: "string — unique identifier",
|
|
367
|
+
description: "string | null",
|
|
368
|
+
code: "string — the JavaScript source code",
|
|
369
|
+
status: "'active' | 'inactive'",
|
|
370
|
+
testPayload: "object | null — sample payload for testing",
|
|
371
|
+
},
|
|
372
|
+
trigger_types: {
|
|
373
|
+
"on-demand": {
|
|
374
|
+
description: "Manually invoked via API call (invoke_trigger). Use for user-initiated actions.",
|
|
375
|
+
invocation: "Call invoke_trigger with the trigger ID and optional payload",
|
|
376
|
+
example_use_cases: [
|
|
377
|
+
"Send a notification",
|
|
378
|
+
"Run a data transformation",
|
|
379
|
+
"Generate a report",
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
"event-driven": {
|
|
383
|
+
description: "Automatically fires when a data event occurs (record created, updated, deleted).",
|
|
384
|
+
event_types: [
|
|
385
|
+
"record.created — fires after a new record is created",
|
|
386
|
+
"record.updated — fires after a record is modified",
|
|
387
|
+
"record.deleted — fires after a record is deleted",
|
|
388
|
+
],
|
|
389
|
+
config: "Specify the collection (structureId) and event type to listen for",
|
|
390
|
+
},
|
|
391
|
+
scheduled: {
|
|
392
|
+
description: "Runs on a cron schedule (e.g., every hour, daily at midnight).",
|
|
393
|
+
config: "Uses standard cron expression syntax (e.g., '0 * * * *' for hourly)",
|
|
394
|
+
},
|
|
395
|
+
webhook: {
|
|
396
|
+
description: "Triggered by an external HTTP POST to a generated webhook URL.",
|
|
397
|
+
config: "Each webhook trigger gets a unique URL to share with external services",
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
trigger_shape: {
|
|
401
|
+
id: "UUID",
|
|
402
|
+
name: "string",
|
|
403
|
+
functionId: "UUID — the compute function to execute",
|
|
404
|
+
executionType: "'on-demand' | 'event-driven' | 'scheduled' | 'webhook'",
|
|
405
|
+
status: "'active' | 'paused'",
|
|
406
|
+
config: "object — type-specific configuration (cron, event filters, etc.)",
|
|
407
|
+
},
|
|
408
|
+
function_crud: {
|
|
409
|
+
get_function: {
|
|
410
|
+
description: "Get a compute function by ID, including its code",
|
|
411
|
+
required_params: ["functionId"],
|
|
412
|
+
},
|
|
413
|
+
create_function: {
|
|
414
|
+
description: "Create a new compute function",
|
|
415
|
+
required_params: ["name", "slug", "code"],
|
|
416
|
+
optional_params: ["description", "timeout"],
|
|
417
|
+
code_format: "module.exports = async (ctx) => { /* your code */ return result; }",
|
|
418
|
+
},
|
|
419
|
+
update_function: {
|
|
420
|
+
description: "Update an existing function. Partial updates supported.",
|
|
421
|
+
required_params: ["functionId"],
|
|
422
|
+
optional_params: ["name", "description", "code", "timeout"],
|
|
423
|
+
},
|
|
424
|
+
delete_function: {
|
|
425
|
+
description: "Delete a compute function by ID",
|
|
426
|
+
required_params: ["functionId"],
|
|
427
|
+
},
|
|
428
|
+
test_function: {
|
|
429
|
+
description: "Test execute code without saving. Returns output, duration, and logs.",
|
|
430
|
+
required_params: ["code"],
|
|
431
|
+
optional_params: ["input"],
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
trigger_crud: {
|
|
435
|
+
get_trigger: {
|
|
436
|
+
description: "Get any trigger by ID (all execution types)",
|
|
437
|
+
required_params: ["triggerId"],
|
|
438
|
+
},
|
|
439
|
+
create_trigger: {
|
|
440
|
+
description: "Create a new function trigger",
|
|
441
|
+
required_params: ["name", "functionId", "executionType"],
|
|
442
|
+
optional_params: ["description", "triggerMetadata", "enabled"],
|
|
443
|
+
triggerMetadata_examples: {
|
|
444
|
+
"event-driven": { event: "record.created", recordSlug: "orders" },
|
|
445
|
+
scheduled: { scheduleType: "cron", cronExpression: "0 9 * * *", timezone: "America/New_York" },
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
update_trigger: {
|
|
449
|
+
description: "Update an existing trigger. Partial updates supported.",
|
|
450
|
+
required_params: ["triggerId"],
|
|
451
|
+
optional_params: ["name", "description", "enabled", "triggerMetadata"],
|
|
452
|
+
},
|
|
453
|
+
delete_trigger: {
|
|
454
|
+
description: "Delete a trigger by ID",
|
|
455
|
+
required_params: ["triggerId"],
|
|
456
|
+
},
|
|
457
|
+
pause_trigger: {
|
|
458
|
+
description: "Pause a trigger to temporarily disable it",
|
|
459
|
+
required_params: ["triggerId"],
|
|
460
|
+
},
|
|
461
|
+
resume_trigger: {
|
|
462
|
+
description: "Resume a paused trigger",
|
|
463
|
+
required_params: ["triggerId"],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
tips: [
|
|
467
|
+
"Use list_functions to see all available functions",
|
|
468
|
+
"Use list_triggers to see how functions are wired to execution events",
|
|
469
|
+
"Only on-demand triggers can be invoked via invoke_trigger — other types fire automatically",
|
|
470
|
+
"Functions receive the trigger payload in their execution context",
|
|
471
|
+
"Use pause_trigger/resume_trigger to temporarily disable triggers without deleting them",
|
|
472
|
+
"Use test_function to validate code before creating or updating a function",
|
|
473
|
+
"Use create_function + create_trigger together to set up a complete compute pipeline",
|
|
474
|
+
],
|
|
475
|
+
}, null, 2),
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
});
|
|
479
|
+
}));
|
|
480
|
+
// ── Smart Queries ──────────────────────────────────────────────────
|
|
481
|
+
server.tool("describe_smart_queries", "Get the schema reference for Centrali smart queries. Explains parameterized queries, variable substitution, and execution.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
482
|
+
return ({
|
|
483
|
+
content: [
|
|
484
|
+
{
|
|
485
|
+
type: "text",
|
|
486
|
+
text: JSON.stringify({
|
|
487
|
+
domain: "Smart Queries",
|
|
488
|
+
description: "Smart queries are reusable, parameterized queries defined in the Centrali console. They allow complex filtering and aggregation logic to be saved and executed with variable substitution.",
|
|
489
|
+
smart_query_shape: {
|
|
490
|
+
id: "UUID",
|
|
491
|
+
name: "string — display name",
|
|
492
|
+
structureSlug: "string — the collection this query targets",
|
|
493
|
+
description: "string | null",
|
|
494
|
+
query: "object — the query definition with filters, sort, fields, and aggregations",
|
|
495
|
+
variables: "object[] — declared variables with name, type, and default values",
|
|
496
|
+
},
|
|
497
|
+
variable_syntax: {
|
|
498
|
+
description: "Variables use double-brace syntax: {{variableName}}. When executing, pass a variables object to substitute values.",
|
|
499
|
+
example: {
|
|
500
|
+
query_filter: {
|
|
501
|
+
"data.status": "{{status}}",
|
|
502
|
+
"data.amount[gte]": "{{minAmount}}",
|
|
503
|
+
},
|
|
504
|
+
execution_variables: {
|
|
505
|
+
status: "active",
|
|
506
|
+
minAmount: "1000",
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
note: "Variable values are always passed as strings — the query engine handles type coercion",
|
|
510
|
+
},
|
|
511
|
+
crud_tools: {
|
|
512
|
+
get_smart_query: {
|
|
513
|
+
description: "Get a smart query by ID, including its full definition",
|
|
514
|
+
required_params: ["recordSlug", "queryId"],
|
|
515
|
+
},
|
|
516
|
+
create_smart_query: {
|
|
517
|
+
description: "Create a new smart query for a collection",
|
|
518
|
+
required_params: ["recordSlug", "name", "queryDefinition"],
|
|
519
|
+
optional_params: ["description"],
|
|
520
|
+
queryDefinition_example: {
|
|
521
|
+
where: { status: { "$eq": "active" } },
|
|
522
|
+
sort: [{ field: "createdAt", direction: "desc" }],
|
|
523
|
+
limit: 100,
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
update_smart_query: {
|
|
527
|
+
description: "Update an existing smart query. Partial updates supported.",
|
|
528
|
+
required_params: ["recordSlug", "queryId"],
|
|
529
|
+
optional_params: ["name", "description", "queryDefinition"],
|
|
530
|
+
},
|
|
531
|
+
delete_smart_query: {
|
|
532
|
+
description: "Delete a smart query by ID",
|
|
533
|
+
required_params: ["recordSlug", "queryId"],
|
|
534
|
+
},
|
|
535
|
+
test_smart_query: {
|
|
536
|
+
description: "Test execute a query definition without saving it. Preview results before creating.",
|
|
537
|
+
required_params: ["recordSlug", "queryDefinition"],
|
|
538
|
+
optional_params: ["variables"],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
tips: [
|
|
542
|
+
"Use list_smart_queries to discover available queries (optionally filter by collection slug)",
|
|
543
|
+
"Smart queries return the same result shape as query_records",
|
|
544
|
+
"Variables are defined when creating the query in the console — check the query's variables array to see what's expected",
|
|
545
|
+
"Prefer smart queries over ad-hoc query_records filters for complex/reusable logic",
|
|
546
|
+
"Use test_smart_query to validate query syntax and preview results before saving",
|
|
547
|
+
"Use create_smart_query to save reusable queries that can be executed with execute_smart_query",
|
|
548
|
+
],
|
|
549
|
+
}, null, 2),
|
|
550
|
+
},
|
|
551
|
+
],
|
|
552
|
+
});
|
|
553
|
+
}));
|
|
554
|
+
// ── Orchestrations ─────────────────────────────────────────────────
|
|
555
|
+
server.tool("describe_orchestrations", "Get the schema reference for Centrali orchestrations. Explains multi-step workflows, runs, steps, and execution model.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
556
|
+
return ({
|
|
557
|
+
content: [
|
|
558
|
+
{
|
|
559
|
+
type: "text",
|
|
560
|
+
text: JSON.stringify({
|
|
561
|
+
domain: "Orchestrations",
|
|
562
|
+
description: "Orchestrations are multi-step workflows that chain compute functions together. Each step executes a function and passes its output to the next step.",
|
|
563
|
+
orchestration_shape: {
|
|
564
|
+
id: "UUID",
|
|
565
|
+
name: "string",
|
|
566
|
+
description: "string | null",
|
|
567
|
+
status: "'draft' | 'active' | 'paused'",
|
|
568
|
+
steps: "OrchestrationStep[] — ordered array of step definitions",
|
|
569
|
+
},
|
|
570
|
+
step_shape: {
|
|
571
|
+
id: "UUID",
|
|
572
|
+
name: "string — step display name",
|
|
573
|
+
functionId: "UUID — the compute function to execute",
|
|
574
|
+
order: "number — execution order (0-indexed)",
|
|
575
|
+
inputMapping: "object | null — maps data from previous step outputs or orchestration input to this step's input",
|
|
576
|
+
retryConfig: "object | null — { maxRetries, delayMs } for automatic retry on failure",
|
|
577
|
+
timeoutMs: "number | null — step execution timeout",
|
|
578
|
+
},
|
|
579
|
+
run_shape: {
|
|
580
|
+
id: "UUID",
|
|
581
|
+
orchestrationId: "UUID",
|
|
582
|
+
status: "'pending' | 'running' | 'waiting' | 'completed' | 'failed'",
|
|
583
|
+
input: "object — the input data provided when the run was triggered",
|
|
584
|
+
output: "object | null — final output after all steps complete",
|
|
585
|
+
startedAt: "ISO 8601 datetime",
|
|
586
|
+
completedAt: "ISO 8601 datetime | null",
|
|
587
|
+
correlationId: "string | null — optional tracing ID",
|
|
588
|
+
},
|
|
589
|
+
run_step_shape: {
|
|
590
|
+
stepId: "UUID",
|
|
591
|
+
status: "'pending' | 'running' | 'completed' | 'failed' | 'skipped'",
|
|
592
|
+
input: "object — input passed to this step",
|
|
593
|
+
output: "object | null — output returned by this step",
|
|
594
|
+
error: "string | null — error message if failed",
|
|
595
|
+
startedAt: "ISO 8601 datetime | null",
|
|
596
|
+
completedAt: "ISO 8601 datetime | null",
|
|
597
|
+
},
|
|
598
|
+
crud_tools: {
|
|
599
|
+
create_orchestration: {
|
|
600
|
+
description: "Create a new orchestration workflow",
|
|
601
|
+
required_params: ["slug", "name", "trigger", "steps"],
|
|
602
|
+
optional_params: ["description"],
|
|
603
|
+
trigger_example: { type: "on-demand" },
|
|
604
|
+
step_example: {
|
|
605
|
+
id: "validate",
|
|
606
|
+
type: "compute",
|
|
607
|
+
functionId: "func-uuid",
|
|
608
|
+
onSuccess: { nextStepId: "process" },
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
update_orchestration: {
|
|
612
|
+
description: "Update an existing orchestration. Partial updates supported.",
|
|
613
|
+
required_params: ["orchestrationId"],
|
|
614
|
+
optional_params: ["name", "description", "status", "trigger", "steps"],
|
|
615
|
+
},
|
|
616
|
+
delete_orchestration: {
|
|
617
|
+
description: "Delete an orchestration and all its runs",
|
|
618
|
+
required_params: ["orchestrationId"],
|
|
619
|
+
},
|
|
620
|
+
activate_orchestration: {
|
|
621
|
+
description: "Set orchestration status to 'active' so it can be triggered",
|
|
622
|
+
required_params: ["orchestrationId"],
|
|
623
|
+
},
|
|
624
|
+
pause_orchestration: {
|
|
625
|
+
description: "Set orchestration status to 'paused' to prevent triggering",
|
|
626
|
+
required_params: ["orchestrationId"],
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
tips: [
|
|
630
|
+
"Use list_orchestrations to see available workflows",
|
|
631
|
+
"Use create_orchestration to define new multi-step workflows",
|
|
632
|
+
"Use activate_orchestration to enable a draft orchestration for triggering",
|
|
633
|
+
"Use pause_orchestration to temporarily disable an orchestration without deleting it",
|
|
634
|
+
"Use trigger_orchestration with an input object to start a run",
|
|
635
|
+
"Use get_orchestration_run with includeSteps=true to see step-by-step execution details",
|
|
636
|
+
"Orchestrations must be 'active' to be triggered — draft orchestrations cannot run",
|
|
637
|
+
"Use correlationId when triggering to trace runs across systems",
|
|
638
|
+
],
|
|
639
|
+
}, null, 2),
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
});
|
|
643
|
+
}));
|
|
644
|
+
// ── Insights ───────────────────────────────────────────────────────
|
|
645
|
+
server.tool("describe_insights", "Get the schema reference for Centrali anomaly insights. Explains anomaly detection, severity levels, and the acknowledge/dismiss workflow.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
646
|
+
return ({
|
|
647
|
+
content: [
|
|
648
|
+
{
|
|
649
|
+
type: "text",
|
|
650
|
+
text: JSON.stringify({
|
|
651
|
+
domain: "Anomaly Insights",
|
|
652
|
+
description: "Centrali's AI-powered anomaly detection scans your data for unusual patterns. Insights are generated automatically or on-demand via trigger_anomaly_analysis.",
|
|
653
|
+
insight_shape: {
|
|
654
|
+
id: "UUID",
|
|
655
|
+
structureSlug: "string — the collection where the anomaly was detected",
|
|
656
|
+
type: "string — anomaly type (e.g., 'spike', 'drop', 'outlier', 'pattern_break')",
|
|
657
|
+
severity: "'critical' | 'high' | 'medium' | 'low'",
|
|
658
|
+
status: "'active' | 'acknowledged' | 'dismissed'",
|
|
659
|
+
title: "string — human-readable summary",
|
|
660
|
+
description: "string — detailed explanation of the anomaly",
|
|
661
|
+
affectedRecords: "string[] — IDs of affected records",
|
|
662
|
+
detectedAt: "ISO 8601 datetime",
|
|
663
|
+
metadata: "object — additional context (field names, thresholds, values)",
|
|
664
|
+
},
|
|
665
|
+
lifecycle: {
|
|
666
|
+
active: "Newly detected, needs review",
|
|
667
|
+
acknowledged: "Reviewed and marked as handled (via acknowledge_insight). Remains visible for tracking.",
|
|
668
|
+
dismissed: "Marked as false positive or not relevant (via dismiss_insight). Hidden from default views.",
|
|
669
|
+
},
|
|
670
|
+
summary_shape: {
|
|
671
|
+
totalActive: "number",
|
|
672
|
+
totalAcknowledged: "number",
|
|
673
|
+
totalDismissed: "number",
|
|
674
|
+
bySeverity: "{ critical: n, high: n, medium: n, low: n }",
|
|
675
|
+
},
|
|
676
|
+
tips: [
|
|
677
|
+
"Use trigger_anomaly_analysis to scan a specific collection on-demand",
|
|
678
|
+
"Use get_insights_summary for a quick overview before diving into individual insights",
|
|
679
|
+
"Filter by severity='critical' to focus on the most important issues first",
|
|
680
|
+
"Acknowledged insights are still visible — use dismiss for false positives",
|
|
681
|
+
],
|
|
682
|
+
}, null, 2),
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
});
|
|
686
|
+
}));
|
|
687
|
+
// ── Validation ─────────────────────────────────────────────────────
|
|
688
|
+
server.tool("describe_validation", "Get the schema reference for Centrali data quality validation. Explains scan types, suggestion lifecycle, and how to review/apply fixes.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
689
|
+
return ({
|
|
690
|
+
content: [
|
|
691
|
+
{
|
|
692
|
+
type: "text",
|
|
693
|
+
text: JSON.stringify({
|
|
694
|
+
domain: "Data Validation",
|
|
695
|
+
description: "AI-powered data quality scanning that detects issues in your records and suggests corrections. Scans run asynchronously and produce suggestions that can be accepted (applied) or rejected.",
|
|
696
|
+
scan_types: {
|
|
697
|
+
typo: "Detects spelling errors and typos in text fields (e.g., 'Chciago' → 'Chicago')",
|
|
698
|
+
format: "Detects format inconsistencies (e.g., mixed date formats, inconsistent phone number formatting)",
|
|
699
|
+
duplicate: "Detects potential duplicate records based on field similarity",
|
|
700
|
+
semantic: "Detects semantically incorrect values (e.g., a country field containing a city name)",
|
|
701
|
+
type: "Detects type mismatches (e.g., a number stored as a string, invalid email format)",
|
|
702
|
+
},
|
|
703
|
+
suggestion_shape: {
|
|
704
|
+
id: "UUID",
|
|
705
|
+
structureSlug: "string — the collection containing the issue",
|
|
706
|
+
recordId: "UUID — the specific record with the issue",
|
|
707
|
+
field: "string — the field name where the issue was found",
|
|
708
|
+
issueType: "'typo' | 'format' | 'duplicate' | 'semantic' | 'type'",
|
|
709
|
+
severity: "'error' | 'warning' | 'info'",
|
|
710
|
+
currentValue: "any — the current (problematic) value",
|
|
711
|
+
suggestedValue: "any — the proposed correction",
|
|
712
|
+
confidence: "number (0-1) — AI confidence in the suggestion. 0.9+ is high confidence.",
|
|
713
|
+
explanation: "string — why this was flagged and what the fix does",
|
|
714
|
+
status: "'pending' | 'accepted' | 'rejected' | 'auto-applied'",
|
|
715
|
+
},
|
|
716
|
+
lifecycle: {
|
|
717
|
+
pending: "Newly generated, needs human review",
|
|
718
|
+
accepted: "Accepted and applied to the record (via accept_validation_suggestion)",
|
|
719
|
+
rejected: "Rejected as incorrect or not applicable (via reject_validation_suggestion)",
|
|
720
|
+
"auto-applied": "Automatically applied for high-confidence suggestions (if auto-apply is enabled)",
|
|
721
|
+
},
|
|
722
|
+
tips: [
|
|
723
|
+
"Use trigger_validation_scan to start a scan — you can target specific issue types",
|
|
724
|
+
"Use list_validation_suggestions with minConfidence=0.9 to focus on high-confidence fixes",
|
|
725
|
+
"Use get_validation_summary for a quick overview of data quality",
|
|
726
|
+
"Accept applies the fix immediately — the record's field value is updated",
|
|
727
|
+
"Scans are async — there may be a short delay before suggestions appear after triggering",
|
|
728
|
+
],
|
|
729
|
+
}, null, 2),
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
});
|
|
733
|
+
}));
|
|
734
|
+
// ── Pages ──────────────────────────────────────────────────────────
|
|
735
|
+
server.tool("describe_pages", "Get the schema reference for Centrali pages. Explains page types, the definition structure, sections, blocks, and the publish lifecycle.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
736
|
+
return ({
|
|
737
|
+
content: [
|
|
738
|
+
{
|
|
739
|
+
type: "text",
|
|
740
|
+
text: JSON.stringify({
|
|
741
|
+
domain: "Pages",
|
|
742
|
+
description: "Pages are pre-built UI views that surface data from collections. Each page has a type, a versioned definition (describing layout and data binding), and a publish lifecycle.",
|
|
743
|
+
page_types: {
|
|
744
|
+
list: {
|
|
745
|
+
description: "Data table view — shows multiple records with columns, sorting, filtering, and pagination.",
|
|
746
|
+
typical_sections: ["content"],
|
|
747
|
+
typical_blocks: ["data-table"],
|
|
748
|
+
validation_requirement: "List pages must have at least one block with configured columns (in presentation.columns or block.columns) to pass publishing validation.",
|
|
749
|
+
use_when: "You need to browse, search, or manage a set of records",
|
|
750
|
+
},
|
|
751
|
+
detail: {
|
|
752
|
+
description: "Single-record view — shows all fields of one record with optional related data and actions.",
|
|
753
|
+
typical_sections: ["hero", "content", "supporting"],
|
|
754
|
+
typical_blocks: [
|
|
755
|
+
"detail-fields",
|
|
756
|
+
"field-display",
|
|
757
|
+
"related-list",
|
|
758
|
+
"activity-feed",
|
|
759
|
+
"markdown",
|
|
760
|
+
],
|
|
761
|
+
use_when: "You need to display a single record's full details (e.g., customer profile)",
|
|
762
|
+
},
|
|
763
|
+
form: {
|
|
764
|
+
description: "Data entry form — lets users create or update records with validation and submission actions.",
|
|
765
|
+
typical_sections: ["content", "actions"],
|
|
766
|
+
typical_blocks: [
|
|
767
|
+
"form",
|
|
768
|
+
"field-group",
|
|
769
|
+
"text-input",
|
|
770
|
+
"number-input",
|
|
771
|
+
"select",
|
|
772
|
+
"checkbox",
|
|
773
|
+
"date-picker",
|
|
774
|
+
"textarea",
|
|
775
|
+
"file-upload",
|
|
776
|
+
],
|
|
777
|
+
validation_requirement: "Form pages must have at least one field block (text-input, number-input, select, checkbox, date-picker, textarea, file-upload, field, field-group, or field-display) to pass publishing validation.",
|
|
778
|
+
use_when: "You need users to input or edit data",
|
|
779
|
+
},
|
|
780
|
+
dashboard: {
|
|
781
|
+
description: "Metrics and analytics view — shows KPIs, charts, tables, and activity feeds for data overview.",
|
|
782
|
+
typical_sections: ["metrics", "content", "supporting"],
|
|
783
|
+
typical_blocks: [
|
|
784
|
+
"stat-group",
|
|
785
|
+
"metric-card",
|
|
786
|
+
"chart",
|
|
787
|
+
"data-table",
|
|
788
|
+
"activity-feed",
|
|
789
|
+
"progress-indicator",
|
|
790
|
+
"kanban",
|
|
791
|
+
"calendar",
|
|
792
|
+
"filter-bar",
|
|
793
|
+
],
|
|
794
|
+
use_when: "You need an overview with KPIs, trends, and visual summaries",
|
|
795
|
+
},
|
|
796
|
+
},
|
|
797
|
+
describeDefinitionWith: "describe_page_definition",
|
|
798
|
+
describeBlocksWith: "describe_page_blocks",
|
|
799
|
+
describeActionsWith: "describe_page_actions",
|
|
800
|
+
lifecycle: {
|
|
801
|
+
"1_create": "create_page — creates the page record with name, slug, and type",
|
|
802
|
+
"2_draft": "save_page_draft — saves or updates the page definition (layout, blocks, data bindings)",
|
|
803
|
+
"3_validate": "validate_page — checks the draft for errors before publishing",
|
|
804
|
+
"4_publish": "publish_page — makes the page accessible at its runtime URL",
|
|
805
|
+
"5_iterate": "Repeat steps 2-4 to update. Each publish creates a new version.",
|
|
806
|
+
unpublish: "unpublish_page — removes the page from its runtime URL (keeps the definition)",
|
|
807
|
+
},
|
|
808
|
+
access_modes: {
|
|
809
|
+
public: "Anyone can view — no authentication required",
|
|
810
|
+
authenticated: "Only logged-in users can view — redirects to login if not authenticated",
|
|
811
|
+
"role-gated": "Only users with specific roles can view — returns 403 for unauthorized users",
|
|
812
|
+
},
|
|
813
|
+
describeNavigationWith: "describe_navigation",
|
|
814
|
+
tips: [
|
|
815
|
+
"Call describe_page_definition to learn the full definition schema before using save_page_draft",
|
|
816
|
+
"Call describe_page_blocks to see all available block types and their configs",
|
|
817
|
+
"Call describe_navigation to learn the navigation config schema before using set_navigation",
|
|
818
|
+
"Use generate_starter_pages to auto-generate page proposals from your collections",
|
|
819
|
+
"Always validate_page before publish_page — publishing will reject if there are errors",
|
|
820
|
+
"Set access policy with set_page_access_policy before publishing if you need auth",
|
|
821
|
+
],
|
|
822
|
+
}, null, 2),
|
|
823
|
+
},
|
|
824
|
+
],
|
|
825
|
+
});
|
|
826
|
+
}));
|
|
827
|
+
// ── Page Definition Schema ─────────────────────────────────────────
|
|
828
|
+
server.tool("describe_page_definition", "Get the full schema for a page definition — the JSON structure passed to save_page_draft. Explains sections, blocks, data sources, and how they compose together.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
829
|
+
return ({
|
|
830
|
+
content: [
|
|
831
|
+
{
|
|
832
|
+
type: "text",
|
|
833
|
+
text: JSON.stringify({
|
|
834
|
+
domain: "Page Definition",
|
|
835
|
+
description: "A page definition is a JSON object that describes the page layout. It contains sections, and each section contains blocks. Blocks are the actual UI components (tables, charts, forms, etc.).",
|
|
836
|
+
definition_shape: {
|
|
837
|
+
sections: "PageSection[] — ordered array of sections that make up the page",
|
|
838
|
+
theme: {
|
|
839
|
+
inherit: "boolean — if true, inherits the workspace theme. Usually set to true.",
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
section_shape: {
|
|
843
|
+
id: "string — unique ID for the section (use a UUID or descriptive slug like 'metrics-strip')",
|
|
844
|
+
kind: "'content' | 'metrics' | 'exceptions' | 'actions' | 'hero' | 'supporting'",
|
|
845
|
+
title: "string — section heading displayed to users",
|
|
846
|
+
layout: "'single-column' | 'two-column' | 'metric-strip' | 'stack'",
|
|
847
|
+
blocks: "PageBlock[] — array of blocks in this section",
|
|
848
|
+
},
|
|
849
|
+
section_kinds: {
|
|
850
|
+
hero: "Top of page — typically a title/summary or key metric. Use for detail pages.",
|
|
851
|
+
metrics: "KPI strip — typically metric-card or stat-group blocks in metric-strip layout",
|
|
852
|
+
content: "Main content area — data tables, charts, forms. The workhorse section.",
|
|
853
|
+
supporting: "Secondary content — related data, activity feeds, supplementary info",
|
|
854
|
+
actions: "Action buttons or form submission areas. Often at the bottom of form pages.",
|
|
855
|
+
exceptions: "Error/warning display area — for anomaly or validation results",
|
|
856
|
+
},
|
|
857
|
+
section_layouts: {
|
|
858
|
+
"single-column": "Full-width blocks stacked vertically",
|
|
859
|
+
"two-column": "Blocks arranged in two columns side by side",
|
|
860
|
+
"metric-strip": "Horizontal strip of small blocks (ideal for stat-group, metric-card)",
|
|
861
|
+
stack: "Compact vertical stack",
|
|
862
|
+
},
|
|
863
|
+
block_shape: {
|
|
864
|
+
id: "string — unique ID for the block (use a UUID)",
|
|
865
|
+
blockType: "string — the type of UI component (see describe_page_blocks for all types)",
|
|
866
|
+
dataSource: "DataSource | null — where this block gets its data (see below)",
|
|
867
|
+
presentation: "BlockPresentation | null — display options (density, columns, chart type, emphasis rules)",
|
|
868
|
+
actions: "BlockAction[] | null — interactive actions (buttons, row clicks, form submissions). See describe_page_actions.",
|
|
869
|
+
narrative: "BlockNarrative | null — contextual copy (primary question, framing text, comparison)",
|
|
870
|
+
filterableColumns: "string[] | null — which columns users can filter on (for data-table blocks)",
|
|
871
|
+
},
|
|
872
|
+
data_source_shape: {
|
|
873
|
+
type: "'structure' | 'query'",
|
|
874
|
+
ref: "string — the collection ID (UUID) for 'structure' type, or smart query ID for 'query' type",
|
|
875
|
+
mode: "'list' | 'single' | 'aggregate' — how to fetch data. 'list' returns multiple records, 'single' returns one, 'aggregate' returns computed values.",
|
|
876
|
+
config: {
|
|
877
|
+
fields: "string[] | null — specific fields to fetch (null = all fields)",
|
|
878
|
+
fieldOrder: "string[] | null — display order for fields",
|
|
879
|
+
filters: "object | null — same filter syntax as query_records (data.field[operator]: value)",
|
|
880
|
+
sort: "SortConfig[] | null — array of { field, direction: 'asc' | 'desc' }",
|
|
881
|
+
limit: "number | null — max records to fetch",
|
|
882
|
+
page: "number | null — for pagination",
|
|
883
|
+
pageSize: "number | null — records per page",
|
|
884
|
+
},
|
|
885
|
+
aggregation: {
|
|
886
|
+
description: "Only used when mode is 'aggregate'. Defines computations over the data.",
|
|
887
|
+
operations: "Record<string, { count?: '*', sum?: 'fieldName', avg?: 'fieldName', min?: 'fieldName', max?: 'fieldName' }>",
|
|
888
|
+
groupBy: "string[] | null — fields to group by",
|
|
889
|
+
},
|
|
890
|
+
},
|
|
891
|
+
presentation_shape: {
|
|
892
|
+
density: "'compact' | 'comfortable' | 'spacious' — controls spacing/padding",
|
|
893
|
+
columns: "string[] — which columns to show (for table blocks)",
|
|
894
|
+
chartType: "'bar' | 'line' | 'pie' — for chart blocks",
|
|
895
|
+
emphasisRules: "EmphasisRule[] — conditional formatting rules: { field, operator, threshold, severity }",
|
|
896
|
+
},
|
|
897
|
+
narrative_shape: {
|
|
898
|
+
primaryQuestion: "string — the business question this block answers (e.g., 'How are sales trending?')",
|
|
899
|
+
framingCopy: "string — contextual description shown above/below the block",
|
|
900
|
+
comparison: "{ type: 'previous-period' | 'target' | 'baseline', value: number } — for metrics comparison",
|
|
901
|
+
nextActionLabel: "string — label for a suggested next action (e.g., 'View all orders')",
|
|
902
|
+
},
|
|
903
|
+
example_list_page_definition: {
|
|
904
|
+
sections: [
|
|
905
|
+
{
|
|
906
|
+
id: "main-content",
|
|
907
|
+
kind: "content",
|
|
908
|
+
title: "All Customers",
|
|
909
|
+
layout: "single-column",
|
|
910
|
+
blocks: [
|
|
911
|
+
{
|
|
912
|
+
id: "customer-table",
|
|
913
|
+
blockType: "data-table",
|
|
914
|
+
dataSource: {
|
|
915
|
+
type: "structure",
|
|
916
|
+
ref: "<collection-uuid>",
|
|
917
|
+
mode: "list",
|
|
918
|
+
config: {
|
|
919
|
+
fields: [
|
|
920
|
+
"name",
|
|
921
|
+
"email",
|
|
922
|
+
"status",
|
|
923
|
+
"createdAt",
|
|
924
|
+
],
|
|
925
|
+
sort: [{ field: "createdAt", direction: "desc" }],
|
|
926
|
+
pageSize: 25,
|
|
927
|
+
},
|
|
928
|
+
},
|
|
929
|
+
presentation: { density: "comfortable" },
|
|
930
|
+
filterableColumns: ["status", "name"],
|
|
931
|
+
},
|
|
932
|
+
],
|
|
933
|
+
},
|
|
934
|
+
],
|
|
935
|
+
theme: { inherit: true },
|
|
936
|
+
},
|
|
937
|
+
example_dashboard_definition: {
|
|
938
|
+
sections: [
|
|
939
|
+
{
|
|
940
|
+
id: "kpi-strip",
|
|
941
|
+
kind: "metrics",
|
|
942
|
+
title: "Key Metrics",
|
|
943
|
+
layout: "metric-strip",
|
|
944
|
+
blocks: [
|
|
945
|
+
{
|
|
946
|
+
id: "total-orders",
|
|
947
|
+
blockType: "metric-card",
|
|
948
|
+
dataSource: {
|
|
949
|
+
type: "structure",
|
|
950
|
+
ref: "<orders-collection-uuid>",
|
|
951
|
+
mode: "aggregate",
|
|
952
|
+
config: {},
|
|
953
|
+
aggregation: {
|
|
954
|
+
operations: { totalOrders: { count: "*" } },
|
|
955
|
+
},
|
|
956
|
+
},
|
|
957
|
+
narrative: {
|
|
958
|
+
primaryQuestion: "How many orders do we have?",
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
id: "total-revenue",
|
|
963
|
+
blockType: "metric-card",
|
|
964
|
+
dataSource: {
|
|
965
|
+
type: "structure",
|
|
966
|
+
ref: "<orders-collection-uuid>",
|
|
967
|
+
mode: "aggregate",
|
|
968
|
+
config: {},
|
|
969
|
+
aggregation: {
|
|
970
|
+
operations: {
|
|
971
|
+
revenue: { sum: "totalAmount" },
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
narrative: {
|
|
976
|
+
primaryQuestion: "What is our total revenue?",
|
|
977
|
+
},
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
id: "charts",
|
|
983
|
+
kind: "content",
|
|
984
|
+
title: "Trends",
|
|
985
|
+
layout: "two-column",
|
|
986
|
+
blocks: [
|
|
987
|
+
{
|
|
988
|
+
id: "orders-by-status",
|
|
989
|
+
blockType: "chart",
|
|
990
|
+
dataSource: {
|
|
991
|
+
type: "structure",
|
|
992
|
+
ref: "<orders-collection-uuid>",
|
|
993
|
+
mode: "aggregate",
|
|
994
|
+
config: {},
|
|
995
|
+
aggregation: {
|
|
996
|
+
operations: { count: { count: "*" } },
|
|
997
|
+
groupBy: ["status"],
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
presentation: { chartType: "pie" },
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
},
|
|
1004
|
+
],
|
|
1005
|
+
theme: { inherit: true },
|
|
1006
|
+
},
|
|
1007
|
+
}, null, 2),
|
|
1008
|
+
},
|
|
1009
|
+
],
|
|
1010
|
+
});
|
|
1011
|
+
}));
|
|
1012
|
+
// ── Page Block Types ───────────────────────────────────────────────
|
|
1013
|
+
server.tool("describe_page_blocks", "Get all available page block types and their configuration shapes. Each block type has specific data source requirements and presentation options.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
1014
|
+
return ({
|
|
1015
|
+
content: [
|
|
1016
|
+
{
|
|
1017
|
+
type: "text",
|
|
1018
|
+
text: JSON.stringify({
|
|
1019
|
+
domain: "Page Block Types",
|
|
1020
|
+
description: "Blocks are the UI components inside page sections. Each blockType has its own configuration requirements.",
|
|
1021
|
+
block_types: {
|
|
1022
|
+
"data-table": {
|
|
1023
|
+
description: "Tabular data display with columns, sorting, filtering, and pagination. The primary block for list pages.",
|
|
1024
|
+
dataSource: "Required — type: 'structure', mode: 'list'",
|
|
1025
|
+
config: {
|
|
1026
|
+
columns: "{ field: string, label: string, sortable?: boolean }[] — column definitions",
|
|
1027
|
+
defaultSort: "{ field: string, direction: 'asc' | 'desc' } — initial sort",
|
|
1028
|
+
pageSize: "number — rows per page (default: 25)",
|
|
1029
|
+
rowClickAction: "BlockAction | null — action when a row is clicked (typically navigate-to-page)",
|
|
1030
|
+
},
|
|
1031
|
+
presentation: "density, columns, emphasisRules",
|
|
1032
|
+
filterableColumns: "string[] — columns users can filter on",
|
|
1033
|
+
},
|
|
1034
|
+
"metric-card": {
|
|
1035
|
+
description: "Single KPI metric display — shows a number with label, optional trend, and thresholds.",
|
|
1036
|
+
dataSource: "Required — type: 'structure', mode: 'aggregate' with a single aggregation",
|
|
1037
|
+
config: {
|
|
1038
|
+
aggregation: "'count' | 'sum' | 'average' | 'min' | 'max'",
|
|
1039
|
+
valueField: "string — field to aggregate (not needed for 'count')",
|
|
1040
|
+
label: "string — display label for the metric",
|
|
1041
|
+
format: "'number' | 'currency' | 'percentage' — value formatting",
|
|
1042
|
+
trend: "{ enabled: boolean, comparison: 'previous-period' }",
|
|
1043
|
+
thresholds: "{ green?: [min, max], amber?: [min, max], red?: [min, max] } — color-code the value",
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
"stat-group": {
|
|
1047
|
+
description: "Group of 2-5 related metrics displayed side by side. More compact than individual metric-cards.",
|
|
1048
|
+
dataSource: "Not set at block level — each item has its own dataSource",
|
|
1049
|
+
config: {
|
|
1050
|
+
items: "StatGroupItem[] — array of { id, label, dataSource, aggregation, valueField, format, trend }",
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
chart: {
|
|
1054
|
+
description: "Data visualization — bar, line, or pie chart.",
|
|
1055
|
+
dataSource: "Required — type: 'structure', mode: 'aggregate' with groupBy",
|
|
1056
|
+
config: {
|
|
1057
|
+
chartType: "'bar' | 'line' | 'pie'",
|
|
1058
|
+
categoryField: "string — field for x-axis / categories",
|
|
1059
|
+
valueField: "string — field for y-axis / values",
|
|
1060
|
+
groupByField: "string | null — optional field for series grouping",
|
|
1061
|
+
colors: "string[] | null — custom color palette",
|
|
1062
|
+
showLegend: "boolean (default: true)",
|
|
1063
|
+
},
|
|
1064
|
+
presentation: "chartType",
|
|
1065
|
+
},
|
|
1066
|
+
form: {
|
|
1067
|
+
description: "Data entry form with typed fields, validation, and submission. Used for creating or editing records.",
|
|
1068
|
+
dataSource: "Optional — if set with mode: 'single', pre-populates form for editing",
|
|
1069
|
+
config: {
|
|
1070
|
+
fields: "FormFieldDef[] — array of field definitions (see below)",
|
|
1071
|
+
},
|
|
1072
|
+
form_field_shape: {
|
|
1073
|
+
key: "string — maps to the record's data field name",
|
|
1074
|
+
label: "string — display label",
|
|
1075
|
+
type: "'text' | 'number' | 'email' | 'date' | 'boolean' | 'select' | 'textarea'",
|
|
1076
|
+
required: "boolean (default: false)",
|
|
1077
|
+
placeholder: "string | null",
|
|
1078
|
+
optionSource: "For 'select' type: { type: 'static' | 'dynamic', staticOptions?: [{label, value}], dynamicRef?: 'collection-id', labelField?, valueField? }",
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
"detail-fields": {
|
|
1082
|
+
description: "Read-only display of a single record's fields. Used for detail pages.",
|
|
1083
|
+
dataSource: "Required — type: 'structure', mode: 'single'",
|
|
1084
|
+
},
|
|
1085
|
+
"activity-feed": {
|
|
1086
|
+
description: "Chronological list of recent items/events, showing title, description, and timestamp.",
|
|
1087
|
+
dataSource: "Required — type: 'structure', mode: 'list'",
|
|
1088
|
+
config: {
|
|
1089
|
+
titleField: "string — field to use as item title",
|
|
1090
|
+
descriptionField: "string | null — field for item description",
|
|
1091
|
+
timestampField: "string — field for item timestamp",
|
|
1092
|
+
iconField: "string | null — field for item icon",
|
|
1093
|
+
maxItems: "number (default: 10)",
|
|
1094
|
+
},
|
|
1095
|
+
},
|
|
1096
|
+
"progress-indicator": {
|
|
1097
|
+
description: "Visual progress display — bar, ring, or badge. Shows completion toward a target.",
|
|
1098
|
+
dataSource: "Optional — can use static value or aggregate from data",
|
|
1099
|
+
config: {
|
|
1100
|
+
mode: "'bar' | 'ring' | 'badge'",
|
|
1101
|
+
label: "string",
|
|
1102
|
+
value: "number | null — static value (if no dataSource)",
|
|
1103
|
+
valueField: "string | null — field to aggregate (if using dataSource)",
|
|
1104
|
+
aggregation: "'count' | 'sum' | 'average' | 'percentage'",
|
|
1105
|
+
target: "number — the 100% target value",
|
|
1106
|
+
thresholds: "{ green?: [min, max], amber?: [min, max], red?: [min, max] }",
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
1109
|
+
kanban: {
|
|
1110
|
+
description: "Kanban board — drag-and-drop cards grouped by a status/category field.",
|
|
1111
|
+
dataSource: "Required — type: 'structure', mode: 'list'",
|
|
1112
|
+
config: {
|
|
1113
|
+
groupByField: "string — field to group cards by (e.g., 'status')",
|
|
1114
|
+
columnOrder: "string[] | null — explicit column order (e.g., ['todo', 'in-progress', 'done'])",
|
|
1115
|
+
cardTitleField: "string — field for card title",
|
|
1116
|
+
cardSubtitleField: "string | null — field for card subtitle",
|
|
1117
|
+
cardBadgeField: "string | null — field for a badge/tag on the card",
|
|
1118
|
+
},
|
|
1119
|
+
},
|
|
1120
|
+
calendar: {
|
|
1121
|
+
description: "Calendar view — displays records as events on a month/week/day calendar.",
|
|
1122
|
+
dataSource: "Required — type: 'structure', mode: 'list'",
|
|
1123
|
+
config: {
|
|
1124
|
+
dateField: "string — field for event start date",
|
|
1125
|
+
endDateField: "string | null — field for event end date (for multi-day events)",
|
|
1126
|
+
titleField: "string — field for event title",
|
|
1127
|
+
colorField: "string | null — field to determine event color",
|
|
1128
|
+
defaultView: "'month' | 'week' | 'day' (default: 'month')",
|
|
1129
|
+
eventClickAction: "BlockAction | null — action when an event is clicked",
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1132
|
+
"filter-bar": {
|
|
1133
|
+
description: "Global filter bar for dashboards — provides date range pickers and dropdown filters that control other blocks on the page.",
|
|
1134
|
+
dataSource: "Not required",
|
|
1135
|
+
config: {
|
|
1136
|
+
dateField: "string | null — field to filter by date range",
|
|
1137
|
+
datePresets: "('today' | '7d' | '30d' | '90d' | 'custom')[] — available date range presets",
|
|
1138
|
+
dropdownFilters: "{ field: string, label: string }[] — dropdown filter options",
|
|
1139
|
+
},
|
|
1140
|
+
},
|
|
1141
|
+
"related-list": {
|
|
1142
|
+
description: "Displays related records from another collection. Similar to data-table but used in detail pages to show linked data (e.g., orders belonging to a customer).",
|
|
1143
|
+
dataSource: "Required — type: 'structure', mode: 'list' (with filters to scope to the parent record)",
|
|
1144
|
+
allowed_actions: ["navigate-to-page", "invoke-trigger"],
|
|
1145
|
+
default_activation: "row-click",
|
|
1146
|
+
param_source: "row",
|
|
1147
|
+
},
|
|
1148
|
+
"field-display": {
|
|
1149
|
+
description: "Read-only display of a single field value with label. Used in detail pages to show individual fields with formatting.",
|
|
1150
|
+
dataSource: "Required — type: 'structure', mode: 'single'",
|
|
1151
|
+
allowed_actions: ["invoke-trigger", "navigate-to-page"],
|
|
1152
|
+
default_activation: "button",
|
|
1153
|
+
param_source: "record",
|
|
1154
|
+
},
|
|
1155
|
+
"field-group": {
|
|
1156
|
+
description: "Group of editable form fields. Used in form pages for structured data entry with multiple fields in a logical group.",
|
|
1157
|
+
dataSource: "Optional — if set with mode: 'single', pre-populates for editing",
|
|
1158
|
+
allowed_actions: [
|
|
1159
|
+
"create-record",
|
|
1160
|
+
"update-record",
|
|
1161
|
+
"invoke-trigger",
|
|
1162
|
+
],
|
|
1163
|
+
default_activation: "form-submit",
|
|
1164
|
+
param_source: "form",
|
|
1165
|
+
},
|
|
1166
|
+
"text-input": {
|
|
1167
|
+
description: "Single-line text input field for form pages.",
|
|
1168
|
+
dataSource: "Not required (value comes from user input)",
|
|
1169
|
+
category: "field-block",
|
|
1170
|
+
},
|
|
1171
|
+
"number-input": {
|
|
1172
|
+
description: "Numeric input field for form pages. Supports min/max constraints.",
|
|
1173
|
+
dataSource: "Not required",
|
|
1174
|
+
category: "field-block",
|
|
1175
|
+
},
|
|
1176
|
+
select: {
|
|
1177
|
+
description: "Dropdown select input for form pages. Options can be static or dynamically loaded from a collection.",
|
|
1178
|
+
dataSource: "Not required (static options) or optional (dynamic options from collection)",
|
|
1179
|
+
category: "field-block",
|
|
1180
|
+
},
|
|
1181
|
+
checkbox: {
|
|
1182
|
+
description: "Boolean checkbox input for form pages.",
|
|
1183
|
+
dataSource: "Not required",
|
|
1184
|
+
category: "field-block",
|
|
1185
|
+
},
|
|
1186
|
+
"date-picker": {
|
|
1187
|
+
description: "Date/datetime picker input for form pages.",
|
|
1188
|
+
dataSource: "Not required",
|
|
1189
|
+
category: "field-block",
|
|
1190
|
+
},
|
|
1191
|
+
textarea: {
|
|
1192
|
+
description: "Multi-line text input for form pages. Use for long text, descriptions, notes.",
|
|
1193
|
+
dataSource: "Not required",
|
|
1194
|
+
category: "field-block",
|
|
1195
|
+
},
|
|
1196
|
+
"file-upload": {
|
|
1197
|
+
description: "File upload input for form pages. Stores files in blob storage.",
|
|
1198
|
+
dataSource: "Not required",
|
|
1199
|
+
category: "field-block",
|
|
1200
|
+
},
|
|
1201
|
+
field: {
|
|
1202
|
+
description: "Generic field block — base type for custom field rendering in form pages.",
|
|
1203
|
+
dataSource: "Not required",
|
|
1204
|
+
category: "field-block",
|
|
1205
|
+
},
|
|
1206
|
+
markdown: {
|
|
1207
|
+
description: "Static markdown content block. Renders markdown text on the page. Must have non-empty content to pass publishing validation.",
|
|
1208
|
+
dataSource: "Not required",
|
|
1209
|
+
requires: "content property (string) — the markdown text",
|
|
1210
|
+
category: "content-block",
|
|
1211
|
+
},
|
|
1212
|
+
"static-html": {
|
|
1213
|
+
description: "Static HTML content block. Renders raw HTML on the page. Must have non-empty content to pass publishing validation.",
|
|
1214
|
+
dataSource: "Not required",
|
|
1215
|
+
requires: "content property (string) — the HTML markup",
|
|
1216
|
+
category: "content-block",
|
|
1217
|
+
},
|
|
1218
|
+
},
|
|
1219
|
+
block_categories: {
|
|
1220
|
+
"data-block": "Blocks that display data from collections (data-table, chart, metric-card, stat-group, detail-fields, related-list, field-display, activity-feed, kanban, calendar, progress-indicator)",
|
|
1221
|
+
"field-block": "Input blocks for form pages (text-input, number-input, select, checkbox, date-picker, textarea, file-upload, field, field-group). Form pages must have at least one field block.",
|
|
1222
|
+
"content-block": "Static content blocks (markdown, static-html). Must have non-empty 'content' property.",
|
|
1223
|
+
"layout-block": "Blocks that control page-level behavior (filter-bar)",
|
|
1224
|
+
},
|
|
1225
|
+
action_mappings: {
|
|
1226
|
+
description: "Not all action types are valid on all block types. The publish validator warns when an action type is not recommended for a block type.",
|
|
1227
|
+
per_block_type: {
|
|
1228
|
+
"data-table": {
|
|
1229
|
+
allowed_actions: ["navigate-to-page", "invoke-trigger"],
|
|
1230
|
+
default_activation: "row-click",
|
|
1231
|
+
param_source: "row",
|
|
1232
|
+
},
|
|
1233
|
+
"related-list": {
|
|
1234
|
+
allowed_actions: ["navigate-to-page", "invoke-trigger"],
|
|
1235
|
+
default_activation: "row-click",
|
|
1236
|
+
param_source: "row",
|
|
1237
|
+
},
|
|
1238
|
+
"field-display": {
|
|
1239
|
+
allowed_actions: ["invoke-trigger", "navigate-to-page"],
|
|
1240
|
+
default_activation: "button",
|
|
1241
|
+
param_source: "record",
|
|
1242
|
+
},
|
|
1243
|
+
"field-group": {
|
|
1244
|
+
allowed_actions: [
|
|
1245
|
+
"create-record",
|
|
1246
|
+
"update-record",
|
|
1247
|
+
"invoke-trigger",
|
|
1248
|
+
],
|
|
1249
|
+
default_activation: "form-submit",
|
|
1250
|
+
param_source: "form",
|
|
1251
|
+
},
|
|
1252
|
+
actions: {
|
|
1253
|
+
allowed_actions: [
|
|
1254
|
+
"invoke-trigger",
|
|
1255
|
+
"navigate-to-page",
|
|
1256
|
+
"start-orchestration",
|
|
1257
|
+
"enqueue-job",
|
|
1258
|
+
],
|
|
1259
|
+
default_activation: "button",
|
|
1260
|
+
param_source: "none",
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
}, null, 2),
|
|
1265
|
+
},
|
|
1266
|
+
],
|
|
1267
|
+
});
|
|
1268
|
+
}));
|
|
1269
|
+
// ── Page Actions ───────────────────────────────────────────────────
|
|
1270
|
+
server.tool("describe_page_actions", "Get all available page action types and their configuration shapes. Actions define interactive behaviors on blocks (button clicks, row clicks, form submissions).", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
1271
|
+
return ({
|
|
1272
|
+
content: [
|
|
1273
|
+
{
|
|
1274
|
+
type: "text",
|
|
1275
|
+
text: JSON.stringify({
|
|
1276
|
+
domain: "Page Actions",
|
|
1277
|
+
description: "Actions define what happens when a user interacts with a block — clicking a row, pressing a button, or submitting a form.",
|
|
1278
|
+
action_shape: {
|
|
1279
|
+
id: "string — unique ID for the action (use a UUID)",
|
|
1280
|
+
type: "ActionType — what the action does (see action_types below)",
|
|
1281
|
+
label: "string — button/link text (e.g., 'View Details', 'Create Order')",
|
|
1282
|
+
targetRef: "string — reference to the target resource (page slug, structure ID, orchestration ID, etc.)",
|
|
1283
|
+
config: "object | null — action-type-specific configuration",
|
|
1284
|
+
confirmationRequired: "boolean — if true, shows a confirmation dialog before executing",
|
|
1285
|
+
activation: "'row-click' | 'button' | 'form-submit' — how the action is triggered",
|
|
1286
|
+
paramConfig: {
|
|
1287
|
+
source: "'row' | 'record' | 'form' | 'none' — where to get parameters",
|
|
1288
|
+
mode: "'all' | 'selected' — pass all fields or only selected ones",
|
|
1289
|
+
selectedFields: "string[] | null — specific fields to pass (when mode is 'selected')",
|
|
1290
|
+
},
|
|
1291
|
+
presentation: {
|
|
1292
|
+
display: "'button' | 'icon' | 'icon-button'",
|
|
1293
|
+
icon: "string | null — icon name",
|
|
1294
|
+
color: "'primary' | 'secondary' | 'destructive' | 'neutral'",
|
|
1295
|
+
},
|
|
1296
|
+
postSubmit: {
|
|
1297
|
+
behavior: "'message' | 'navigate'",
|
|
1298
|
+
message: "string | null — success message to show (when behavior is 'message')",
|
|
1299
|
+
targetPageSlug: "string | null — page to navigate to (when behavior is 'navigate')",
|
|
1300
|
+
},
|
|
1301
|
+
executionMode: "'fire-and-forget' | 'fire-and-poll' — whether to wait for completion",
|
|
1302
|
+
},
|
|
1303
|
+
action_types: {
|
|
1304
|
+
"navigate-to-page": {
|
|
1305
|
+
description: "Navigate to another page. Use for detail-view links and list-to-detail navigation.",
|
|
1306
|
+
targetRef: "Page slug to navigate to",
|
|
1307
|
+
typical_activation: "row-click",
|
|
1308
|
+
paramConfig: "Use source: 'row', mode: 'selected', selectedFields: ['id'] to pass the record ID to the detail page",
|
|
1309
|
+
example: {
|
|
1310
|
+
id: "view-detail",
|
|
1311
|
+
type: "navigate-to-page",
|
|
1312
|
+
label: "View Details",
|
|
1313
|
+
targetRef: "customer-detail",
|
|
1314
|
+
activation: "row-click",
|
|
1315
|
+
paramConfig: {
|
|
1316
|
+
source: "row",
|
|
1317
|
+
mode: "selected",
|
|
1318
|
+
selectedFields: ["id"],
|
|
1319
|
+
},
|
|
1320
|
+
},
|
|
1321
|
+
},
|
|
1322
|
+
"create-record": {
|
|
1323
|
+
description: "Create a new record in a collection. Typically used with form blocks.",
|
|
1324
|
+
targetRef: "Collection ID (UUID) to create the record in",
|
|
1325
|
+
typical_activation: "form-submit",
|
|
1326
|
+
paramConfig: "Use source: 'form', mode: 'all'",
|
|
1327
|
+
postSubmit: "Usually { behavior: 'message', message: 'Record created successfully' } or { behavior: 'navigate', targetPageSlug: 'list-page' }",
|
|
1328
|
+
},
|
|
1329
|
+
"update-record": {
|
|
1330
|
+
description: "Update an existing record. Used with form blocks pre-populated with record data.",
|
|
1331
|
+
targetRef: "Collection ID (UUID)",
|
|
1332
|
+
typical_activation: "form-submit",
|
|
1333
|
+
paramConfig: "Use source: 'form', mode: 'all'",
|
|
1334
|
+
},
|
|
1335
|
+
"invoke-trigger": {
|
|
1336
|
+
description: "Invoke an on-demand compute trigger. Use for server-side actions like sending emails, running calculations, etc.",
|
|
1337
|
+
targetRef: "Trigger ID (UUID)",
|
|
1338
|
+
typical_activation: "button",
|
|
1339
|
+
paramConfig: "Use source: 'row' to pass the current record as payload, or source: 'none' for no payload",
|
|
1340
|
+
confirmationRequired: "Recommended: true for destructive or costly operations",
|
|
1341
|
+
executionMode: "'fire-and-forget' for async, 'fire-and-poll' to show loading state",
|
|
1342
|
+
},
|
|
1343
|
+
"start-orchestration": {
|
|
1344
|
+
description: "Start an orchestration workflow run. Use for multi-step processes.",
|
|
1345
|
+
targetRef: "Orchestration ID (UUID)",
|
|
1346
|
+
typical_activation: "button",
|
|
1347
|
+
executionMode: "'fire-and-poll' recommended — orchestrations are long-running",
|
|
1348
|
+
},
|
|
1349
|
+
"enqueue-job": {
|
|
1350
|
+
description: "Enqueue a background job. Use for bulk operations or long-running tasks.",
|
|
1351
|
+
targetRef: "Job type identifier",
|
|
1352
|
+
typical_activation: "button",
|
|
1353
|
+
executionMode: "'fire-and-forget'",
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
tips: [
|
|
1357
|
+
"Every action needs an 'id' — use UUIDs for uniqueness",
|
|
1358
|
+
"Use 'navigate-to-page' with activation 'row-click' for list-to-detail navigation",
|
|
1359
|
+
"Form pages typically have one action with activation 'form-submit' and type 'create-record' or 'update-record'",
|
|
1360
|
+
"Set confirmationRequired: true for destructive actions (delete, bulk update)",
|
|
1361
|
+
"Use postSubmit to control what happens after a form submission",
|
|
1362
|
+
"Actions on data-table blocks often use paramConfig with source: 'row' to pass the clicked record's data",
|
|
1363
|
+
],
|
|
1364
|
+
}, null, 2),
|
|
1365
|
+
},
|
|
1366
|
+
],
|
|
1367
|
+
});
|
|
1368
|
+
}));
|
|
1369
|
+
// ── Navigation ─────────────────────────────────────────────────────
|
|
1370
|
+
server.tool("describe_navigation", "Get the full schema for the workspace navigation configuration. Explains how to construct the sidebar/nav shell that wraps published pages — item types, grouping, branding, and limits.", {}, () => __awaiter(this, void 0, void 0, function* () {
|
|
1371
|
+
return ({
|
|
1372
|
+
content: [
|
|
1373
|
+
{
|
|
1374
|
+
type: "text",
|
|
1375
|
+
text: JSON.stringify({
|
|
1376
|
+
domain: "Navigation",
|
|
1377
|
+
description: "The navigation configuration controls the sidebar/nav shell that wraps published pages. When enabled, all pages in the workspace share a common sidebar with branding, grouped links, and external links. When disabled or deleted, pages render standalone.",
|
|
1378
|
+
tools: {
|
|
1379
|
+
get_navigation: "Returns the current config enriched with page metadata (name, slug, isPublished for each page item)",
|
|
1380
|
+
set_navigation: "Create or update the config. Pass the full config object (not a partial — it replaces the existing config).",
|
|
1381
|
+
delete_navigation: "Remove the config entirely. Pages revert to standalone rendering.",
|
|
1382
|
+
},
|
|
1383
|
+
config_shape: {
|
|
1384
|
+
enabled: "boolean — master toggle. If false, the nav shell is hidden even if items exist.",
|
|
1385
|
+
branding: {
|
|
1386
|
+
logoUrl: "string | undefined — URL to the workspace logo displayed at the top of the sidebar. Must be a valid URL or empty string.",
|
|
1387
|
+
displayName: "string | undefined — workspace display name shown in the sidebar header. Max 100 characters.",
|
|
1388
|
+
},
|
|
1389
|
+
items: "NavEntry[] — ordered array of navigation items (max 50). Each entry is one of three types: 'group', 'page', or 'external-link'.",
|
|
1390
|
+
},
|
|
1391
|
+
item_types: {
|
|
1392
|
+
page: {
|
|
1393
|
+
description: "Links to a published page in the workspace. The sidebar displays the page's name and slug automatically (resolved from pageId).",
|
|
1394
|
+
shape: {
|
|
1395
|
+
type: "'page' (literal)",
|
|
1396
|
+
id: "string (UUID) — unique ID for this nav item",
|
|
1397
|
+
pageId: "string (UUID) — the page ID to link to. Must reference an existing page.",
|
|
1398
|
+
icon: "string | undefined — icon name (max 50 chars). Optional.",
|
|
1399
|
+
},
|
|
1400
|
+
note: "GET response enriches page items with name, slug, and isPublished fields. When setting, you only need type, id, and pageId.",
|
|
1401
|
+
},
|
|
1402
|
+
group: {
|
|
1403
|
+
description: "A collapsible group that contains page items and/or external links. Use to organize nav items by category.",
|
|
1404
|
+
shape: {
|
|
1405
|
+
type: "'group' (literal)",
|
|
1406
|
+
id: "string (UUID) — unique ID for this group",
|
|
1407
|
+
label: "string — group heading displayed in the sidebar. 1-50 characters.",
|
|
1408
|
+
children: "(NavPageItem | NavExternalLink)[] — ordered array of child items (max 20). Cannot contain nested groups.",
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
"external-link": {
|
|
1412
|
+
description: "Links to an external URL. Opens in a new tab. Use for documentation, support links, or other external resources.",
|
|
1413
|
+
shape: {
|
|
1414
|
+
type: "'external-link' (literal)",
|
|
1415
|
+
id: "string (UUID) — unique ID for this nav item",
|
|
1416
|
+
label: "string — link text displayed in the sidebar. 1-50 characters.",
|
|
1417
|
+
url: "string — the external URL. Must be a valid URL.",
|
|
1418
|
+
icon: "string | undefined — icon name (max 50 chars). Optional.",
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
},
|
|
1422
|
+
limits: {
|
|
1423
|
+
max_top_level_items: 50,
|
|
1424
|
+
max_children_per_group: 20,
|
|
1425
|
+
max_label_length: 50,
|
|
1426
|
+
max_display_name_length: 100,
|
|
1427
|
+
nesting: "Groups cannot be nested — children must be 'page' or 'external-link' items only.",
|
|
1428
|
+
},
|
|
1429
|
+
example: {
|
|
1430
|
+
config: {
|
|
1431
|
+
enabled: true,
|
|
1432
|
+
branding: {
|
|
1433
|
+
displayName: "Acme Corp",
|
|
1434
|
+
logoUrl: "https://acme.com/logo.png",
|
|
1435
|
+
},
|
|
1436
|
+
items: [
|
|
1437
|
+
{
|
|
1438
|
+
type: "page",
|
|
1439
|
+
id: "nav-dashboard",
|
|
1440
|
+
pageId: "<dashboard-page-uuid>",
|
|
1441
|
+
icon: "LayoutDashboard",
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
type: "group",
|
|
1445
|
+
id: "nav-data",
|
|
1446
|
+
label: "Data Management",
|
|
1447
|
+
children: [
|
|
1448
|
+
{
|
|
1449
|
+
type: "page",
|
|
1450
|
+
id: "nav-customers",
|
|
1451
|
+
pageId: "<customers-page-uuid>",
|
|
1452
|
+
icon: "Users",
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
type: "page",
|
|
1456
|
+
id: "nav-orders",
|
|
1457
|
+
pageId: "<orders-page-uuid>",
|
|
1458
|
+
icon: "ShoppingCart",
|
|
1459
|
+
},
|
|
1460
|
+
],
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
type: "external-link",
|
|
1464
|
+
id: "nav-docs",
|
|
1465
|
+
label: "Documentation",
|
|
1466
|
+
url: "https://docs.acme.com",
|
|
1467
|
+
icon: "BookOpen",
|
|
1468
|
+
},
|
|
1469
|
+
],
|
|
1470
|
+
},
|
|
1471
|
+
},
|
|
1472
|
+
tips: [
|
|
1473
|
+
"Use UUIDs for all 'id' fields — they must be unique across the config",
|
|
1474
|
+
"Page items only need 'type', 'id', and 'pageId' — name and slug are resolved automatically from the page",
|
|
1475
|
+
"Groups cannot be nested — keep the hierarchy to one level deep",
|
|
1476
|
+
"set_navigation replaces the entire config — always send the full config, not a partial update",
|
|
1477
|
+
"Pages don't need to be published to appear in the nav config, but unpublished pages will show as disabled in the sidebar",
|
|
1478
|
+
"The showInNavShell field on a page (via update_page) controls whether the page appears in auto-generated nav suggestions",
|
|
1479
|
+
],
|
|
1480
|
+
}, null, 2),
|
|
1481
|
+
},
|
|
1482
|
+
],
|
|
1483
|
+
});
|
|
1484
|
+
}));
|
|
1485
|
+
}
|