workspace-builder
SKILL.md
App Engine Studio & Workspace Builder for ServiceNow
App Engine Studio (AES) enables low-code application development with custom workspaces.
AES Architecture
Application (sys_scope)
├── Tables & Forms
├── Workflows
├── Workspaces (sys_aw_workspace)
│ ├── Lists
│ ├── Forms
│ └── Dashboards
└── Portals
Key Tables
| Table | Purpose |
|---|---|
sys_scope |
Application scope |
sys_app |
Application record |
sys_aw_workspace |
Workspace definition |
sys_ux_page |
UI Builder pages |
sys_ux_macroponent |
Custom components |
Application Development (ES5)
Create Scoped Application
// Create scoped application (ES5 ONLY!)
var app = new GlideRecord("sys_scope")
app.initialize()
// Basic info
app.setValue("name", "IT Asset Tracker")
app.setValue("scope", "x_myco_asset_track")
app.setValue("short_description", "Track IT assets across the organization")
app.setValue("version", "1.0.0")
// Vendor
app.setValue("vendor", "My Company")
app.setValue("vendor_prefix", "x_myco")
// License
app.setValue("licensable", true)
app.insert()
Create Application Table
// Create table in scoped app (ES5 ONLY!)
function createAppTable(scope, tableDef) {
var table = new GlideRecord("sys_db_object")
table.initialize()
table.setValue("name", scope + "_" + tableDef.name)
table.setValue("label", tableDef.label)
table.setValue("super_class", tableDef.extends || "task")
// Scope assignment
table.setValue("sys_scope", getAppSysId(scope))
// Options
table.setValue("is_extendable", tableDef.extendable || false)
table.setValue("create_access_controls", true)
table.insert()
// Create fields
if (tableDef.fields) {
for (var i = 0; i < tableDef.fields.length; i++) {
createField(scope + "_" + tableDef.name, tableDef.fields[i])
}
}
return table.getUniqueValue()
}
// Example
createAppTable("x_myco_asset_track", {
name: "asset_item",
label: "Asset Item",
extends: "cmdb_ci",
fields: [
{ name: "u_purchase_date", label: "Purchase Date", type: "glide_date" },
{ name: "u_warranty_end", label: "Warranty End", type: "glide_date" },
{ name: "u_assigned_user", label: "Assigned User", type: "reference", reference: "sys_user" },
],
})
Workspace Configuration (ES5)
Create Custom Workspace
// Create workspace (ES5 ONLY!)
var workspace = new GlideRecord("sys_aw_workspace")
workspace.initialize()
workspace.setValue("name", "asset_tracker_workspace")
workspace.setValue("title", "Asset Tracker")
workspace.setValue("description", "Workspace for IT asset management")
// Primary table
workspace.setValue("primary_table", "x_myco_asset_track_asset_item")
// URL
workspace.setValue("url", "asset-tracker")
// Branding
workspace.setValue("icon", "laptop")
workspace.setValue("color", "#2E7D32")
// App scope
workspace.setValue("sys_scope", appScopeSysId)
// Features
workspace.setValue("agent_assist_enabled", false)
workspace.setValue("contextual_side_panel_enabled", true)
workspace.insert()
Configure Workspace Lists
// Create workspace list (ES5 ONLY!)
function createWorkspaceList(workspaceSysId, listDef) {
var list = new GlideRecord("sys_aw_list")
list.initialize()
list.setValue("workspace", workspaceSysId)
list.setValue("name", listDef.name)
list.setValue("table", listDef.table)
// Filter
list.setValue("filter", listDef.filter || "")
// Columns
list.setValue("columns", listDef.columns.join(","))
// Sorting
if (listDef.orderBy) {
list.setValue("order_by", listDef.orderBy)
list.setValue("order_by_desc", listDef.orderDesc || false)
}
// Grouping
if (listDef.groupBy) {
list.setValue("group_by", listDef.groupBy)
}
list.insert()
return list.getUniqueValue()
}
// Example lists
createWorkspaceList(workspaceSysId, {
name: "My Assets",
table: "x_myco_asset_track_asset_item",
filter: "u_assigned_user=javascript:gs.getUserID()",
columns: ["number", "name", "u_purchase_date", "u_warranty_end", "state"],
})
createWorkspaceList(workspaceSysId, {
name: "Expiring Warranties",
table: "x_myco_asset_track_asset_item",
filter: "u_warranty_endBETWEENjavascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)",
columns: ["number", "name", "u_assigned_user", "u_warranty_end"],
orderBy: "u_warranty_end",
})
UI Builder Pages (ES5)
Page Configuration
// Create UI Builder page (ES5 ONLY!)
// Note: Full page creation typically done via UI Builder
var page = new GlideRecord("sys_ux_page")
page.initialize()
page.setValue("name", "asset_dashboard")
page.setValue("title", "Asset Dashboard")
page.setValue("description", "Dashboard for asset overview")
// Page type
page.setValue("page_type", "workspace")
// Workspace link
page.setValue("workspace", workspaceSysId)
// Scope
page.setValue("sys_scope", appScopeSysId)
page.insert()
Custom Component (Macroponent)
// Create custom macroponent definition (ES5 ONLY!)
// Note: Actual components created via UI Builder
var component = new GlideRecord("sys_ux_macroponent")
component.initialize()
component.setValue("name", "asset_summary_card")
component.setValue("label", "Asset Summary Card")
component.setValue("description", "Displays asset summary information")
// Component category
component.setValue("category", "data_visualization")
// Scope
component.setValue("sys_scope", appScopeSysId)
// Properties (inputs)
component.setValue(
"properties",
JSON.stringify([
{ name: "title", type: "string", label: "Card Title" },
{ name: "assetTable", type: "string", label: "Asset Table" },
{ name: "filter", type: "string", label: "Filter" },
]),
)
component.insert()
Data Brokers (ES5)
Create Data Broker
// Data broker for workspace data (ES5 ONLY!)
// Data brokers provide data to UI Builder pages
var broker = new GlideRecord("sys_ux_data_broker")
broker.initialize()
broker.setValue("name", "asset_stats")
broker.setValue("label", "Asset Statistics")
// Data source type
broker.setValue("type", "script")
// Script to fetch data (ES5 ONLY!)
broker.setValue(
"script",
"(function getData(inputs) {\n" +
" var result = {\n" +
" total: 0,\n" +
" assigned: 0,\n" +
" available: 0,\n" +
" expiring_warranty: 0\n" +
" };\n" +
" \n" +
' var ga = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
' ga.addAggregate("COUNT");\n' +
' ga.groupBy("state");\n' +
" ga.query();\n" +
" \n" +
" while (ga.next()) {\n" +
' var count = parseInt(ga.getAggregate("COUNT"), 10);\n' +
" result.total += count;\n" +
" \n" +
' var state = ga.getValue("state");\n' +
' if (state === "in_use") {\n' +
" result.assigned = count;\n" +
' } else if (state === "available") {\n' +
" result.available = count;\n" +
" }\n" +
" }\n" +
" \n" +
" // Expiring warranties\n" +
' var expiring = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
' expiring.addQuery("u_warranty_end", "BETWEEN", "javascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)");\n' +
' expiring.addAggregate("COUNT");\n' +
" expiring.query();\n" +
" \n" +
" if (expiring.next()) {\n" +
' result.expiring_warranty = parseInt(expiring.getAggregate("COUNT"), 10);\n' +
" }\n" +
" \n" +
" return result;\n" +
"})(inputs);",
)
broker.setValue("sys_scope", appScopeSysId)
broker.insert()
Application Deployment (ES5)
Create Update Set
// Create update set for app deployment (ES5 ONLY!)
function createAppUpdateSet(appName, description) {
var updateSet = new GlideRecord("sys_update_set")
updateSet.initialize()
updateSet.setValue("name", appName + " - " + new GlideDateTime().getDate())
updateSet.setValue("description", description)
updateSet.setValue("application", getAppSysId(appName))
updateSet.setValue("state", "in progress")
return updateSet.insert()
}
Export Application
// Prepare app for export (ES5 ONLY!)
function prepareAppExport(appScope) {
// Validate all components
var issues = []
// Check for missing dependencies
var dependency = new GlideRecord("sys_app_dependency")
dependency.addQuery("app.scope", appScope)
dependency.query()
while (dependency.next()) {
if (!isDependencyInstalled(dependency.getValue("dependency"))) {
issues.push("Missing dependency: " + dependency.dependency.getDisplayValue())
}
}
// Validate update sets
var updateSet = new GlideRecord("sys_update_set")
updateSet.addQuery("application.scope", appScope)
updateSet.addQuery("state", "in progress")
updateSet.query()
while (updateSet.next()) {
issues.push("Open update set: " + updateSet.getValue("name"))
}
return {
ready: issues.length === 0,
issues: issues,
}
}
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|---|
snow_query_table |
Query app components |
snow_execute_script_with_output |
Test app scripts |
snow_find_artifact |
Find configurations |
snow_update_set_create |
Create update sets |
Example Workflow
// 1. Query applications
await snow_query_table({
table: "sys_scope",
query: "scopeSTARTSWITHx_",
fields: "name,scope,version,vendor",
})
// 2. Find app tables
await snow_query_table({
table: "sys_db_object",
query: "nameSTARTSWITHx_myco",
fields: "name,label,super_class",
})
// 3. Get workspace configs
await snow_query_table({
table: "sys_aw_workspace",
query: "sys_scope.scopeSTARTSWITHx_",
fields: "name,title,primary_table,url",
})
Best Practices
- Naming Conventions - Consistent prefixes
- Scoped Apps - Use scope isolation
- Reusable Components - Modular design
- Data Brokers - Efficient data fetching
- Workspace Design - User-focused layouts
- Testing - ATF tests for apps
- Documentation - App documentation
- ES5 Only - No modern JavaScript syntax
Weekly Installs
51
Repository
groeimetai/snow-flowGitHub Stars
53
First Seen
Jan 22, 2026
Security Audits
Installed on
claude-code47
opencode46
github-copilot46
codex46
gemini-cli46
cursor45