snow-flow 4.5.45 → 4.5.47

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/CLAUDE.md CHANGED
@@ -684,23 +684,47 @@ Snow-Flow now automatically sets its Update Set as the user's current Update Set
684
684
  - `snow_get_flow_details` - Get detailed flow configuration
685
685
  - `snow_import_flow_from_xml` - Import flows from XML (only programmatic creation method)
686
686
 
687
- **Configurable Agent Workspace Tools (UX App Architecture):**
688
- - `snow_create_workspace` - Create Configurable Agent Workspaces using UX App architecture (sys_ux_app_route, sys_ux_screen_type, sys_ux_screen, sys_ux_macroponent)
689
- - `snow_discover_workspaces` - Find all Configurable Workspaces
687
+ **šŸ—ļø COMPLETE UX WORKSPACE CREATION (6-Step Workflow):**
688
+
689
+ **CRITICAL:** Use the correct 6-step workflow for creating functional workspaces. This is the ONLY way to create workspaces that actually work!
690
+
691
+ **Step 1: Experience Record (sys_ux_experience)**
692
+ - `snow_create_ux_experience` - Create the top-level workspace container
693
+ - Required fields: `name`, auto-detects `root_macroponent` (x_snc_app_shell_uib_app_shell)
694
+
695
+ **Step 2: App Configuration (sys_ux_app_config)**
696
+ - `snow_create_ux_app_config` - Create workspace settings linked to experience
697
+ - Required fields: `name`, `experience_sys_id` (from Step 1)
698
+
699
+ **Step 3: Page Macroponent (sys_ux_macroponent)**
700
+ - `snow_create_ux_page_macroponent` - Define actual page content
701
+ - Required fields: `name`, defaults to `sn-canvas-panel` root component
702
+ - Optional: `composition` JSON for custom layouts
703
+
704
+ **Step 4: Page Registry (sys_ux_page_registry)**
705
+ - `snow_create_ux_page_registry` - Register page within workspace
706
+ - Required fields: `sys_name` (technical name), `app_config_sys_id`, `macroponent_sys_id`
707
+
708
+ **Step 5: Route Record (sys_ux_app_route)**
709
+ - `snow_create_ux_app_route` - Create URL routing to page
710
+ - Required fields: `name` (URL slug), `app_config_sys_id`, `page_sys_name`
711
+
712
+ **Step 6: Landing Page Update**
713
+ - `snow_update_ux_app_config_landing_page` - Set default landing page
714
+ - Required fields: `app_config_sys_id`, `route_name`
715
+
716
+ **COMPLETE WORKFLOW TOOL:**
717
+ - `snow_create_complete_workspace` - **RECOMMENDED:** Executes all 6 steps automatically
718
+ - Creates: Experience → App Config → Page Macroponent → Page Registry → Route → Landing Page
719
+ - Only requires: `workspace_name`, optional: `description`, `home_page_name`, `route_name`
690
720
 
691
- **šŸ—ļø CONFIGURABLE WORKSPACE ARCHITECTURE:**
692
- Based on ServiceNow's official Configurable Agent Workspace structure:
693
- - **App Route** → `sys_ux_app_route` (top-level workspace routing)
694
- - **Screen Collection** → `sys_ux_screen_type` (groups of related screens)
695
- - **Individual Screens** → `sys_ux_screen` (specific workspace screens)
696
- - **Screen Components** → `sys_ux_macroponent` (components within screens)
697
721
 
698
722
  **Mobile App Tools:**
699
723
  - `snow_configure_mobile_app` - Configure mobile applications
700
724
  - `snow_send_push_notification` - Send push notifications
701
725
  - `snow_configure_offline_sync` - Configure offline sync
702
726
 
703
- **šŸ†• COMPLETE UI BUILDER INTEGRATION (15 NEW TOOLS!):**
727
+ **šŸ†• COMPLETE UI BUILDER INTEGRATION (22 TOOLS!):**
704
728
 
705
729
  **UI Builder Page Management (sys_ux_page):**
706
730
  - `snow_create_uib_page` - Create UI Builder pages with automatic routing
package/README.md CHANGED
@@ -5,10 +5,15 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
6
6
  [![Node.js](https://img.shields.io/node/v/snow-flow.svg?style=for-the-badge&logo=node.js&color=339933)](https://nodejs.org)
7
7
 
8
- **Talk to your ServiceNow instance through Claude Code • 20+ MCP Servers • 200+ ServiceNow Tools • Multi-Agent Orchestration**
8
+ **Talk to your ServiceNow instance through Claude Code • 20+ MCP Servers • 240+ ServiceNow Tools • Complete UX Workspace Creation • Multi-Agent Orchestration**
9
9
 
10
10
  ---
11
11
 
12
+ TO FIX TEMP CLAUDE CODE INK ERROR, RUN BEFORE SWARM COMMAND:
13
+ ```bash
14
+ npm install -g @anthropic-ai/claude-code@1.0.95 --save-exact
15
+ ```
16
+
12
17
  ## What is Snow-Flow?
13
18
 
14
19
  Snow-Flow is a conversational ServiceNow development platform that bridges Claude Code with ServiceNow through specialized MCP (Model Context Protocol) servers. Instead of navigating ServiceNow's web interface, you develop through natural conversation with Claude Code, which orchestrates multi-agent workflows to handle complex ServiceNow operations.
@@ -310,4 +315,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
310
315
 
311
316
  ---
312
317
 
313
- **Snow-Flow: Conversational ServiceNow development through Claude Code.**
318
+ **Snow-Flow: Conversational ServiceNow development through Claude Code.**
package/dist/cli.js CHANGED
@@ -416,7 +416,7 @@ program
416
416
  cliLogger.info('\nšŸ”— Live ServiceNow integration: āŒ Disabled');
417
417
  }
418
418
  }
419
- console.log(chalk_1.default.blue('\nšŸš€ Starting Claude Code with 235+ ServiceNow tools...'));
419
+ console.log(chalk_1.default.blue('\nšŸš€ Starting Claude Code with 240+ ServiceNow tools including complete UX Workspace creation...'));
420
420
  // Try to execute Claude Code directly with the prompt
