code-assist について
code-assistは、ServiceNowの開発者向けスキルで、AIを使ったサーバー側・クライアント側スクリプトの生成やリファクタリングを支援します。ビジネスルール、クライアントスクリプト、スクリプトインクルード、UIスクリプト、ServiceNow APIのパターンを扱います。
主な機能
- 01ビジネスルールとクライアントスクリプトの生成
- 02再利用可能なスクリプトインクルードとUIスクリプトの作成
- 03GlideRecord、GlideSystem、GlideAjaxパターンの適用
- 04ServiceNowのスクリプト作成慣行への準拠
- 05生成したスクリプトの検索と検証
活用シーン
- 01サーバー側の業務ロジックの作成
- 02フォーム操作スクリプトの生成
- 03再利用可能なサーバー側JavaScriptライブラリの構築
- 04ServiceNowの規約に沿ったスクリプトのリファクタリング
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 コード生成
概要
このスキルでは、ServiceNow のサーバー側およびクライアント側の主要なスクリプト成果物について、AI 支援によるコード生成を扱います。
- ビジネスルール(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を完了してください
手順
Step 1: ServiceNow のスクリプトの種類を理解する
| スクリプトの種類 | テーブル | 実行場所 | トリガー |
|---|---|---|---|
| ビジネスルール | sys_script | サーバー | DB 操作(挿入、更新、削除、クエリ) |
| クライアントスクリプト | sys_script_client | クライアント(ブラウザー) | フォームイベント(onLoad、onChange、onSubmit) |
| スクリプトインクルード | sys_script_include | サーバー | 他のサーバースクリプトから呼び出し |
| UI スクリプト | sys_ui_script | クライアント(ブラウザー) | グローバルまたはオンデマンドで読み込み |
主なテーブル:
| テーブル | 用途 |
|---|---|
| sys_script | スクリプト本体を含むビジネスルールの定義 |
| sys_script_client | クライアントスクリプトの定義 |
| sys_script_include | 再利用可能なサーバー側スクリプトクラス |
| sys_ui_script | グローバルなクライアント側スクリプトライブラリ |
Step 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);"
}'
Step 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);
Step 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;
}
Step 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'
});
Step 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();
}
};
Step 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 |
ベストプラクティス
- 検証には before ルールを使用する: データがデータベースに到達する前に検証し、変更します
- 副作用には after ルールを使用する: 通知、イベントトリガー、他のレコードの更新に使用します
- 負荷の高い操作には async ルールを使用する: GlideRecord のループ、外部連携、一括更新に使用します
- クライアントスクリプトでの DOM 操作を避ける: DOM に直接アクセスするのではなく、g_form API メソッドを使用します
- クライアントスクリプトでの GlideRecord クエリを最小限にする: GlideAjax を使用してサーバーサイドのロジックを呼び出します
- ビジネスルールのスクリプトを IIFE で囲む:
(function executeRule(current, previous) { ... })(current, previous);を使用します - onChange で isLoading を確認する: フォームの読み込み中に干渉しないよう、
isLoadingがtrueの場合は常に早期に return します - 再利用可能なロジックにはスクリプトインクルードを使用する: ビジネスルール間でコードを重複させないでください
- 適切な order 値を設定する: 数値が小さいほど先に実行されます。将来の挿入に備えて、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 スクリプトの fieldname が指定されていない
- 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- ビジネスルールと連携するセキュリティルール