关于 code-assist
code-assist 是面向 ServiceNow 开发者的技能,借助 AI 生成或重构服务器端和客户端脚本,涵盖业务规则、客户端脚本、脚本包含项、UI 脚本及 ServiceNow API 模式。
主要功能
- 01生成业务规则和客户端脚本
- 02创建可复用脚本包含项和 UI 脚本
- 03应用 GlideRecord、GlideSystem 和 GlideAjax 模式
- 04遵循 ServiceNow 脚本实践
- 05查询并验证生成的脚本
适用场景
- 01创建服务器端业务逻辑
- 02生成表单交互脚本
- 03构建可复用的服务器端 JavaScript 库
- 04按 ServiceNow 约定重构脚本
MCP 相关
该技能介绍 SN-Create-Record、SN-Query-Table、SN-Update-Record 和 SN-Get-Table-Schema 等 MCP 工具,用于创建、检查、更新脚本记录及查询 ServiceNow 表结构。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill code-assist安装后在 Claude Code 中输入/code-assist确认已加载。
详细介绍
AI 辅助的 ServiceNow 代码生成
概述
此技能涵盖使用 AI 辅助生成 ServiceNow 核心服务端和客户端脚本工件的代码:
- 业务规则(sys_script):由数据库操作触发的服务端逻辑
- 客户端脚本(sys_script_client):用于表单交互的浏览器端逻辑
- 脚本包含(sys_script_include):可复用的服务端 JavaScript 库
- UI 脚本(sys_ui_script):全局客户端 JavaScript 库
- 应用 ServiceNow 脚本最佳实践和 API 约定
- 使用恰当的 GlideRecord、GlideSystem 和 GlideAjax 模式生成代码
适用场景: 创建新的服务端或客户端脚本、重构现有代码、实现业务逻辑,或为常见的 ServiceNow 模式生成样板代码时。
前提条件
- 角色: 业务规则需要
admin或personalize_script;脚本包含需要admin - 访问权限: sys_script、sys_script_include、sys_ui_script、sys_script_client 表
- 知识: JavaScript、ServiceNow 脚本 API(GlideRecord、GlideSystem、GlideAjax)
- 相关技能: 完成
development/code-review,以便在生成后进行验证
流程
步骤 1:了解 ServiceNow 脚本类型
| 脚本类型 | 表 | 运行位置 | 触发条件 |
|---|---|---|---|
| 业务规则 | sys_script | 服务端 | 数据库操作(插入、更新、删除、查询) |
| 客户端脚本 | sys_script_client | 客户端(浏览器) | 表单事件(onLoad、onChange、onSubmit) |
| 脚本包含 | sys_script_include | 服务端 | 由其他服务端脚本调用 |
| UI 脚本 | sys_ui_script | 客户端(浏览器) | 全局或按需加载 |
主要表:
| 表 | 用途 |
|---|---|
| sys_script | 包含脚本正文的业务规则定义 |
| sys_script_client | 客户端脚本定义 |
| sys_script_include | 可复用的服务端脚本类 |
| sys_ui_script | 全局客户端脚本库 |
步骤 2:生成业务规则
MCP 方式——创建 before 业务规则:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: "Validate Priority on Critical Incidents"
collection: incident
when: before
order: 100
active: true
filter_condition: priority=1
action_insert: true
action_update: true
action_delete: false
action_query: false
script: |
(function executeRule(current, previous) {
// Ensure critical incidents have an assignment group
if (!current.assignment_group) {
current.assignment_group.setDisplayValue('Service Desk');
gs.addInfoMessage('Assignment group set to Service Desk for critical incident');
}
// Set SLA tracking
if (current.isNewRecord()) {
current.sla_due = gs.daysAgoEnd(-1); // Due in 1 day
}
})(current, previous);
REST 替代方案:
curl -u "$SN_USER:$SN_PASS" \
"$SN_INSTANCE/api/now/table/sys_script" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-X POST \
-d '{
"name": "Validate Priority on Critical Incidents",
"collection": "incident",
"when": "before",
"order": 100,
"active": true,
"action_insert": true,
"action_update": true,
"script": "(function executeRule(current, previous) {\n if (!current.assignment_group) {\n current.assignment_group.setDisplayValue(\"Service Desk\");\n }\n})(current, previous);"
}'
步骤 3:生成业务规则模式
after 业务规则——发送通知:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: "Notify on High Priority Change"
collection: change_request
when: after
order: 200
active: true
action_insert: true
action_update: true
script: |
(function executeRule(current, previous) {
// Only trigger when priority changes to high
if (current.priority.changesTo(1) || current.priority.changesTo(2)) {
var assignmentGroup = current.assignment_group.getDisplayValue();
gs.eventQueue('change.high_priority', current, assignmentGroup, current.number);
}
})(current, previous);
异步业务规则——繁重处理:
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: "Sync CMDB Relationships on Insert"
collection: cmdb_ci_business_app
when: async
order: 100
active: true
action_insert: true
script: |
(function executeRule(current, previous) {
// Async - runs in background, safe for heavy operations
var relSync = new CMDBRelationshipSync();
relSync.syncRelationships(current.sys_id);
// Update related records
var gr = new GlideRecord('cmdb_rel_ci');
gr.addQuery('parent', current.sys_id);
gr.query();
while (gr.next()) {
gr.setValue('validated', true);
gr.update();
}
})(current, previous);
步骤 4:生成客户端脚本
onChange 客户端脚本:
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Set Impact from Category"
table: incident
type: onChange
fieldname: category
active: true
ui_type: 0
script: |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue === '') {
return;
}
// Map category to impact
var impactMap = {
'network': '1',
'hardware': '2',
'software': '3',
'inquiry': '3'
};
var impact = impactMap[newValue] || '3';
g_form.setValue('impact', impact);
if (newValue === 'network') {
g_form.setMandatory('cmdb_ci', true);
g_form.showFieldMsg('cmdb_ci', 'Configuration item is required for network issues', 'info');
} else {
g_form.setMandatory('cmdb_ci', false);
g_form.hideFieldMsg('cmdb_ci');
}
}
onLoad 客户端脚本:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Initialize Form Defaults"
table: incident
type: onLoad
active: true
ui_type: 0
script: |
function onLoad() {
// Only apply to new records
if (!g_form.isNewRecord()) {
return;
}
// Set default contact type
g_form.setValue('contact_type', 'self-service');
// Hide fields not needed for self-service
g_form.setVisible('caller_id', false);
// Make description mandatory
g_form.setMandatory('description', true);
// Add informational message
g_form.addInfoMessage('Please provide detailed information to help us resolve your issue quickly.');
}
onSubmit 客户端脚本:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Validate Before Submit"
table: incident
type: onSubmit
active: true
ui_type: 0
script: |
function onSubmit() {
// Validate short description length
var shortDesc = g_form.getValue('short_description');
if (shortDesc.length < 10) {
g_form.showFieldMsg('short_description', 'Please provide a more detailed summary (at least 10 characters)', 'error');
return false;
}
// Confirm high priority submission
var priority = g_form.getValue('priority');
if (priority === '1') {
return confirm('You are submitting a Critical priority incident. This will trigger immediate notifications. Continue?');
}
return true;
}
步骤 5:生成脚本包含
标准脚本包含(基于类):
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_include
data:
name: "IncidentUtils"
api_name: "global.IncidentUtils"
client_callable: false
active: true
access: package_private
script: |
var IncidentUtils = Class.create();
IncidentUtils.prototype = {
initialize: function() {
},
/**
* Get the count of open incidents for a given CI
* @param {string} ciSysId - The sys_id of the configuration item
* @returns {number} Count of open incidents
*/
getOpenIncidentCount: function(ciSysId) {
var ga = new GlideAggregate('incident');
ga.addQuery('cmdb_ci', ciSysId);
ga.addQuery('state', 'IN', '1,2,3');
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
return parseInt(ga.getAggregate('COUNT'), 10);
}
return 0;
},
/**
* Auto-assign incident based on category and location
* @param {GlideRecord} incident - The incident GlideRecord
* @returns {string} sys_id of the assigned group
*/
autoAssign: function(incident) {
var gr = new GlideRecord('sys_user_group');
gr.addQuery('type', incident.category);
gr.addQuery('active', true);
gr.setLimit(1);
gr.query();
if (gr.next()) {
incident.assignment_group = gr.sys_id;
return gr.sys_id.toString();
}
return null;
},
type: 'IncidentUtils'
};
可由 GlideAjax 调用的脚本包含:
Tool: SN-Create-Record
Parameters:
table_name: sys_script_include
data:
name: "IncidentAjax"
api_name: "global.IncidentAjax"
client_callable: true
active: true
script: |
var IncidentAjax = Class.create();
IncidentAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
/**
* Get open incident count for a CI (callable from client)
*/
getOpenCount: function() {
var ciSysId = this.getParameter('sysparm_ci_sys_id');
var utils = new IncidentUtils();
var count = utils.getOpenIncidentCount(ciSysId);
return count.toString();
},
/**
* Validate if a user can be assigned to an incident
*/
validateAssignee: function() {
var userId = this.getParameter('sysparm_user_id');
var gr = new GlideRecord('sys_user');
if (gr.get(userId)) {
var result = {};
result.valid = gr.active.toString() === 'true';
result.name = gr.getDisplayValue();
result.hasRole = gr.hasRole('itil');
return JSON.stringify(result);
}
return JSON.stringify({valid: false});
},
type: 'IncidentAjax'
});
步骤 6:生成 UI 脚本
MCP 方式:
Tool: SN-Create-Record
Parameters:
table_name: sys_ui_script
data:
name: "CustomFormHelpers"
global: true
active: true
ui_type: 0
script: |
/**
* Custom form helper utilities
* Available globally on all forms
*/
var CustomFormHelpers = {
/**
* Call a GlideAjax script include and handle response
* @param {string} scriptInclude - Name of the script include
* @param {string} method - Method name to call
* @param {object} params - Key-value pairs of parameters
* @param {function} callback - Callback function receiving the answer
*/
ajaxCall: function(scriptInclude, method, params, callback) {
var ga = new GlideAjax(scriptInclude);
ga.addParam('sysparm_name', method);
for (var key in params) {
if (params.hasOwnProperty(key)) {
ga.addParam(key, params[key]);
}
}
ga.getXMLAnswer(function(answer) {
if (callback && typeof callback === 'function') {
callback(answer);
}
});
},
/**
* Show a confirmation dialog with custom message
* @param {string} message - Message to display
* @param {function} onConfirm - Callback on confirm
* @param {function} onCancel - Callback on cancel
*/
confirmAction: function(message, onConfirm, onCancel) {
var dialog = new GlideDialogWindow('glide_confirm_standard');
dialog.setTitle('Confirm Action');
dialog.setBody(message);
dialog.setPreference('onPromptComplete', onConfirm);
dialog.setPreference('onPromptCancel', onCancel || function() {});
dialog.render();
}
};
步骤 7:部署并验证
验证创建的脚本:
MCP 方式:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: name=Validate Priority on Critical Incidents
fields: sys_id,name,collection,when,active,script
limit: 1
REST 替代方案:
curl -u "$SN_USER:$SN_PASS" \
"$SN_INSTANCE/api/now/table/sys_script?sysparm_query=name=Validate Priority on Critical Incidents&sysparm_fields=sys_id,name,collection,when,active" \
-H "Accept: application/json"
工具用法
| 操作 | MCP 工具 | REST 端点 |
|---|---|---|
| 创建业务规则 | SN-Create-Record | POST /api/now/table/sys_script |
| 创建客户端脚本 | SN-Create-Record | POST /api/now/table/sys_script_client |
| 创建脚本包含 | SN-Create-Record | POST /api/now/table/sys_script_include |
| 创建 UI 脚本 | SN-Create-Record | POST /api/now/table/sys_ui_script |
| 查询现有脚本 | SN-Query-Table | GET /api/now/table/{script_table} |
| 更新脚本 | SN-Update-Record | PATCH /api/now/table/{script_table}/{sys_id} |
| 获取表架构 | SN-Get-Table-Schema | GET /api/now/table/sys_dictionary |
最佳实践
- 使用前置规则进行验证: 在数据到达数据库之前验证并修改数据
- 使用后置规则处理副作用: 通知、事件触发器以及对其他记录的更新
- 使用异步规则执行繁重操作: GlideRecord 循环、外部集成、批量更新
- 避免在客户端脚本中操作 DOM: 使用 g_form API 方法,而不是直接访问 DOM
- 尽量减少客户端脚本中的 GlideRecord 查询: 使用 GlideAjax 调用服务器端逻辑
- 将业务规则脚本封装在 IIFE 中: 使用
(function executeRule(current, previous) { ... })(current, previous); - 在 onChange 中检查 isLoading: 如果
isLoading为 true,务必提前返回,以避免在表单加载期间造成干扰 - 使用脚本包含以复用逻辑: 切勿在业务规则之间重复代码
- 设置合适的顺序值: 数值越小,执行越早;按 100 递增留出空间,以便后续插入
- 正确设置脚本作用域: 自定义应用使用有作用域的应用 API(例如
x_myapp.MyScriptInclude) - 添加 JSDoc 注释: 说明参数、返回类型和副作用
故障排除
业务规则未触发
症状: 业务规则脚本未执行 原因:
- 规则未激活
when设置错误(before/after/async)- 过滤条件不匹配
- 操作标记(insert/update/delete)未设置 解决方案:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: name=[rule_name]
fields: sys_id,name,active,when,collection,action_insert,action_update,filter_condition
客户端脚本未运行
症状: 客户端脚本未在表单上触发 原因:
- 脚本未激活
- 类型错误(onLoad/onChange/onSubmit)
- onChange 脚本缺少字段名
- UI 类型不匹配(桌面端、移动端或两者) 解决方案:
Tool: SN-Query-Table
Parameters:
table_name: sys_script_client
query: name=[script_name]
fields: sys_id,name,active,type,table,fieldname,ui_type
未找到脚本包含
症状: 出现“Class not found”或“is not defined”错误 原因:
- 脚本包含未激活
- 名称不匹配(区分大小写)
- 作用域访问限制
- GlideAjax 调用未设置 client_callable 解决方案:
Tool: SN-Query-Table
Parameters:
table_name: sys_script_include
query: name=[include_name]
fields: sys_id,name,active,client_callable,api_name,access
GlideAjax 返回空值
症状: GlideAjax 回调收到空值或 undefined 答案
原因: 脚本包含方法未返回值,或 client_callable 为 false
解决方案: 确认脚本包含已设置 client_callable: true,且方法返回字符串值。
示例
示例:完整功能——带客户端反馈的自动分配
# 1. Create server-side logic (Script Include)
Tool: SN-Create-Record
Parameters:
table_name: sys_script_include
data:
name: "AutoAssignmentEngine"
client_callable: true
active: true
script: |
var AutoAssignmentEngine = Class.create();
AutoAssignmentEngine.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getSuggestedGroup: function() {
var category = this.getParameter('sysparm_category');
var location = this.getParameter('sysparm_location');
var gr = new GlideRecord('sys_user_group');
gr.addQuery('type', category);
gr.addQuery('active', true);
gr.setLimit(1);
gr.query();
if (gr.next()) {
return JSON.stringify({sys_id: gr.sys_id.toString(), name: gr.getDisplayValue()});
}
return JSON.stringify({sys_id: '', name: ''});
},
type: 'AutoAssignmentEngine'
});
# 2. Create client script to call it
Tool: SN-Create-Record
Parameters:
table_name: sys_script_client
data:
name: "Auto-suggest Assignment Group"
table: incident
type: onChange
fieldname: category
active: true
script: |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue === '') return;
var ga = new GlideAjax('AutoAssignmentEngine');
ga.addParam('sysparm_name', 'getSuggestedGroup');
ga.addParam('sysparm_category', newValue);
ga.addParam('sysparm_location', g_form.getValue('location'));
ga.getXMLAnswer(function(answer) {
var result = JSON.parse(answer);
if (result.sys_id) {
g_form.setValue('assignment_group', result.sys_id, result.name);
g_form.addInfoMessage('Auto-assigned to: ' + result.name);
}
});
}
# 3. Create business rule as fallback
Tool: SN-Create-Record
Parameters:
table_name: sys_script
data:
name: "Ensure Assignment Group on Insert"
collection: incident
when: before
action_insert: true
active: true
script: |
(function executeRule(current, previous) {
if (!current.assignment_group) {
var engine = new AutoAssignmentEngine();
var result = engine.autoAssign(current);
if (!result) {
current.assignment_group.setDisplayValue('Service Desk');
}
}
})(current, previous);
相关技能
development/code-review- 检查生成代码的质量和安全性admin/workflow-creation- 构建与脚本配合使用的工作流catalog/variable-management- 为服务目录项目编写目录脚本security/acl-management- 配合业务规则使用的安全规则