mcpField
C
code-assist

code-assist

github.com/happy-technologies-llc開発4143 回インストール4 日前に更新
リポジトリ

code-assist について

code-assistは、ServiceNowの開発者向けスキルで、AIを使ったサーバー側・クライアント側スクリプトの生成やリファクタリングを支援します。ビジネスルール、クライアントスクリプト、スクリプトインクルード、UIスクリプト、ServiceNow APIのパターンを扱います。

主な機能

  1. 01ビジネスルールとクライアントスクリプトの生成
  2. 02再利用可能なスクリプトインクルードとUIスクリプトの作成
  3. 03GlideRecord、GlideSystem、GlideAjaxパターンの適用
  4. 04ServiceNowのスクリプト作成慣行への準拠
  5. 05生成したスクリプトの検索と検証

活用シーン

  1. 01サーバー側の業務ロジックの作成
  2. 02フォーム操作スクリプトの生成
  3. 03再利用可能なサーバー側JavaScriptライブラリの構築
  4. 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-RecordPOST /api/now/table/sys_script
クライアントスクリプトを作成SN-Create-RecordPOST /api/now/table/sys_script_client
スクリプトインクルードを作成SN-Create-RecordPOST /api/now/table/sys_script_include
UI スクリプトを作成SN-Create-RecordPOST /api/now/table/sys_ui_script
既存のスクリプトをクエリSN-Query-TableGET /api/now/table/{script_table}
スクリプトを更新SN-Update-RecordPATCH /api/now/table/{script_table}/{sys_id}
テーブルスキーマを取得SN-Get-Table-SchemaGET /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 コメントを追加する: パラメーター、戻り値の型、副作用を記述します

トラブルシューティング

ビジネスルールが実行されない

症状: ビジネスルールのスクリプトが実行されません 原因:

  1. ルールが無効になっている
  2. when の設定が誤っている(before/after/async)
  3. フィルター条件に一致していない
  4. アクションフラグ(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
クライアントスクリプトが実行されない

症状: クライアントスクリプトがフォームで実行されません 原因:

  1. スクリプトが無効になっている
  2. タイプが誤っている(onLoad/onChange/onSubmit)
  3. onChange スクリプトの fieldname が指定されていない
  4. 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」エラーが発生します 原因:

  1. スクリプトインクルードが無効になっている
  2. 名前が一致していない(大文字と小文字を区別します)
  3. スコープのアクセス制限
  4. 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 - ビジネスルールと連携するセキュリティルール

コメント