mcpField
U
ui-generation

ui-generation

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

ui-generation について

ServiceNow の開発者や市民開発者向けに、自然言語の要件からクライアントスクリプト、Service Portal ウィジェット、UI Builder ページなどを生成するスキルです。既存パターンの検索と生成物の検証も手順に含みます。

主な機能

  1. 01要件からコンポーネントと動作を特定
  2. 02フォーム用クライアントスクリプトの生成
  3. 03Service Portal ウィジェットの作成
  4. 04UI Builder ページとイベント対応の構築
  5. 05既存コンポーネントの検索と生成物の検証

活用シーン

  1. 01業務要件からフォームの動作を生成
  2. 02Service Portal ウィジェットの試作
  3. 03データリソース付きワークスペースページの構築
  4. 04UI 操作用イベントハンドラーの作成

MCP 対応

MCP ツールで既存の UI コンポーネントを検索し、スクリプトやページ設定を作成・更新します。データバインディングに必要なテーブルスキーマの確認にも使います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill ui-generation

インストール後、Claude Code で/ui-generationを入力して読み込まれたか確認します。

詳細

自然言語からの UI 生成

概要

このスキルでは、自然言語による説明から ServiceNow の UI コンポーネントを生成します。

  • フォームとリスト用のクライアントスクリプト(onChange、onLoad、onSubmit、onCellEdit)の作成
  • UI Builder と Service Portal ウィジェット用のデータバインディング設定の構築
  • ユーザー操作向けイベントハンドラーロジックの生成
  • Jelly/HTML テンプレートと処理スクリプトを使用した UI Pages の作成
  • AngularJS コントローラーと HTML テンプレートを使用した Service Portal ウィジェットの構築
  • データリソースとイベントマッピングを使用した UI Builder エクスペリエンスページの生成
  • 再利用可能なフォーム装飾とフォーマッター向けの UI マクロの作成

使用する場面: 開発者や市民開発者が、平易な言葉で記述されたビジネス要件から UI コンポーネントを作成する必要がある場合や、フォームの動作、ポータルウィジェット、ワークスペースページを迅速にプロトタイプ作成する場合に使用します。

前提条件

  • ロール: admin、personalize_form、または sp_admin(Service Portal ウィジェット用)
  • プラグイン: com.glide.ui.ui_page(UI Pages)、com.glide.service-portal(Service Portal)、com.sn_ui_builder(UI Builder)
  • アクセス権: sys_script_client、sys_ui_page、sp_widget、sys_ux_page_registry テーブルへの書き込みアクセス権
  • 知識: JavaScript の基礎、GlideForm API、AngularJS の基礎(Service Portal 用)、UI Builder の概念
  • 関連スキル: クライアントスクリプトのパターンについては development/client-scripts、カタログフォームの動作については catalog/ui-policies

手順

ステップ 1:自然言語の説明を解析する

ユーザーのリクエストを分析し、次の項目を特定します。

  • コンポーネントの種類: クライアントスクリプト、UI ページ、Service Portal ウィジェット、UI Builder ページ、UI マクロ
  • 対象のテーブル/フォーム: コンポーネントを適用するテーブルまたはフォーム
  • 動作: 実行する処理(フィールドの表示/非表示、データの検証、レコードの取得、UI の描画)
  • トリガー: 動作を開始するイベント(フィールド変更、フォーム読み込み、ボタンクリック、ページ読み込み)
  • データ依存関係: コンポーネントが読み取りまたは書き込みを必要とするデータ
ステップ 2:既存のコンポーネントを照会してパターンを確認する

MCP アプローチ:

Tool: SN-Query-Table
Parameters:
  table_name: sys_script_client
  query: table=incident^active=true^typeLIKEonChange
  fields: sys_id,name,table,type,field_name,script,active,ui_type
  limit: 10

REST アプローチ:

GET /api/now/table/sys_script_client
  ?sysparm_query=table=incident^active=true^typeLIKEonChange
  &sysparm_fields=sys_id,name,table,type,field_name,script,active,ui_type
  &sysparm_limit=10
ステップ 3:クライアントスクリプトを生成する

自然言語を GlideForm API の呼び出しに変換します。一般的なパターンは次のとおりです。

onChange -- フィールドの表示/非表示:

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_script_client
  data:
    name: "Show VPN fields when category is Network"
    table: incident
    type: onChange
    field_name: category
    ui_type: 0
    active: true
    script: |
      function onChange(control, oldValue, newValue, isLoading) {
        if (isLoading) return;
        var isNetwork = (newValue == 'network');
        g_form.setVisible('u_vpn_type', isNetwork);
        g_form.setVisible('u_network_segment', isNetwork);
        if (isNetwork) {
          g_form.setMandatory('u_vpn_type', true);
        } else {
          g_form.setMandatory('u_vpn_type', false);
        }
      }

REST アプローチ:

