@centrali-io/centrali-mcp 4.2.16 → 4.3.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/tools/describe.js +72 -4
- package/dist/tools/pages.js +6 -4
- package/package.json +1 -1
- package/src/tools/describe.ts +80 -4
- package/src/tools/pages.ts +6 -4
package/dist/tools/describe.js
CHANGED
|
@@ -942,7 +942,7 @@ function registerDescribeTools(server) {
|
|
|
942
942
|
"Always validate_page before publish_page — publishing will reject if there are errors",
|
|
943
943
|
"Set access policy with set_page_access_policy before publishing if you need auth",
|
|
944
944
|
"Use variable bindings on data sources to scope data dynamically — bind to URL params, auth context, record context, or static defaults",
|
|
945
|
-
"For list→detail navigation: set useQueryParams:true on navigate-to-page actions, then bind the detail page's data source variables to { source: 'url', param: 'id' }",
|
|
945
|
+
"For list→detail navigation: set useQueryParams:true on navigate-to-page actions, then bind the detail page's data source variables to { source: 'url', param: 'id' }. Use config.paramMapping to rename fields if source and target use different names (e.g., { requestId: 'id' })",
|
|
946
946
|
"For user-scoped views (e.g., My Approvals): bind a variable to { source: 'auth', field: 'userId' } and use a smart query with {{assigneeId}}",
|
|
947
947
|
],
|
|
948
948
|
multi_page_pattern: {
|
|
@@ -1042,8 +1042,10 @@ function registerDescribeTools(server) {
|
|
|
1042
1042
|
},
|
|
1043
1043
|
behavior: {
|
|
1044
1044
|
query_type: "For type:'query' — resolved variables substitute into smart query {{placeholders}} before execution",
|
|
1045
|
-
structure_type: "For type:'structure' — resolved variables become equality filters on the collection",
|
|
1045
|
+
structure_type: "For type:'structure' — resolved variables become equality filters on the collection. IMPORTANT: the variable name 'id' is special — it matches the system record UUID (used for single-record fetch by ID). ALL OTHER variable names filter against user data fields and are automatically prefixed with 'data.' (e.g., variable 'requestId' filters on data.requestId in the JSONB column). System columns like createdAt, updatedAt, status do NOT get the data. prefix.",
|
|
1046
1046
|
unresolved: "If any variable cannot be resolved, the block returns an empty result with a variableError message — NEVER unfiltered data",
|
|
1047
|
+
detail_page_pattern: "For a detail page: use variables: { id: { source: 'url', param: 'id' } } on the primary record-card block to fetch by system ID. For related-list blocks, use the actual data field name (e.g., variables: { requestId: { source: 'url', param: 'id' } }) — this filters related records where data.requestId matches the URL param. The 'id' variable fetches a record BY its UUID; other variable names filter records WHERE a field equals the value.",
|
|
1048
|
+
reference_fields: "Reference fields (foreign keys between collections) store the target record's system UUID. To filter a related list by a reference field, bind the variable to the parent record's system ID (from URL param), not a custom field value. Example: approval_steps has a data.requestId field that stores the governance_request's UUID.",
|
|
1047
1049
|
},
|
|
1048
1050
|
precedence: "url > record > auth > static (highest to lowest priority)",
|
|
1049
1051
|
},
|
|
@@ -1340,11 +1342,65 @@ function registerDescribeTools(server) {
|
|
|
1340
1342
|
requires: "content property (string) — the HTML markup",
|
|
1341
1343
|
category: "content-block",
|
|
1342
1344
|
},
|
|
1345
|
+
"data-html": {
|
|
1346
|
+
description: "Data-bound HTML block. Write HTML templates with {{fieldName}} placeholders that resolve against a data source. Supports two modes: LIST mode (default) renders the template ONCE PER RECORD — perfect for card grids, custom list layouts, or any repeating template. SINGLE mode renders the template once for a single record (detail pages). Records are auto-flattened so {{firstName}} works directly — no need for {{data.firstName}}. Output is sanitized via DOMPurify. No JavaScript allowed.",
|
|
1347
|
+
dataSource: "Required — type: 'structure' or 'query'. Data is resolved server-side and injected into the template.",
|
|
1348
|
+
requires: "content property (string) — HTML template with {{field}} placeholders. Must have non-empty content to pass publishing validation.",
|
|
1349
|
+
template_syntax: "{{fieldName}} for top-level fields, {{data.status}} for nested. Unresolved placeholders render as empty string.",
|
|
1350
|
+
modes: {
|
|
1351
|
+
list: "Default. Template repeats for EACH record. Write a single card/row template and it renders N times for N records. Example: a styled customer card template produces a grid of cards.",
|
|
1352
|
+
single: "Template renders once for ONE record. Use with mode:'single' and a variable binding like { id: { source: 'url', param: 'id' } } to fetch a specific record.",
|
|
1353
|
+
},
|
|
1354
|
+
example_list_mode: {
|
|
1355
|
+
blockType: "data-html",
|
|
1356
|
+
dataSource: { type: "structure", ref: "<collection-uuid>", recordSlug: "customers", config: { sort: [{ field: "createdAt", direction: "desc" }], limit: 20 } },
|
|
1357
|
+
content: "<div style='display:inline-block; width:280px; margin:8px; padding:16px; border:1px solid #e5e7eb; border-radius:8px; vertical-align:top;'><h3>{{firstName}} {{lastName}}</h3><p style='color:#6b7280;'>{{email}}</p></div>",
|
|
1358
|
+
},
|
|
1359
|
+
example_single_mode: {
|
|
1360
|
+
blockType: "data-html",
|
|
1361
|
+
dataSource: { type: "structure", ref: "<collection-uuid>", mode: "single", config: {}, variables: { id: { source: "url", param: "id" } } },
|
|
1362
|
+
content: "<div style='padding:16px; border:1px solid #e5e7eb; border-radius:8px;'><h2>{{firstName}} {{lastName}}</h2><p>{{email}}</p><p>Company: {{company}}</p></div>",
|
|
1363
|
+
},
|
|
1364
|
+
IMPORTANT: "No iteration syntax needed for lists. Just write a single template — the runtime repeats it automatically for each record. Records are auto-flattened: use {{fieldName}} directly, not {{data.fieldName}}.",
|
|
1365
|
+
category: "data-block",
|
|
1366
|
+
},
|
|
1367
|
+
"image": {
|
|
1368
|
+
description: "Image block. Displays an image from a URL with responsive sizing, lazy loading, optional caption, alignment, and link-on-click.",
|
|
1369
|
+
dataSource: "Not required",
|
|
1370
|
+
requires: "src property (string) — the image URL. Optional: alt, caption, alignment (left|center|right), maxWidth, linkUrl.",
|
|
1371
|
+
category: "media-block",
|
|
1372
|
+
},
|
|
1373
|
+
"video": {
|
|
1374
|
+
description: "Video block. Embeds YouTube/Vimeo videos via iframe or direct MP4/WebM via native <video>. Auto-detects provider from URL.",
|
|
1375
|
+
dataSource: "Not required",
|
|
1376
|
+
requires: "src property (string) — video URL (YouTube, Vimeo, or direct). Optional: autoplay (boolean, muted only), loop, controls (default true), poster, caption.",
|
|
1377
|
+
category: "media-block",
|
|
1378
|
+
},
|
|
1379
|
+
"modal": {
|
|
1380
|
+
description: "Declarative modal block. Invisible by default — renders as an overlay when triggered by an 'open-modal' action. Content can be static HTML, markdown, or data-bound HTML. Supports actions (buttons) inside the modal for confirm/cancel flows. Uses Radix Dialog.",
|
|
1381
|
+
dataSource: "Optional — only needed if contentType is 'data-html'",
|
|
1382
|
+
requires: "content property (string), contentType ('html' | 'markdown' | 'data-html', default 'html'). Optional: title, modalSize ('sm' | 'md' | 'lg' | 'fullscreen', default 'md').",
|
|
1383
|
+
actions: "Optional actions array — renders as buttons at the bottom of the modal. Supports close-modal (close the modal), invoke-trigger, navigate-to-page, start-orchestration, open-modal (open another modal). Use close-modal with targetRef set to this modal's own ID for a 'Cancel' button.",
|
|
1384
|
+
trigger: "Add an 'open-modal' action on another block with targetRef set to this modal block's ID. The modal opens when the action fires.",
|
|
1385
|
+
category: "interactive-block",
|
|
1386
|
+
example_confirm_modal: {
|
|
1387
|
+
blockType: "modal",
|
|
1388
|
+
title: "Confirm Delete",
|
|
1389
|
+
modalSize: "sm",
|
|
1390
|
+
contentType: "html",
|
|
1391
|
+
content: "<p>Are you sure you want to delete this item? This action cannot be undone.</p>",
|
|
1392
|
+
actions: [
|
|
1393
|
+
{ id: "cancel", type: "close-modal", label: "Cancel", targetRef: "THIS_MODAL_ID", presentation: { color: "neutral" } },
|
|
1394
|
+
{ id: "confirm", type: "invoke-trigger", label: "Delete", targetRef: "TRIGGER_ID", presentation: { color: "destructive" } },
|
|
1395
|
+
],
|
|
1396
|
+
},
|
|
1397
|
+
},
|
|
1343
1398
|
},
|
|
1344
1399
|
block_categories: {
|
|
1345
|
-
"data-block": "Blocks that display data from collections (data-table, chart, metric-card, stat-group, field-display, related-list, activity-feed, kanban, calendar, progress-indicator)",
|
|
1400
|
+
"data-block": "Blocks that display data from collections (data-table, chart, metric-card, stat-group, field-display, related-list, activity-feed, kanban, calendar, progress-indicator, data-html)",
|
|
1346
1401
|
"form-block": "Use 'field-group' blockType with a 'fields' array for form pages. Do NOT use individual field block types (text-input, select, etc.) — the runtime renders field types from the field-group's fields array.",
|
|
1347
1402
|
"content-block": "Static content blocks (markdown, static-html). Must have non-empty 'content' property.",
|
|
1403
|
+
"media-block": "Media blocks (image, video). Require a 'src' URL property.",
|
|
1348
1404
|
"layout-block": "Blocks that control page-level behavior (filter-bar)",
|
|
1349
1405
|
},
|
|
1350
1406
|
action_mappings: {
|
|
@@ -1431,16 +1487,18 @@ function registerDescribeTools(server) {
|
|
|
1431
1487
|
targetRef: "Page slug to navigate to",
|
|
1432
1488
|
typical_activation: "row-click",
|
|
1433
1489
|
paramConfig: "Use source: 'row', mode: 'selected', selectedFields: ['id'] to pass the record ID to the detail page",
|
|
1490
|
+
paramMapping: "Optional config.paramMapping: { sourceField: 'targetParam' } to rename fields in the URL. E.g., { requestId: 'id' } passes ?id=<value> instead of ?requestId=<value>. Only applies when useQueryParams is true.",
|
|
1434
1491
|
example: {
|
|
1435
1492
|
id: "view-detail",
|
|
1436
1493
|
type: "navigate-to-page",
|
|
1437
1494
|
label: "View Details",
|
|
1438
1495
|
targetRef: "customer-detail",
|
|
1439
1496
|
activation: "row-click",
|
|
1497
|
+
config: { useQueryParams: true, paramMapping: { requestId: "id" } },
|
|
1440
1498
|
paramConfig: {
|
|
1441
1499
|
source: "row",
|
|
1442
1500
|
mode: "selected",
|
|
1443
|
-
selectedFields: ["
|
|
1501
|
+
selectedFields: ["requestId"],
|
|
1444
1502
|
},
|
|
1445
1503
|
},
|
|
1446
1504
|
},
|
|
@@ -1477,6 +1535,16 @@ function registerDescribeTools(server) {
|
|
|
1477
1535
|
typical_activation: "button",
|
|
1478
1536
|
executionMode: "'fire-and-forget'",
|
|
1479
1537
|
},
|
|
1538
|
+
"open-modal": {
|
|
1539
|
+
description: "Open a modal block. Client-side only — no backend call. The modal block must be defined on the same page with blockType 'modal'.",
|
|
1540
|
+
targetRef: "The modal block's ID (UUID) — must match a block with blockType 'modal' on this page.",
|
|
1541
|
+
typical_activation: "button",
|
|
1542
|
+
},
|
|
1543
|
+
"close-modal": {
|
|
1544
|
+
description: "Close an open modal. Client-side only.",
|
|
1545
|
+
targetRef: "The modal block's ID (UUID) to close.",
|
|
1546
|
+
typical_activation: "button",
|
|
1547
|
+
},
|
|
1480
1548
|
},
|
|
1481
1549
|
tips: [
|
|
1482
1550
|
"Every action needs an 'id' — use UUIDs for uniqueness",
|
package/dist/tools/pages.js
CHANGED
|
@@ -144,7 +144,7 @@ function registerPageTools(server, sdk, centraliUrl, workspaceId) {
|
|
|
144
144
|
}));
|
|
145
145
|
server.tool("create_page", `Create a new page in the workspace. A page is a UI view backed by data from structures. Specify the page type: 'list' for data tables, 'detail' for single-record views, 'form' for data entry, 'dashboard' for metrics and charts.
|
|
146
146
|
|
|
147
|
-
Navigate-to-page actions can use config.useQueryParams: true to pass selected row fields as URL query params to the target page. Pair with paramConfig: { source: 'row', mode: 'selected', selectedFields: ['id'] } to control which fields are passed. The target detail page can then use variable bindings with { source: 'url', param: 'id' } to read those params.`, {
|
|
147
|
+
Navigate-to-page actions can use config.useQueryParams: true to pass selected row fields as URL query params to the target page. Pair with paramConfig: { source: 'row', mode: 'selected', selectedFields: ['id'] } to control which fields are passed. Use config.paramMapping: { sourceField: 'targetParam' } to rename fields in the URL (e.g., { requestId: 'id' } passes ?id=<value> instead of ?requestId=<value>). The target detail page can then use variable bindings with { source: 'url', param: 'id' } to read those params.`, {
|
|
148
148
|
name: zod_1.z.string().describe("Display name for the page (e.g., 'Customer List')"),
|
|
149
149
|
slug: zod_1.z.string().describe("URL-safe slug (e.g., 'customer-list')"),
|
|
150
150
|
pageType: zod_1.z
|
|
@@ -223,12 +223,14 @@ Variable binding sources:
|
|
|
223
223
|
- { source: 'static', value: 'active' } — literal default value
|
|
224
224
|
|
|
225
225
|
For query data sources: variables substitute into smart query {{placeholders}}.
|
|
226
|
-
For structure data sources: variables become equality filters.
|
|
226
|
+
For structure data sources: variables become equality filters. IMPORTANT: variable name 'id' is special — it fetches the record BY its system UUID. All other variable names filter against data.<fieldName> in the JSONB column (e.g., variable 'requestId' → filters on data.requestId). Reference fields between collections store system UUIDs.
|
|
227
227
|
|
|
228
228
|
Common patterns:
|
|
229
|
-
- Detail page
|
|
229
|
+
- Detail page primary record: { id: { source: 'url', param: 'id' } } — fetches single record by system ID
|
|
230
|
+
- Detail page related list: { requestId: { source: 'url', param: 'id' } } — filters where data.requestId = URL param
|
|
230
231
|
- User-scoped view: { assigneeId: { source: 'auth', field: 'userId' } }
|
|
231
|
-
- Navigate with params: action config { useQueryParams: true } + paramConfig { source: 'row', mode: 'selected', selectedFields: ['id'] }
|
|
232
|
+
- Navigate with params: action config { useQueryParams: true } + paramConfig { source: 'row', mode: 'selected', selectedFields: ['id'] }
|
|
233
|
+
- Rename params: action config { useQueryParams: true, paramMapping: { requestId: 'id' } } — passes ?id=<value> instead of ?requestId=<value>`, {
|
|
232
234
|
pageId: zod_1.z.string().describe("The page ID (UUID)"),
|
|
233
235
|
definition: zod_1.z
|
|
234
236
|
.record(zod_1.z.string(), zod_1.z.any())
|
package/package.json
CHANGED
package/src/tools/describe.ts
CHANGED
|
@@ -1089,7 +1089,7 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
1089
1089
|
"Always validate_page before publish_page — publishing will reject if there are errors",
|
|
1090
1090
|
"Set access policy with set_page_access_policy before publishing if you need auth",
|
|
1091
1091
|
"Use variable bindings on data sources to scope data dynamically — bind to URL params, auth context, record context, or static defaults",
|
|
1092
|
-
"For list→detail navigation: set useQueryParams:true on navigate-to-page actions, then bind the detail page's data source variables to { source: 'url', param: 'id' }",
|
|
1092
|
+
"For list→detail navigation: set useQueryParams:true on navigate-to-page actions, then bind the detail page's data source variables to { source: 'url', param: 'id' }. Use config.paramMapping to rename fields if source and target use different names (e.g., { requestId: 'id' })",
|
|
1093
1093
|
"For user-scoped views (e.g., My Approvals): bind a variable to { source: 'auth', field: 'userId' } and use a smart query with {{assigneeId}}",
|
|
1094
1094
|
],
|
|
1095
1095
|
multi_page_pattern: {
|
|
@@ -1214,8 +1214,10 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
1214
1214
|
},
|
|
1215
1215
|
behavior: {
|
|
1216
1216
|
query_type: "For type:'query' — resolved variables substitute into smart query {{placeholders}} before execution",
|
|
1217
|
-
structure_type: "For type:'structure' — resolved variables become equality filters on the collection",
|
|
1217
|
+
structure_type: "For type:'structure' — resolved variables become equality filters on the collection. IMPORTANT: the variable name 'id' is special — it matches the system record UUID (used for single-record fetch by ID). ALL OTHER variable names filter against user data fields and are automatically prefixed with 'data.' (e.g., variable 'requestId' filters on data.requestId in the JSONB column). System columns like createdAt, updatedAt, status do NOT get the data. prefix.",
|
|
1218
1218
|
unresolved: "If any variable cannot be resolved, the block returns an empty result with a variableError message — NEVER unfiltered data",
|
|
1219
|
+
detail_page_pattern: "For a detail page: use variables: { id: { source: 'url', param: 'id' } } on the primary record-card block to fetch by system ID. For related-list blocks, use the actual data field name (e.g., variables: { requestId: { source: 'url', param: 'id' } }) — this filters related records where data.requestId matches the URL param. The 'id' variable fetches a record BY its UUID; other variable names filter records WHERE a field equals the value.",
|
|
1220
|
+
reference_fields: "Reference fields (foreign keys between collections) store the target record's system UUID. To filter a related list by a reference field, bind the variable to the parent record's system ID (from URL param), not a custom field value. Example: approval_steps has a data.requestId field that stores the governance_request's UUID.",
|
|
1219
1221
|
},
|
|
1220
1222
|
precedence: "url > record > auth > static (highest to lowest priority)",
|
|
1221
1223
|
},
|
|
@@ -1546,14 +1548,73 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
1546
1548
|
requires: "content property (string) — the HTML markup",
|
|
1547
1549
|
category: "content-block",
|
|
1548
1550
|
},
|
|
1551
|
+
"data-html": {
|
|
1552
|
+
description:
|
|
1553
|
+
"Data-bound HTML block. Write HTML templates with {{fieldName}} placeholders that resolve against a data source. Supports two modes: LIST mode (default) renders the template ONCE PER RECORD — perfect for card grids, custom list layouts, or any repeating template. SINGLE mode renders the template once for a single record (detail pages). Records are auto-flattened so {{firstName}} works directly — no need for {{data.firstName}}. Output is sanitized via DOMPurify. No JavaScript allowed.",
|
|
1554
|
+
dataSource: "Required — type: 'structure' or 'query'. Data is resolved server-side and injected into the template.",
|
|
1555
|
+
requires: "content property (string) — HTML template with {{field}} placeholders. Must have non-empty content to pass publishing validation.",
|
|
1556
|
+
template_syntax: "{{fieldName}} for top-level fields, {{data.status}} for nested. Unresolved placeholders render as empty string.",
|
|
1557
|
+
modes: {
|
|
1558
|
+
list: "Default. Template repeats for EACH record. Write a single card/row template and it renders N times for N records. Example: a styled customer card template produces a grid of cards.",
|
|
1559
|
+
single: "Template renders once for ONE record. Use with mode:'single' and a variable binding like { id: { source: 'url', param: 'id' } } to fetch a specific record.",
|
|
1560
|
+
},
|
|
1561
|
+
example_list_mode: {
|
|
1562
|
+
blockType: "data-html",
|
|
1563
|
+
dataSource: { type: "structure", ref: "<collection-uuid>", recordSlug: "customers", config: { sort: [{ field: "createdAt", direction: "desc" }], limit: 20 } },
|
|
1564
|
+
content: "<div style='display:inline-block; width:280px; margin:8px; padding:16px; border:1px solid #e5e7eb; border-radius:8px; vertical-align:top;'><h3>{{firstName}} {{lastName}}</h3><p style='color:#6b7280;'>{{email}}</p></div>",
|
|
1565
|
+
},
|
|
1566
|
+
example_single_mode: {
|
|
1567
|
+
blockType: "data-html",
|
|
1568
|
+
dataSource: { type: "structure", ref: "<collection-uuid>", mode: "single", config: {}, variables: { id: { source: "url", param: "id" } } },
|
|
1569
|
+
content: "<div style='padding:16px; border:1px solid #e5e7eb; border-radius:8px;'><h2>{{firstName}} {{lastName}}</h2><p>{{email}}</p><p>Company: {{company}}</p></div>",
|
|
1570
|
+
},
|
|
1571
|
+
IMPORTANT: "No iteration syntax needed for lists. Just write a single template — the runtime repeats it automatically for each record. Records are auto-flattened: use {{fieldName}} directly, not {{data.fieldName}}.",
|
|
1572
|
+
category: "data-block",
|
|
1573
|
+
},
|
|
1574
|
+
"image": {
|
|
1575
|
+
description:
|
|
1576
|
+
"Image block. Displays an image from a URL with responsive sizing, lazy loading, optional caption, alignment, and link-on-click.",
|
|
1577
|
+
dataSource: "Not required",
|
|
1578
|
+
requires: "src property (string) — the image URL. Optional: alt, caption, alignment (left|center|right), maxWidth, linkUrl.",
|
|
1579
|
+
category: "media-block",
|
|
1580
|
+
},
|
|
1581
|
+
"video": {
|
|
1582
|
+
description:
|
|
1583
|
+
"Video block. Embeds YouTube/Vimeo videos via iframe or direct MP4/WebM via native <video>. Auto-detects provider from URL.",
|
|
1584
|
+
dataSource: "Not required",
|
|
1585
|
+
requires: "src property (string) — video URL (YouTube, Vimeo, or direct). Optional: autoplay (boolean, muted only), loop, controls (default true), poster, caption.",
|
|
1586
|
+
category: "media-block",
|
|
1587
|
+
},
|
|
1588
|
+
"modal": {
|
|
1589
|
+
description:
|
|
1590
|
+
"Declarative modal block. Invisible by default — renders as an overlay when triggered by an 'open-modal' action. Content can be static HTML, markdown, or data-bound HTML. Supports actions (buttons) inside the modal for confirm/cancel flows. Uses Radix Dialog.",
|
|
1591
|
+
dataSource: "Optional — only needed if contentType is 'data-html'",
|
|
1592
|
+
requires: "content property (string), contentType ('html' | 'markdown' | 'data-html', default 'html'). Optional: title, modalSize ('sm' | 'md' | 'lg' | 'fullscreen', default 'md').",
|
|
1593
|
+
actions: "Optional actions array — renders as buttons at the bottom of the modal. Supports close-modal (close the modal), invoke-trigger, navigate-to-page, start-orchestration, open-modal (open another modal). Use close-modal with targetRef set to this modal's own ID for a 'Cancel' button.",
|
|
1594
|
+
trigger: "Add an 'open-modal' action on another block with targetRef set to this modal block's ID. The modal opens when the action fires.",
|
|
1595
|
+
category: "interactive-block",
|
|
1596
|
+
example_confirm_modal: {
|
|
1597
|
+
blockType: "modal",
|
|
1598
|
+
title: "Confirm Delete",
|
|
1599
|
+
modalSize: "sm",
|
|
1600
|
+
contentType: "html",
|
|
1601
|
+
content: "<p>Are you sure you want to delete this item? This action cannot be undone.</p>",
|
|
1602
|
+
actions: [
|
|
1603
|
+
{ id: "cancel", type: "close-modal", label: "Cancel", targetRef: "THIS_MODAL_ID", presentation: { color: "neutral" } },
|
|
1604
|
+
{ id: "confirm", type: "invoke-trigger", label: "Delete", targetRef: "TRIGGER_ID", presentation: { color: "destructive" } },
|
|
1605
|
+
],
|
|
1606
|
+
},
|
|
1607
|
+
},
|
|
1549
1608
|
},
|
|
1550
1609
|
block_categories: {
|
|
1551
1610
|
"data-block":
|
|
1552
|
-
"Blocks that display data from collections (data-table, chart, metric-card, stat-group, field-display, related-list, activity-feed, kanban, calendar, progress-indicator)",
|
|
1611
|
+
"Blocks that display data from collections (data-table, chart, metric-card, stat-group, field-display, related-list, activity-feed, kanban, calendar, progress-indicator, data-html)",
|
|
1553
1612
|
"form-block":
|
|
1554
1613
|
"Use 'field-group' blockType with a 'fields' array for form pages. Do NOT use individual field block types (text-input, select, etc.) — the runtime renders field types from the field-group's fields array.",
|
|
1555
1614
|
"content-block":
|
|
1556
1615
|
"Static content blocks (markdown, static-html). Must have non-empty 'content' property.",
|
|
1616
|
+
"media-block":
|
|
1617
|
+
"Media blocks (image, video). Require a 'src' URL property.",
|
|
1557
1618
|
"layout-block":
|
|
1558
1619
|
"Blocks that control page-level behavior (filter-bar)",
|
|
1559
1620
|
},
|
|
@@ -1663,16 +1724,19 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
1663
1724
|
typical_activation: "row-click",
|
|
1664
1725
|
paramConfig:
|
|
1665
1726
|
"Use source: 'row', mode: 'selected', selectedFields: ['id'] to pass the record ID to the detail page",
|
|
1727
|
+
paramMapping:
|
|
1728
|
+
"Optional config.paramMapping: { sourceField: 'targetParam' } to rename fields in the URL. E.g., { requestId: 'id' } passes ?id=<value> instead of ?requestId=<value>. Only applies when useQueryParams is true.",
|
|
1666
1729
|
example: {
|
|
1667
1730
|
id: "view-detail",
|
|
1668
1731
|
type: "navigate-to-page",
|
|
1669
1732
|
label: "View Details",
|
|
1670
1733
|
targetRef: "customer-detail",
|
|
1671
1734
|
activation: "row-click",
|
|
1735
|
+
config: { useQueryParams: true, paramMapping: { requestId: "id" } },
|
|
1672
1736
|
paramConfig: {
|
|
1673
1737
|
source: "row",
|
|
1674
1738
|
mode: "selected",
|
|
1675
|
-
selectedFields: ["
|
|
1739
|
+
selectedFields: ["requestId"],
|
|
1676
1740
|
},
|
|
1677
1741
|
},
|
|
1678
1742
|
},
|
|
@@ -1719,6 +1783,18 @@ export function registerDescribeTools(server: McpServer) {
|
|
|
1719
1783
|
typical_activation: "button",
|
|
1720
1784
|
executionMode: "'fire-and-forget'",
|
|
1721
1785
|
},
|
|
1786
|
+
"open-modal": {
|
|
1787
|
+
description:
|
|
1788
|
+
"Open a modal block. Client-side only — no backend call. The modal block must be defined on the same page with blockType 'modal'.",
|
|
1789
|
+
targetRef: "The modal block's ID (UUID) — must match a block with blockType 'modal' on this page.",
|
|
1790
|
+
typical_activation: "button",
|
|
1791
|
+
},
|
|
1792
|
+
"close-modal": {
|
|
1793
|
+
description:
|
|
1794
|
+
"Close an open modal. Client-side only.",
|
|
1795
|
+
targetRef: "The modal block's ID (UUID) to close.",
|
|
1796
|
+
typical_activation: "button",
|
|
1797
|
+
},
|
|
1722
1798
|
},
|
|
1723
1799
|
tips: [
|
|
1724
1800
|
"Every action needs an 'id' — use UUIDs for uniqueness",
|
package/src/tools/pages.ts
CHANGED
|
@@ -160,7 +160,7 @@ export function registerPageTools(
|
|
|
160
160
|
"create_page",
|
|
161
161
|
`Create a new page in the workspace. A page is a UI view backed by data from structures. Specify the page type: 'list' for data tables, 'detail' for single-record views, 'form' for data entry, 'dashboard' for metrics and charts.
|
|
162
162
|
|
|
163
|
-
Navigate-to-page actions can use config.useQueryParams: true to pass selected row fields as URL query params to the target page. Pair with paramConfig: { source: 'row', mode: 'selected', selectedFields: ['id'] } to control which fields are passed. The target detail page can then use variable bindings with { source: 'url', param: 'id' } to read those params.`,
|
|
163
|
+
Navigate-to-page actions can use config.useQueryParams: true to pass selected row fields as URL query params to the target page. Pair with paramConfig: { source: 'row', mode: 'selected', selectedFields: ['id'] } to control which fields are passed. Use config.paramMapping: { sourceField: 'targetParam' } to rename fields in the URL (e.g., { requestId: 'id' } passes ?id=<value> instead of ?requestId=<value>). The target detail page can then use variable bindings with { source: 'url', param: 'id' } to read those params.`,
|
|
164
164
|
{
|
|
165
165
|
name: z.string().describe("Display name for the page (e.g., 'Customer List')"),
|
|
166
166
|
slug: z.string().describe("URL-safe slug (e.g., 'customer-list')"),
|
|
@@ -252,12 +252,14 @@ Variable binding sources:
|
|
|
252
252
|
- { source: 'static', value: 'active' } — literal default value
|
|
253
253
|
|
|
254
254
|
For query data sources: variables substitute into smart query {{placeholders}}.
|
|
255
|
-
For structure data sources: variables become equality filters.
|
|
255
|
+
For structure data sources: variables become equality filters. IMPORTANT: variable name 'id' is special — it fetches the record BY its system UUID. All other variable names filter against data.<fieldName> in the JSONB column (e.g., variable 'requestId' → filters on data.requestId). Reference fields between collections store system UUIDs.
|
|
256
256
|
|
|
257
257
|
Common patterns:
|
|
258
|
-
- Detail page
|
|
258
|
+
- Detail page primary record: { id: { source: 'url', param: 'id' } } — fetches single record by system ID
|
|
259
|
+
- Detail page related list: { requestId: { source: 'url', param: 'id' } } — filters where data.requestId = URL param
|
|
259
260
|
- User-scoped view: { assigneeId: { source: 'auth', field: 'userId' } }
|
|
260
|
-
- Navigate with params: action config { useQueryParams: true } + paramConfig { source: 'row', mode: 'selected', selectedFields: ['id'] }
|
|
261
|
+
- Navigate with params: action config { useQueryParams: true } + paramConfig { source: 'row', mode: 'selected', selectedFields: ['id'] }
|
|
262
|
+
- Rename params: action config { useQueryParams: true, paramMapping: { requestId: 'id' } } — passes ?id=<value> instead of ?requestId=<value>`,
|
|
261
263
|
{
|
|
262
264
|
pageId: z.string().describe("The page ID (UUID)"),
|
|
263
265
|
definition: z
|