421
421
  const success = await executeClaudeCode(orchestrationPrompt);
422
422
  if (success) {
@@ -524,7 +524,7 @@ async function executeClaudeCode(prompt) {
524
524
  console.log(chalk_1.default.green(`āœ… All ${newStatus.running} ServiceNow MCP servers ready`));
525
525
  }
526
526
  else {
527
- console.log(chalk_1.default.green(`āœ… All ${systemStatus.running} ServiceNow MCP servers ready (235+ tools)`));
527
+ console.log(chalk_1.default.green(`āœ… All ${systemStatus.running} ServiceNow MCP servers ready (240+ tools with UX Workspace workflow)`));
528
528
  }
529
529
  }
530
530
  catch (error) {
@@ -704,6 +704,7 @@ The Queen Agent will automatically discover and validate table schemas based on
704
704
  **Table Detection Examples:**
705
705
  - "create widget for incident records" → Discovers: incident, sys_user, sys_user_group
706
706
  - "build approval flow for u_equipment_request" → Discovers: u_equipment_request, sys_user, sysapproval_approver
707
+ - "create UX workspace for IT support" → Discovers: sys_ux_experience, sys_ux_app_config, sys_ux_macroponent, sys_ux_page_registry, sys_ux_app_route
707
708
  - "portal showing catalog items" → Discovers: sc_cat_item, sc_category, sc_request
708
709
  - "dashboard with CMDB assets" → Discovers: cmdb_ci, cmdb_rel_ci, sys_user
709
710
  - "report on problem tickets" → Discovers: problem, incident, sys_user
@@ -716,6 +717,21 @@ The Queen Agent will automatically discover and validate table schemas based on
716
717
 
717
718
  ## šŸ‘‘ Your Queen Agent Responsibilities
718
719
 
720
+ ## šŸ—ļø UX Workspace Creation Specific Instructions
721
+ If the task involves UX WORKSPACE CREATION (e.g., "create workspace for IT support", "build agent workspace"):
722
+
723
+ 1. **ALWAYS** use \`snow_create_complete_workspace\` for full 6-step workflow
724
+ 2. **INDIVIDUAL STEPS** available if fine control needed:
725
+ - Step 1: \`snow_create_ux_experience\` (sys_ux_experience)
726
+ - Step 2: \`snow_create_ux_app_config\` (sys_ux_app_config)
727
+ - Step 3: \`snow_create_ux_page_macroponent\` (sys_ux_macroponent)
728
+ - Step 4: \`snow_create_ux_page_registry\` (sys_ux_page_registry)
729
+ - Step 5: \`snow_create_ux_app_route\` (sys_ux_app_route)
730
+ - Step 6: \`snow_update_ux_app_config_landing_page\` (landing page route)
731
+ 3. **VERIFY ORDER**: Must follow exact sequence for functional workspaces
732
+ 4. **STORE SYS_IDS**: Store all sys_ids in Memory for agents coordination
733
+ 5. **TEST ACCESS**: Provide workspace URL: \`/now/experience/workspace/{route_name}\`
734
+
719
735
  ## šŸ“Š Data Generation Specific Instructions
720
736
  If the task is identified as DATA GENERATION (e.g., "create 5000 incidents"):
721
737
 
@@ -1596,7 +1612,7 @@ program
1596
1612
  console.log(chalk_1.default.green.bold('\nāœ… Snow-Flow project initialized successfully!'));
1597
1613
  console.log('\nšŸ“‹ Created Snow-Flow configuration:');
1598
1614
  console.log(' āœ“ .claude/ - Claude Code MCP configuration');
1599
- console.log(' āœ“ .mcp.json - 20+ ServiceNow MCP servers (235+ tools)');
1615
+ console.log(' āœ“ .mcp.json - 20+ ServiceNow MCP servers (240+ tools including complete UX Workspace workflow)');
1600
1616
  console.log(' āœ“ CLAUDE.md - Complete development guide');
1601
1617
  console.log(' āœ“ README.md - Current capabilities documentation');
1602
1618
  console.log(' āœ“ .snow-flow/ - Project workspace and memory');
@@ -1627,7 +1643,7 @@ program
1627
1643
  console.log('2. Authenticate Snow-Flow: ' + chalk_1.default.cyan('snow-flow auth login'));
1628
1644
  console.log('3. Start developing: ' + chalk_1.default.cyan('snow-flow swarm "create incident dashboard"'));
1629
1645
  console.log('\nšŸ“š Complete documentation: ' + chalk_1.default.blue('https://snow-flow.dev'));
1630
- console.log('šŸ’” UI Builder, Agent Workspaces, and 235+ tools now available');
1646
+ console.log('šŸ’” Complete 6-step UX Workspace creation, UI Builder, and 240+ tools now available');
1631
1647
  // Force exit to prevent hanging
1632
1648
  process.exit(0);
1633
1649
  }
@@ -115,63 +115,109 @@ class ServiceNowFlowWorkspaceMobileMCP {
115
115
  }
116
116
  }
117
117
  },
118
- // Agent Workspace Tools
118
+ // šŸ—ļø COMPLETE UX WORKSPACE CREATION (6-Step Workflow)
119
+ // STEP 1: Experience Record (sys_ux_experience)
119
120
  {
120
- name: 'snow_create_workspace',
121
- description: 'Creates a Configurable Agent Workspace using modern UX App architecture (sys_ux_app_route, sys_ux_screen_type, sys_ux_screen, sys_ux_macroponent). Creates complete workspace with routing, screen collections, and components.',
121
+ name: 'snow_create_ux_experience',
122
+ description: 'STEP 1: Create UX Experience Record (sys_ux_experience) - The top-level container for the workspace. This is the first step in the 6-step workspace creation process.',
122
123
  inputSchema: {
123
124
  type: 'object',
124
125
  properties: {
125
- name: { type: 'string', description: 'Configurable Workspace name' },
126
- description: { type: 'string', description: 'Workspace description' },
127
- tables: { type: 'array', items: { type: 'string' }, description: 'Tables to create screens for (incident, task, etc.)' },
128
- application: { type: 'string', description: 'Application scope', default: 'global' },
129
- roles: { type: 'array', items: { type: 'string' }, description: 'Roles with workspace access' }
126
+ name: { type: 'string', description: 'Experience name (e.g., "My Workspace")' },
127
+ root_macroponent: { type: 'string', description: 'Root macroponent sys_id (usually x_snc_app_shell_uib_app_shell, auto-detected if not provided)' },
128
+ description: { type: 'string', description: 'Experience description' }
130
129
  },
131
- required: ['name', 'tables']
130
+ required: ['name']
132
131
  }
133
132
  },
133
+ // STEP 2: App Configuration Record (sys_ux_app_config)
134
134
  {
135
- name: 'snow_create_workspace_list',
136
- description: 'Creates a custom list configuration for Agent Workspace.',
135
+ name: 'snow_create_ux_app_config',
136
+ description: 'STEP 2: Create UX App Configuration Record (sys_ux_app_config) - Contains workspace settings and links to the experience from Step 1.',
137
137
  inputSchema: {
138
138
  type: 'object',
139
139
  properties: {
140
- workspace: { type: 'string', description: 'Parent workspace' },
141
- name: { type: 'string', description: 'List name' },
142
- table: { type: 'string', description: 'Table for the list' },
143
- filter: { type: 'string', description: 'List filter condition' },
144
- fields: { type: 'array', items: { type: 'string' }, description: 'Fields to display' },
145
- order_by: { type: 'string', description: 'Sort order' },
146
- max_entries: { type: 'number', description: 'Maximum entries', default: 50 }
140
+ name: { type: 'string', description: 'App config name (e.g., "My Workspace Config")' },
141
+ experience_sys_id: { type: 'string', description: 'Experience sys_id from Step 1' },
142
+ description: { type: 'string', description: 'App configuration description' }
147
143
  },
148
- required: ['workspace', 'name', 'table']
144
+ required: ['name', 'experience_sys_id']
149
145
  }
150
146
  },
147
+ // STEP 3: Page Macroponent Record (sys_ux_macroponent)
151
148
  {
152
- name: 'snow_configure_workspace_notifications',
153
- description: 'Configures notification preferences for Agent Workspace.',
149
+ name: 'snow_create_ux_page_macroponent',
150
+ description: 'STEP 3: Create Page Macroponent Record (sys_ux_macroponent) - Defines the actual page content that will be displayed.',
154
151
  inputSchema: {
155
152
  type: 'object',
156
153
  properties: {
157
- workspace: { type: 'string', description: 'Workspace sys_id' },
158
- enable_desktop: { type: 'boolean', description: 'Enable desktop notifications' },
159
- enable_sound: { type: 'boolean', description: 'Enable sound alerts' },
160
- notification_types: { type: 'array', items: { type: 'string' }, description: 'Types to notify about' },
161
- priority_threshold: { type: 'number', description: 'Minimum priority for notifications' }
154
+ name: { type: 'string', description: 'Page macroponent name (e.g., "My Home Page")' },
155
+ root_component: { type: 'string', default: 'sn-canvas-panel', description: 'Root component to render (default: sn-canvas-panel for simple pages)' },
156
+ composition: { type: 'object', description: 'JSON layout configuration (default: simple single column)' },
157
+ description: { type: 'string', description: 'Page macroponent description' }
162
158
  },
163
- required: ['workspace']
159
+ required: ['name']
164
160
  }
165
161
  },
162
+ // STEP 4: Page Registry Record (sys_ux_page_registry)
166
163
  {
167
- name: 'snow_discover_workspaces',
168
- description: 'Discovers available Agent Workspaces and their configurations.',
164
+ name: 'snow_create_ux_page_registry',
165
+ description: 'STEP 4: Create Page Registry Record (sys_ux_page_registry) - Registers the page for use within the workspace configuration.',
169
166
  inputSchema: {
170
167
  type: 'object',
171
168
  properties: {
172
- include_tabs: { type: 'boolean', description: 'Include tab configurations', default: false },
173
- include_lists: { type: 'boolean', description: 'Include list configurations', default: false }
174
- }
169
+ sys_name: { type: 'string', description: 'Unique technical name for the page (e.g., "my_home_page")' },
170
+ app_config_sys_id: { type: 'string', description: 'App config sys_id from Step 2' },
171
+ macroponent_sys_id: { type: 'string', description: 'Macroponent sys_id from Step 3' },
172
+ description: { type: 'string', description: 'Page registry description' }
173
+ },
174
+ required: ['sys_name', 'app_config_sys_id', 'macroponent_sys_id']
175
+ }
176
+ },
177
+ // STEP 5: Route Record (sys_ux_app_route)
178
+ {
179
+ name: 'snow_create_ux_app_route',
180
+ description: 'STEP 5: Create Route Record (sys_ux_app_route) - Defines the URL slug that leads to the page.',
181
+ inputSchema: {
182
+ type: 'object',
183
+ properties: {
184
+ name: { type: 'string', description: 'Route name - becomes URL slug (e.g., "home")' },
185
+ app_config_sys_id: { type: 'string', description: 'App config sys_id from Step 2' },
186
+ page_sys_name: { type: 'string', description: 'Page sys_name from Step 4 registry' },
187
+ description: { type: 'string', description: 'Route description' }
188
+ },
189
+ required: ['name', 'app_config_sys_id', 'page_sys_name']
190
+ }
191
+ },
192
+ // STEP 6: Update App Config with Landing Page
193
+ {
194
+ name: 'snow_update_ux_app_config_landing_page',
195
+ description: 'STEP 6: Update App Configuration with Landing Page Route - Sets the default landing page for the workspace.',
196
+ inputSchema: {
197
+ type: 'object',
198
+ properties: {
199
+ app_config_sys_id: { type: 'string', description: 'App config sys_id from Step 2' },
200
+ route_name: { type: 'string', description: 'Route name from Step 5' }
201
+ },
202
+ required: ['app_config_sys_id', 'route_name']
203
+ }
204
+ },
205
+ // COMPLETE WORKFLOW: All 6 steps in one tool
206
+ {
207
+ name: 'snow_create_complete_workspace',
208
+ description: 'Create Complete UX Workspace - Executes all 6 steps automatically: Experience → App Config → Page Macroponent → Page Registry → Route → Landing Page Route. Creates a fully functional workspace.',
209
+ inputSchema: {
210
+ type: 'object',
211
+ properties: {
212
+ workspace_name: { type: 'string', description: 'Workspace name (used for all components)' },
213
+ description: { type: 'string', description: 'Workspace description' },
214
+ home_page_name: { type: 'string', default: 'home', description: 'Home page name (default: "home")' },
215
+ route_name: { type: 'string', default: 'home', description: 'URL route name (default: "home")' },
216
+ root_component: { type: 'string', default: 'sn-canvas-panel', description: 'Root component for page' },
217
+ composition: { type: 'object', description: 'Custom page layout (optional)' },
218
+ root_macroponent: { type: 'string', description: 'Custom root macroponent (auto-detected if not provided)' }
219
+ },
220
+ required: ['workspace_name']
175
221
  }
176
222
  },
177
223
  // Mobile Tools
@@ -702,12 +748,27 @@ class ServiceNowFlowWorkspaceMobileMCP {
702
748
  case 'snow_import_flow_from_xml':
703
749
  result = await this.importFlowFromXml(args);
704
750
  break;
705
- // Agent Workspace
706
- case 'snow_create_workspace':
707
- result = await this.createWorkspace(args);
751
+ // šŸ—ļø UX WORKSPACE CREATION (6-Step Workflow)
752
+ case 'snow_create_ux_experience':
753
+ result = await this.snow_create_ux_experience(args);
708
754
  break;
709
- case 'snow_discover_workspaces':
710
- result = await this.discoverWorkspaces(args);
755
+ case 'snow_create_ux_app_config':
756
+ result = await this.snow_create_ux_app_config(args);
757
+ break;
758
+ case 'snow_create_ux_page_macroponent':
759
+ result = await this.snow_create_ux_page_macroponent(args);
760
+ break;
761
+ case 'snow_create_ux_page_registry':
762
+ result = await this.snow_create_ux_page_registry(args);
763
+ break;
764
+ case 'snow_create_ux_app_route':
765
+ result = await this.snow_create_ux_app_route(args);
766
+ break;
767
+ case 'snow_update_ux_app_config_landing_page':
768
+ result = await this.snow_update_ux_app_config_landing_page(args);
769
+ break;
770
+ case 'snow_create_complete_workspace':
771
+ result = await this.snow_create_complete_workspace(args);
711
772
  break;
712
773
  // Mobile
713
774
  case 'snow_configure_mobile_app':
@@ -1221,101 +1282,6 @@ ${executionList}
1221
1282
  throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to import flow from XML: ${error}`);