POST /api/now/table/sys_script_client
Body: {
  "name": "Show VPN fields when category is Network",
  "table": "incident",
  "type": "onChange",
  "field_name": "category",
  "ui_type": "0",
  "active": "true",
  "script": "function onChange(control, oldValue, newValue, isLoading) { ... }"
}
ステップ 4:フォーム初期化用の onLoad スクリプトを生成する

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_script_client
  data:
    name: "Initialize priority banner on form load"
    table: incident
    type: onLoad
    ui_type: 0
    active: true
    script: |
      function onLoad() {
        var priority = g_form.getValue('priority');
        if (priority == '1') {
          g_form.showFieldMsg('priority', 'Critical incident - follow P1 procedures', 'error');
          g_form.setReadOnly('category', true);
          g_form.setReadOnly('subcategory', true);
        }
      }
ステップ 5:onSubmit 検証スクリプトを生成する

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_script_client
  data:
    name: "Validate resolution notes before close"
    table: incident
    type: onSubmit
    ui_type: 0
    active: true
    script: |
      function onSubmit() {
        var state = g_form.getValue('state');
        if (state == '6') {
          var resolution = g_form.getValue('close_notes');
          if (!resolution || resolution.trim().length < 20) {
            g_form.showFieldMsg('close_notes', 'Resolution notes must be at least 20 characters', 'error');
            return false;
          }
        }
        return true;
      }
ステップ 6:Service Portal ウィジェットを生成する

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sp_widget
  data:
    name: "Team Incident Dashboard"
    id: "team-incident-dashboard"
    template: |
      <div class="panel panel-default">
        <div class="panel-heading"><h3>{{c.data.title}}</h3></div>
        <div class="panel-body">
          <div ng-repeat="inc in c.data.incidents track by inc.sys_id">
            <div class="alert alert-{{inc.priorityClass}}">
              <strong>{{inc.number}}</strong> - {{inc.short_description}}
              <span class="pull-right badge">{{inc.state}}</span>
            </div>
          </div>
          <p ng-if="c.data.incidents.length === 0">No open incidents.</p>
        </div>
      </div>
    client_script: |
      api.controller = function($scope) {
        var c = this;
      };
    server_script: |
      (function() {
        data.title = "My Team's Open Incidents";
        var gr = new GlideRecord('incident');
        gr.addQuery('assignment_group', gs.getUser().getMyGroups());
        gr.addActiveQuery();
        gr.orderByDesc('priority');
        gr.setLimit(20);
        gr.query();
        data.incidents = [];
        while (gr.next()) {
          data.incidents.push({
            sys_id: gr.getUniqueValue(),
            number: gr.getValue('number'),
            short_description: gr.getValue('short_description'),
            state: gr.getDisplayValue('state'),
            priority: gr.getValue('priority'),
            priorityClass: {'1':'danger','2':'warning','3':'info','4':'success'}[gr.getValue('priority')] || 'default'
          });
        }
      })();

REST アプローチ:

POST /api/now/table/sp_widget
Body: {
  "name": "Team Incident Dashboard",
  "id": "team-incident-dashboard",
  "template": "<div class='panel panel-default'>...</div>",
  "client_script": "api.controller = function($scope) { var c = this; };",
  "server_script": "(function() { ... })();"
}
ステップ 7:UI Builder エクスペリエンスページを生成する

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_ux_page_registry
  data:
    title: "Executive Incident Overview"
    path: /executive-incident-overview
    description: "Executive dashboard showing incident metrics and trends"
    category: workspace
    active: true

ページのデータリソースを設定します。

Tool: SN-Create-Record
Parameters:
  table_name: sys_ux_data_broker_transform
  data:
    name: "Incident Metrics Data Resource"
    page: "<page_sys_id>"
    table: incident
    query: "active=true^priority<=2"
    fields: "number,short_description,priority,state,assigned_to,sys_created_on"
    max_rows: 50
ステップ 8:イベントハンドラーを生成する

UI Builder のイベントからアクションへのマッピングを設定します。

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_ux_event
  data:
    name: "Row Click Navigate to Record"
    page: "<page_sys_id>"
    component: "<component_sys_id>"
    event_name: "click"
    action_type: "navigate"
    action_config: {
      "route": "/record/incident/${sys_id}",
      "target": "_self"
    }
ステップ 9:フォーム装飾用の UI マクロを生成する

MCP アプローチ:

Tool: SN-Create-Record
Parameters:
  table_name: sys_ui_macro
  data:
    name: "sla_countdown_banner"
    description: "Displays a countdown timer for SLA breach on incident form"
    xml: |
      <?xml version="1.0" encoding="utf-8"?>
      <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide">
        <g:evaluate var="jvar_sla_info" expression="
          var sla = new GlideRecord('task_sla');
          sla.addQuery('task', current.sys_id);
          sla.addQuery('has_breached', false);
          sla.addActiveQuery();
          sla.orderBy('planned_end_time');
          sla.setLimit(1);
          sla.query();
          sla.next() ? sla.getDisplayValue('planned_end_time') : 'N/A';
        "/>
        <div style="background:#fff3cd;padding:10px;border-radius:4px;margin-bottom:10px;">
          <strong>SLA Target:</strong> ${jvar_sla_info}
        </div>
      </j:jelly>
ステップ 10:生成したコンポーネントを検証してテストする

MCP アプローチ:

Tool: SN-Query-Table
Parameters:
  table_name: sys_script_client
  query: sys_id=<created_sys_id>
  fields: sys_id,name,table,type,active,sys_updated_on
  limit: 1

最近の更新レコードについて sys_update_xml テーブルを確認し、スクリプト構文エラーがないことを検証します。

Tool: SN-Query-Table
Parameters:
  table_name: sys_update_xml
  query: name=sys_script_client_<sys_id>^ORDERBYDESCsys_created_on
  fields: sys_id,name,action,sys_created_on
  limit: 1

ツールの使用

ツール目的使用する場面
SN-Query-Tableパターンを確認するために既存の UI コンポーネントを検索するパターンの発見、検証
SN-Create-Recordクライアントスクリプト、ウィジェット、UI ページを作成する新しい UI コンポーネントの構築
SN-Update-Recordスクリプト、テンプレート、設定を変更する生成したコンポーネントの反復的な改善
SN-Get-Table-Schemaデータバインディング用のフィールド名を確認するフォームフィールドとデータモデルの把握

ベストプラクティス

  1. ui_type は常に正しく設定してください -- デスクトップには 0、モバイル/サービスポータルには 1、両方には 2 を使用します
  2. クライアントスクリプトでは、直接 DOM を操作するのではなく、g_form API メソッドを使用してください
  3. クライアントスクリプトでのサーバー呼び出しは最小限にしてください -- 非同期のサーバー検索には GlideAjax を使用します
  4. クライアントスクリプトのスコープを適切に設定してください -- IIFE を使用してグローバルスコープの汚染を避けます
  5. 複数のブラウザーでテストしてください -- 特定のブラウザーに依存する JavaScript 機能は避けます
  6. 空のフィールド値に対応するため、生成するすべての onChange ハンドラーに null チェックを含めてください
  7. フォームの読み込み中に実行されないよう、onChange スクリプトで isLoading ガードを使用してください
  8. 二重送信を防ぐため、サーバー呼び出し中は ウィジェットを読み取り専用にしてください
  9. アクセシビリティ基準に従ってください -- カスタムウィジェットには ARIA ラベルとキーボード操作を含めます
  10. バージョン管理 -- 作成前に、UI コンポーネントを必ず更新セットに関連付けてください

トラブルシューティング

問題原因解決方法
クライアントスクリプトが実行されないtype または field_name の値が正しくないtype がイベントと一致し、field_name がカラム名と一致していることを確認します
g_form が未定義というエラーフォームのコンテキスト外でスクリプトが実行されているスクリプトが正しいテーブルと ui_type に紐付いていることを確認します
ウィジェットのデータが読み込まれないサーバースクリプトの GlideRecord クエリエラーサーバースクリプトのテーブル権限とクエリ構文を確認します
UI ページが空白になるXML の Jelly 構文エラーXML 構造を検証し、エスケープされていない文字がないか確認します
イベントハンドラーが応答しないコンポーネント ID の不一致イベント設定のコンポーネント sys_id を確認します
フォームフィールドが非表示にならないフィールド名とラベルの不一致ラベルではなくカラム名(例:u_vpn_type)を使用します

例

例 1:自然言語からクライアントスクリプトを生成

入力:「インシデントフォームでカテゴリとして『Hardware』が選択されたとき、モデルとシリアル番号のフィールドを必須にし、入力を促すメッセージを表示してください。」

生成結果: incident.category の onChange クライアントスクリプト。newValue == 'hardware' の場合に、u_model と u_serial_number に対して g_form.setMandatory() と g_form.showFieldMsg() を呼び出します。

例 2:自然言語から Service Portal ウィジェットを生成

入力:「ログイン中のユーザーの未完了リクエストを、完了率を示すステータスバー付きで表示するウィジェットを作成してください。」

生成結果: 現在のユーザーを対象に sc_req_item を検索するサーバースクリプト、Bootstrap のプログレスバーで描画するクライアント側の AngularJS、クリックして移動する機能を備えた Service Portal ウィジェットです。

例 3:自然言語から UI Builder ページを生成

入力:「優先度別のインシデントを示すグラフと、その下にチームの未完了インシデント一覧を表示するワークスペースページを作成してください。」

生成結果: 2 つのデータリソース(グラフ用の集計とテーブル用の一覧)を含む UI Builder ページの登録、優先度の集計に紐付けられた棒グラフコンポーネント、行クリックで移動できるデータテーブルコンポーネントです。

関連スキル

  • development/client-scripts - クライアントスクリプト開発パターンの詳細
  • development/ui-actions - UI アクションのボタンとリンク
  • catalog/ui-policies - カタログ固有の UI ポリシー
  • development/scripted-rest-apis - ウィジェットデータ用のバックエンド API
  • genai/flow-generation - UI によってトリガーされるプロセス向けの Flow Designer 自動化

コメント