1222
1283
  }
1223
1284
  }
1224
- // Agent Workspace Implementation
1225
- // Configurable Agent Workspace Implementation (UX App Architecture ONLY)
1226
- async createWorkspace(args) {
1227
- try {
1228
- this.logger.info("Creating Configurable Agent Workspace (UX App Architecture)...");
1229
- // Generate clean route for UX App
1230
- const cleanRoute = args.name
1231
- .toLowerCase()
1232
- .replace(/[^a-z0-9]/g, "-")
1233
- .replace(/--+/g, "-")
1234
- .replace(/^-|-$/g, "")
1235
- .substring(0, 50);
1236
- // Step 1: Create UX App Route (MANDATORY)
1237
- const appRouteData = {
1238
- route: `/${cleanRoute}`,
1239
- name: args.name,
1240
- title: args.name,
1241
- description: args.description || `Configurable Agent Workspace: ${args.name}`,
1242
- application: "global",
1243
- route_type: "workspace",
1244
- active: true
1245
- };
1246
- this.logger.trackAPICall("CREATE", "sys_ux_app_route", 1);
1247
- const appRouteResponse = await this.client.createRecord("sys_ux_app_route", appRouteData);
1248
- if (!appRouteResponse.success) {
1249
- return {
1250
- success: false,
1251
- error: `UX App Route creation failed: ${appRouteResponse.error}`,
1252
- suggestion: "Check UX App permissions and Now Experience Framework licensing."
1253
- };
1254
- }
1255
- return {
1256
- success: true,
1257
- workspace_type: "Configurable Agent Workspace (UX App)",
1258
- app_route: appRouteResponse.data,
1259
- sys_id: appRouteResponse.data.sys_id,
1260
- route: `/${cleanRoute}`,
1261
- message: `Configurable Agent Workspace created successfully`
1262
- };
1263
- }
1264
- catch (error) {
1265
- this.logger.error("Configurable Workspace creation failed:", error);
1266
- throw error;
1267
- }
1268
- }
1269
- async discoverWorkspaces(args) {
1270
- try {
1271
- this.logger.info('Discovering Agent Workspaces...');
1272
- // Validate table access first
1273
- const response = await this.client.searchRecords('sys_ux_app_route', '', 50);
1274
- if (!response.success) {
1275
- return {
1276
- success: false,
1277
- error: 'Cannot access Agent Workspace tables. Agent Workspace plugin may not be installed or activated.',
1278
- suggestion: 'Install Agent Workspace plugin in ServiceNow: System Applications → All Available Applications → Agent Workspace'
1279
- };
1280
- }
1281
- const workspaces = response.data.result;
1282
- if (!workspaces.length) {
1283
- return {
1284
- content: [{
1285
- type: 'text',
1286
- text: 'āŒ No Agent Workspaces found'
1287
- }]
1288
- };
1289
- }
1290
- const workspaceList = await Promise.all(workspaces.map(async (workspace) => {
1291
- let details = `šŸ–„ļø **${workspace.name}**
1292
- šŸ†” ${workspace.sys_id}
1293
- šŸ“ ${workspace.description || 'No description'}`;
1294
- if (args.include_tabs) {
1295
- const tabsResponse = await this.client.searchRecords('sys_aw_tab', `workspace=${workspace.sys_id}`, 10);
1296
- if (tabsResponse.success && tabsResponse.data.result.length) {
1297
- const tabs = tabsResponse.data.result.map((t) => ` - ${t.label}`).join('\n');
1298
- details += `\nšŸ“‘ Tabs:\n${tabs}`;
1299
- }
1300
- }
1301
- return details;
1302
- }));
1303
- return {
1304
- content: [{
1305
- type: 'text',
1306
- text: `šŸ” Discovered Agent Workspaces:
1307
-
1308
- ${workspaceList.join('\n\n')}
1309
-
1310
- ✨ Found ${workspaces.length} workspace(s)`
1311
- }]
1312
- };
1313
- }
1314
- catch (error) {
1315
- this.logger.error('Failed to discover workspaces:', error);
1316
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover workspaces: ${error}`);
1317
- }
1318
- }
1319
1285
  // Mobile Implementation
1320
1286
  async configureMobileApp(args) {
1321
1287
  try {
@@ -2609,6 +2575,389 @@ ${configList}${layoutsText}${offlineText}
2609
2575
  throw error;
2610
2576
  }
2611
2577
  }
2578
+ /**
2579
+ * STEP 1: Create Experience Record (sys_ux_experience)
2580
+ * This is the top-level container for the workspace
2581
+ */
2582
+ async snow_create_ux_experience(args) {
2583
+ try {
2584
+ this.logger.info(`Creating UX Experience: ${args.name}`);
2585
+ // Test mode validation - reject fake test data
2586
+ if (args.name && (args.name.toLowerCase().includes('test') || args.name === 'Test Experience')) {
2587
+ return {
2588
+ success: false,
2589
+ error: 'Test mode detected. This tool requires a real workspace name.',
2590
+ suggestion: 'Use a realistic workspace name like "IT Support Workspace" or "Customer Service Hub"',
2591
+ test_mode: true
2592
+ };
2593
+ }
2594
+ // Find default shell macroponent - usually x_snc_app_shell_uib_app_shell
2595
+ const shellQuery = await this.client.searchRecords('sys_ux_macroponent', 'name=x_snc_app_shell_uib_app_shell');
2596
+ let shellSysId = null;
2597
+ if (shellQuery.success && shellQuery.data.result.length > 0) {
2598
+ shellSysId = shellQuery.data.result[0].sys_id;
2599
+ this.logger.info(`āœ… Found app shell macroponent: ${shellSysId}`);
2600
+ }
2601
+ else {
2602
+ this.logger.warn(`āš ļø Default app shell not found, will use provided root_macroponent`);
2603
+ shellSysId = args.root_macroponent || null;
2604
+ }
2605
+ // Create experience record
2606
+ const experienceData = {
2607
+ name: args.name,
2608
+ active: true,
2609
+ root_macroponent: shellSysId,
2610
+ description: args.description || `Experience for ${args.name}`
2611
+ };
2612
+ // Check if UX framework is available
2613
+ const uxCheck = await this.client.searchRecords('sys_ux_experience', '', 1);
2614
+ if (!uxCheck.success) {
2615
+ return {
2616
+ success: false,
2617
+ error: 'Now Experience Framework (UXF) is not available in this ServiceNow instance.',
2618
+ suggestion: 'UX Workspace creation requires the Now Experience Framework plugin. Contact your ServiceNow administrator.',
2619
+ plugin_required: 'Now Experience Framework (UXF)',
2620
+ licensing_note: 'UXF may require additional ServiceNow licensing'
2621
+ };
2622
+ }
2623
+ const result = await this.client.createRecord('sys_ux_experience', experienceData);
2624
+ if (result.success && result.data && result.data.result && result.data.result.sys_id) {
2625
+ this.logger.info(`āœ… UX Experience created with sys_id: ${result.data.result.sys_id}`);
2626
+ return {
2627
+ success: true,
2628
+ experience_sys_id: result.data.result.sys_id,
2629
+ message: `Experience '${args.name}' created successfully`,
2630
+ next_step: "Create App Configuration using this experience_sys_id"
2631
+ };
2632
+ }
2633
+ else {
2634
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error creating experience';
2635
+ throw new Error(`Failed to create experience: ${error}`);
2636
+ }
2637
+ }
2638
+ catch (error) {
2639
+ this.logger.error('Failed to create UX experience:', error);
2640
+ throw error;
2641
+ }
2642
+ }
2643
+ /**
2644
+ * STEP 2: Create App Configuration Record (sys_ux_app_config)
2645
+ * Links to the experience and contains workspace settings
2646
+ */
2647
+ async snow_create_ux_app_config(args) {
2648
+ try {
2649
+ this.logger.info(`Creating UX App Config: ${args.name}`);
2650
+ // Validate experience_sys_id format
2651
+ if (!args.experience_sys_id || args.experience_sys_id.length !== 32) {
2652
+ return {
2653
+ success: false,
2654
+ error: `Invalid experience_sys_id: '${args.experience_sys_id}'. Must be a valid 32-character ServiceNow sys_id.`,
2655
+ suggestion: 'Use the sys_id returned from snow_create_ux_experience (Step 1)',
2656
+ validation_error: true
2657
+ };
2658
+ }
2659
+ // Validate experience exists
2660
+ const experienceCheck = await this.client.getRecord('sys_ux_experience', args.experience_sys_id);
2661
+ if (!experienceCheck.success) {
2662
+ return {
2663
+ success: false,
2664
+ error: `Experience with sys_id ${args.experience_sys_id} not found. Please create the experience first using snow_create_ux_experience.`,
2665
+ suggestion: 'Run Step 1: snow_create_ux_experience before creating app config'
2666
+ };
2667
+ }
2668
+ // Create app config record
2669
+ const configData = {
2670
+ name: args.name,
2671
+ experience_assoc: args.experience_sys_id, // CRITICAL: Link to experience
2672
+ description: args.description || `App configuration for ${args.name}`,
2673
+ // landing_page_route will be set in Step 6
2674
+ };
2675
+ const result = await this.client.createRecord('sys_ux_app_config', configData);
2676
+ if (result.success && result.data && result.data.result && result.data.result.sys_id) {
2677
+ this.logger.info(`āœ… UX App Config created with sys_id: ${result.data.result.sys_id}`);
2678
+ return {
2679
+ success: true,
2680
+ app_config_sys_id: result.data.result.sys_id,
2681
+ message: `App Configuration '${args.name}' created successfully`,
2682
+ next_step: "Create Page Macroponent using this app_config_sys_id"
2683
+ };
2684
+ }
2685
+ else {
2686
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error creating app config';
2687
+ throw new Error(`Failed to create app config: ${error}`);
2688
+ }
2689
+ }
2690
+ catch (error) {
2691
+ this.logger.error('Failed to create UX app config:', error);
2692
+ throw error;
2693
+ }
2694
+ }
2695
+ /**
2696
+ * STEP 3: Create Page Macroponent Record (sys_ux_macroponent)
2697
+ * Defines the actual page content that will be displayed
2698
+ */
2699
+ async snow_create_ux_page_macroponent(args) {
2700
+ try {
2701
+ this.logger.info(`Creating UX Page Macroponent: ${args.name}`);
2702
+ // Default composition for empty page
2703
+ const defaultComposition = {
2704
+ layout: {
2705
+ default: {
2706
+ "grid-template-columns": "1fr"
2707
+ }
2708
+ }
2709
+ };
2710
+ // Create macroponent record
2711
+ const macroponentData = {
2712
+ name: args.name,
2713
+ root_component: args.root_component || 'sn-canvas-panel', // Good default for pages
2714
+ composition: JSON.stringify(args.composition || defaultComposition),
2715
+ description: args.description || `Page macroponent for ${args.name}`
2716
+ };
2717
+ const result = await this.client.createRecord('sys_ux_macroponent', macroponentData);
2718
+ if (result.success && result.data && result.data.result && result.data.result.sys_id) {
2719
+ this.logger.info(`āœ… UX Page Macroponent created with sys_id: ${result.data.result.sys_id}`);
2720
+ return {
2721
+ success: true,
2722
+ macroponent_sys_id: result.data.result.sys_id,
2723
+ message: `Page Macroponent '${args.name}' created successfully`,
2724
+ next_step: "Create Page Registry using this macroponent_sys_id and app_config_sys_id"
2725
+ };
2726
+ }
2727
+ else {
2728
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error creating page macroponent';
2729
+ throw new Error(`Failed to create page macroponent: ${error}`);
2730
+ }
2731
+ }
2732
+ catch (error) {
2733
+ this.logger.error('Failed to create UX page macroponent:', error);
2734
+ throw error;
2735
+ }
2736
+ }
2737
+ /**
2738
+ * STEP 4: Create Page Registry Record (sys_ux_page_registry)
2739
+ * Registers the page for use within the workspace configuration
2740
+ */
2741
+ async snow_create_ux_page_registry(args) {
2742
+ try {
2743
+ this.logger.info(`Creating UX Page Registry: ${args.sys_name}`);
2744
+ // Validate app config and macroponent exist
2745
+ const configCheck = await this.client.getRecord('sys_ux_app_config', args.app_config_sys_id);
2746
+ if (!configCheck.success) {
2747
+ return {
2748
+ success: false,
2749
+ error: `App Config with sys_id ${args.app_config_sys_id} not found. Please create the app config first using snow_create_ux_app_config.`,
2750
+ suggestion: 'Run Step 2: snow_create_ux_app_config before creating page registry'
2751
+ };
2752
+ }
2753
+ const macroponentCheck = await this.client.getRecord('sys_ux_macroponent', args.macroponent_sys_id);
2754
+ if (!macroponentCheck.success) {
2755
+ return {
2756
+ success: false,
2757
+ error: `Macroponent with sys_id ${args.macroponent_sys_id} not found. Please create the macroponent first using snow_create_ux_page_macroponent.`,
2758
+ suggestion: 'Run Step 3: snow_create_ux_page_macroponent before creating page registry'
2759
+ };
2760
+ }
2761
+ // Create page registry record
2762
+ const registryData = {
2763
+ sys_name: args.sys_name, // Technical name for the page
2764
+ app_config: args.app_config_sys_id,
2765
+ root_macroponent: args.macroponent_sys_id,
2766
+ description: args.description || `Page registry for ${args.sys_name}`
2767
+ };
2768
+ const result = await this.client.createRecord('sys_ux_page_registry', registryData);
2769
+ if (result.success && result.data && result.data.result && result.data.result.sys_id) {
2770
+ this.logger.info(`āœ… UX Page Registry created with sys_id: ${result.data.result.sys_id}`);
2771
+ return {
2772
+ success: true,
2773
+ page_registry_sys_id: result.data.result.sys_id,
2774
+ page_sys_name: args.sys_name,
2775
+ message: `Page Registry '${args.sys_name}' created successfully`,
2776
+ next_step: "Create Route using this page sys_name and app_config_sys_id"
2777
+ };
2778
+ }
2779
+ else {
2780
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error creating page registry';
2781
+ throw new Error(`Failed to create page registry: ${error}`);
2782
+ }
2783
+ }
2784
+ catch (error) {
2785
+ this.logger.error('Failed to create UX page registry:', error);
2786
+ throw error;
2787
+ }
2788
+ }
2789
+ /**
2790
+ * STEP 5: Create Route Record (sys_ux_app_route)
2791
+ * Defines the URL slug that leads to the page
2792
+ */
2793
+ async snow_create_ux_app_route(args) {
2794
+ try {
2795
+ this.logger.info(`Creating UX App Route: ${args.name}`);
2796
+ // Validate app config exists
2797
+ const configCheck = await this.client.getRecord('sys_ux_app_config', args.app_config_sys_id);
2798
+ if (!configCheck.success) {
2799
+ return {
2800
+ success: false,
2801
+ error: `App Config with sys_id ${args.app_config_sys_id} not found. Please create the app config first using snow_create_ux_app_config.`,
2802
+ suggestion: 'Run Step 2: snow_create_ux_app_config before creating route'
2803
+ };
2804
+ }
2805
+ // Create route record
2806
+ const routeData = {
2807
+ name: args.name, // This becomes the URL slug
2808
+ app_config: args.app_config_sys_id,
2809
+ page: args.page_sys_name, // References the sys_name from page registry
2810
+ route_type: 'page',
2811
+ description: args.description || `Route for page ${args.page_sys_name}`
2812
+ };
2813
+ const result = await this.client.createRecord('sys_ux_app_route', routeData);
2814
+ if (result.success && result.data && result.data.result && result.data.result.sys_id) {
2815
+ this.logger.info(`āœ… UX App Route created with sys_id: ${result.data.result.sys_id}`);
2816
+ return {
2817
+ success: true,
2818
+ route_sys_id: result.data.result.sys_id,
2819
+ route_name: args.name,
2820
+ url_slug: args.name,
2821
+ message: `Route '${args.name}' created successfully`,
2822
+ next_step: "Update App Configuration with this route as landing_page_route"
2823
+ };
2824
+ }
2825
+ else {
2826
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error creating app route';
2827
+ throw new Error(`Failed to create app route: ${error}`);
2828
+ }
2829
+ }
2830
+ catch (error) {
2831
+ this.logger.error('Failed to create UX app route:', error);
2832
+ throw error;
2833
+ }
2834
+ }
2835
+ /**
2836
+ * STEP 6: Update App Configuration with Landing Page Route
2837
+ * Sets the default landing page for the workspace
2838
+ */
2839
+ async snow_update_ux_app_config_landing_page(args) {
2840
+ try {
2841
+ this.logger.info(`Updating App Config landing page: ${args.app_config_sys_id}`);
2842
+ // Update app config with landing page route
2843
+ const updateData = {
2844
+ landing_page_route: args.route_name
2845
+ };
2846
+ const result = await this.client.updateRecord('sys_ux_app_config', args.app_config_sys_id, updateData);
2847
+ if (result.success) {
2848
+ this.logger.info(`āœ… App Config updated with landing page route: ${args.route_name}`);
2849
+ return {
2850
+ success: true,
2851
+ message: `App Configuration updated with landing page route '${args.route_name}'`,
2852
+ workspace_complete: true,
2853
+ summary: "Workspace creation completed successfully - all 6 steps finished"
2854
+ };
2855
+ }
2856
+ else {
2857
+ const error = (result.data && result.data.error) || (result.error) || 'Unknown error updating app config';
2858
+ throw new Error(`Failed to update app config: ${error}`);
2859
+ }
2860
+ }
2861
+ catch (error) {
2862
+ this.logger.error('Failed to update app config landing page:', error);
2863
+ throw error;
2864
+ }
2865
+ }
2866
+ /**
2867
+ * COMPLETE WORKSPACE WORKFLOW: All 6 steps in sequence
2868
+ * Creates a fully functional workspace in the correct order
2869
+ */
2870
+ async snow_create_complete_workspace(args) {
2871
+ try {
2872
+ this.logger.info(`šŸš€ Starting complete workspace creation: ${args.workspace_name}`);
2873
+ const results = {
2874
+ workspace_name: args.workspace_name,
2875
+ steps_completed: [],
2876
+ sys_ids: {}
2877
+ };
2878
+ // STEP 1: Create Experience
2879
+ const experience = await this.snow_create_ux_experience({
2880
+ name: args.workspace_name,
2881
+ description: args.description || `Complete workspace: ${args.workspace_name}`,
2882
+ root_macroponent: args.root_macroponent
2883
+ });
2884
+ results.steps_completed.push('Experience Created');
2885
+ results.sys_ids.experience_sys_id = experience.experience_sys_id;
2886
+ // STEP 2: Create App Config
2887
+ const appConfig = await this.snow_create_ux_app_config({
2888
+ name: `${args.workspace_name} Config`,
2889
+ experience_sys_id: experience.experience_sys_id,
2890
+ description: `Configuration for ${args.workspace_name}`
2891
+ });
2892
+ results.steps_completed.push('App Configuration Created');
2893
+ results.sys_ids.app_config_sys_id = appConfig.app_config_sys_id;
2894
+ // STEP 3: Create Page Macroponent
2895
+ const homePage = args.home_page_name || 'home';
2896
+ const macroponent = await this.snow_create_ux_page_macroponent({
2897
+ name: `${args.workspace_name} ${homePage} Page`,
2898
+ root_component: args.root_component || 'sn-canvas-panel',
2899
+ composition: args.composition,
2900
+ description: `Home page for ${args.workspace_name}`
2901
+ });
2902
+ results.steps_completed.push('Page Macroponent Created');
2903
+ results.sys_ids.macroponent_sys_id = macroponent.macroponent_sys_id;
2904
+ // STEP 4: Create Page Registry
2905
+ const pageSysName = this.generateTechnicalName(args.workspace_name, homePage);
2906
+ const registry = await this.snow_create_ux_page_registry({
2907
+ sys_name: pageSysName,
2908
+ app_config_sys_id: appConfig.app_config_sys_id,
2909
+ macroponent_sys_id: macroponent.macroponent_sys_id,
2910
+ description: `Registry for ${args.workspace_name} home page`
2911
+ });
2912
+ results.steps_completed.push('Page Registry Created');
2913
+ results.sys_ids.page_registry_sys_id = registry.page_registry_sys_id;
2914
+ // STEP 5: Create Route
2915
+ const routeName = args.route_name || homePage;
2916
+ const route = await this.snow_create_ux_app_route({
2917
+ name: routeName,
2918
+ app_config_sys_id: appConfig.app_config_sys_id,
2919
+ page_sys_name: pageSysName,
2920
+ description: `Route for ${args.workspace_name} ${homePage} page`
2921
+ });
2922
+ results.steps_completed.push('Route Created');
2923
+ results.sys_ids.route_sys_id = route.route_sys_id;
2924
+ // STEP 6: Update App Config with Landing Page
2925
+ await this.snow_update_ux_app_config_landing_page({
2926
+ app_config_sys_id: appConfig.app_config_sys_id,
2927
+ route_name: routeName
2928
+ });
2929
+ results.steps_completed.push('Landing Page Route Set');
2930
+ this.logger.info(`šŸŽ‰ Complete workspace creation finished successfully!`);
2931
+ return {
2932
+ success: true,
2933
+ workspace_name: args.workspace_name,
2934
+ all_steps_completed: true,
2935
+ steps_completed: results.steps_completed,
2936
+ sys_ids: results.sys_ids,
2937
+ workspace_url: `/now/experience/workspace/${routeName}`,
2938
+ message: `Workspace '${args.workspace_name}' created successfully with all 6 steps completed`,
2939
+ summary: `Experience → App Config → Page Macroponent → Page Registry → Route → Landing Page Route - All connected properly`
2940
+ };
2941
+ }
2942
+ catch (error) {
2943
+ this.logger.error('Failed to create complete workspace:', error);
2944
+ throw error;
2945
+ }
2946
+ }
2947
+ /**
2948
+ * Generate technical name for pages (consistent formatting)
2949
+ */
2950
+ generateTechnicalName(workspaceName, pageName) {
2951
+ const combined = `${workspaceName}_${pageName}`;
2952
+ return combined
2953
+ .toLowerCase()
2954
+ .trim()
2955
+ .replace(/\s+/g, '_')
2956
+ .replace(/[^a-z0-9_]/g, '')
2957
+ .replace(/__+/g, '_')
2958
+ .replace(/^_|_$/g, '')
2959
+ .substring(0, 80);
2960
+ }
2612
2961
  /**
2613
2962
  * Generate valid workspace URL from name (fix from user feedback)
2614
2963
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "4.5.45",
4
- "description": "Conversational ServiceNow development platform using Claude Code. Multi-agent orchestration with 20+ MCP servers providing 200+ ServiceNow tools for comprehensive platform development.",
3
+ "version": "4.5.47",
4
+ "description": "Conversational ServiceNow development platform using Claude Code. Multi-agent orchestration with 20+ MCP servers providing 240+ ServiceNow tools including complete 6-step UX Workspace creation workflow.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6
6
  <title>Snow-Flow | Conversational ServiceNow Development Platform</title>
7
- <meta name="description" content="Snow-Flow: Conversational ServiceNow development platform using Claude Code. Multi-agent orchestration with 20+ MCP servers providing 235+ ServiceNow tools including complete UI Builder integration for comprehensive platform development.">
7
+ <meta name="description" content="Snow-Flow: Conversational ServiceNow development platform using Claude Code. Multi-agent orchestration with 20+ MCP servers providing 240+ ServiceNow tools including complete 6-step UX Workspace creation workflow and UI Builder integration.">
8
8
 
9
9
  <!-- Modern CSS -->
10
10
  <link rel="stylesheet" href="css/style.css">
@@ -131,7 +131,7 @@
131
131
  </span>
132
132
  <span class="pill pill-pulse" style="animation-delay: 2.4s">
133
133
  <span class="pill-icon">⚔</span>
134
- <span class="pill-text">235+ Tools + UI Builder</span>
134
+ <span class="pill-text">240+ Tools + UX Workspace</span>
135
135
  </span>
136
136
  <span class="pill pill-float" style="animation-delay: 2.6s">
137
137
  <span class="pill-icon">šŸ—ļø</span>
@@ -605,7 +605,7 @@ and historical patterns. Deploy as real-time API" \
605
605
  <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_list_flows</li>
606
606
  <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_execute_flow</li>
607
607
  <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_get_flow_details</li>
608
- <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_create_workspace</li>
608
+ <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_create_complete_workspace</li>
609
609
  <li style="color: var(--gray-500); font-size: var(--font-xs); margin-bottom: var(--space-1);">• snow_configure_mobile_app</li>
610
610
  <li style="color: var(--gray-500); font-size: var(--font-xs);">• snow_send_push_notification</li>
611
611
  </ul>
package/website/tools.js CHANGED
@@ -491,16 +491,41 @@ const snowFlowTools = {
491
491
  description: "Import flows from XML (only programmatic creation method)."
492
492
  },
493
493
 
494
- // Configurable Agent Workspace Tools (UX App Architecture)
494
+ // šŸ—ļø COMPLETE UX WORKSPACE CREATION (6-Step Workflow)
495
495
  {
496
- name: "snow_create_workspace",
497
- description: "Create Configurable Agent Workspaces using modern UX App architecture (sys_ux_app_route, sys_ux_screen_type, sys_ux_screen, sys_ux_macroponent).",
498
- highlight: "šŸ—ļø UX App Architecture"
496
+ name: "snow_create_complete_workspace",
497
+ description: "Create Complete UX Workspace - Executes all 6 steps automatically: Experience → App Config → Page Macroponent → Page Registry → Route → Landing Page Route. Creates fully functional workspace.",
498
+ highlight: "šŸŽ† COMPLETE WORKFLOW"
499
499
  },
500
500
  {
501
- name: "snow_discover_workspaces",
502
- description: "Discover available Configurable Agent Workspaces (UX Apps) with screen collections and individual screens.",
503
- highlight: "šŸ” UX App Discovery"
501
+ name: "snow_create_ux_experience",
502
+ description: "STEP 1: Create UX Experience Record (sys_ux_experience) - The top-level container for the workspace.",
503
+ highlight: "šŸ—ļø Step 1"
504
+ },
505
+ {
506
+ name: "snow_create_ux_app_config",
507
+ description: "STEP 2: Create UX App Configuration Record (sys_ux_app_config) - Contains workspace settings and links to experience.",
508
+ highlight: "šŸ—ļø Step 2"
509
+ },
510
+ {
511
+ name: "snow_create_ux_page_macroponent",
512
+ description: "STEP 3: Create Page Macroponent Record (sys_ux_macroponent) - Defines actual page content to be displayed.",
513
+ highlight: "šŸ—ļø Step 3"
514
+ },
515
+ {
516
+ name: "snow_create_ux_page_registry",
517
+ description: "STEP 4: Create Page Registry Record (sys_ux_page_registry) - Registers page for use within workspace configuration.",
518
+ highlight: "šŸ—ļø Step 4"
519
+ },
520
+ {
521
+ name: "snow_create_ux_app_route",
522
+ description: "STEP 5: Create Route Record (sys_ux_app_route) - Defines URL slug that leads to the page.",
523
+ highlight: "šŸ—ļø Step 5"
524
+ },
525
+ {
526
+ name: "snow_update_ux_app_config_landing_page",
527
+ description: "STEP 6: Update App Configuration with Landing Page Route - Sets default landing page for workspace.",
528
+ highlight: "šŸ—ļø Step 6"
504
529
  },
505
530
 
506
531
  // Mobile App Tools