mcpField
A
agent-workspace

agent-workspace

github.com/serac-labsデザイン・UI7757 回インストール9 週間前に更新
リポジトリ

agent-workspace について

ServiceNow のフルフィラー向けに Agent Workspace を設定するスキルです。ワークスペースのレイアウト、コンテキストサイドパネル、Agent Assist、UI アクションを ES5 の例と MCP ツールの手順で扱います。

主な機能

  1. 01ワークスペースのリストとフォーム設定
  2. 02コンテキストサイドパネルの設定
  3. 03Agent Assist の設定
  4. 04ワークスペース UI アクションの作成
  5. 05MCP ツールによるウィジェットの検索とデプロイ

活用シーン

  1. 01IT サービスデスク用ワークスペースの構築
  2. 02レコードと並べてインシデント情報を表示
  3. 03エージェント向けの類似インシデント検索

MCP 対応

snow_query_table、snow_artifact_manage、snow_execute_script などの MCP ツールを使い、ワークスペース要素の確認、デプロイ、テストを行う手順を説明します。

インストール

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

$npx skills add https://github.com/serac-labs/serac --skill agent-workspace

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

詳細

ServiceNow 向けエージェントワークスペース

エージェントワークスペースは、フルフィラーの生産性を高める、最新のカスタマイズ可能なインターフェイスを提供します。

ワークスペースのアーキテクチャ

Workspace (sys_aw_workspace)
    ├── Lists (sys_aw_list)
    ├── Forms (sys_aw_form)
    ├── Related Lists
    ├── UI Actions
    └── Contextual Side Panel
        ├── Agent Assist
        ├── Related Records
        └── Activity Stream

主なテーブル

テーブル用途
sys_aw_workspaceワークスペースの定義
sys_aw_listリストの構成
sys_aw_formフォームの構成
sys_aw_related_list関連リストの構成
sys_aw_actionワークスペースの UI アクション

ワークスペースの構成(ES5)

ワークスペースの作成
// Create workspace (ES5 ONLY!)
var workspace = new GlideRecord("sys_aw_workspace")
workspace.initialize()

// Basic info
workspace.setValue("name", "IT Service Desk Workspace")
workspace.setValue("title", "IT Service Desk")
workspace.setValue("description", "Workspace for IT service desk agents")

// Primary table
workspace.setValue("primary_table", "incident")

// URL path
workspace.setValue("url", "it-service-desk")

// Icon and branding
workspace.setValue("icon", "support")
workspace.setValue("color", "#0056B3")

// Default list
workspace.setValue("default_list", getListConfig("incident_active"))

// Enable features
workspace.setValue("agent_assist_enabled", true)
workspace.setValue("contextual_side_panel_enabled", true)
workspace.setValue("activity_stream_enabled", true)

workspace.insert()
ワークスペースのリスト構成
// Create list configuration (ES5 ONLY!)
var list = new GlideRecord("sys_aw_list")
list.initialize()

list.setValue("name", "My Active Incidents")
list.setValue("table", "incident")
list.setValue("workspace", workspaceSysId)

// Filter
list.setValue("filter", "active=true^assigned_to=javascript:gs.getUserID()")

// Columns
list.setValue("columns", "number,short_description,priority,state,caller_id,opened_at")

// Sort
list.setValue("order_by", "priority")
list.setValue("order_by_desc", false)

// Row actions
list.setValue("show_row_actions", true)

// Grouping (optional)
list.setValue("group_by", "priority")

list.insert()
ワークスペースのフォーム構成
// Create form configuration (ES5 ONLY!)
var form = new GlideRecord("sys_aw_form")
form.initialize()

form.setValue("name", "Incident Form")
form.setValue("table", "incident")
form.setValue("workspace", workspaceSysId)

// Form sections
var sections = [
  {
    name: "Details",
    columns: 2,
    fields: ["number", "state", "caller_id", "opened_at", "short_description", "priority"],
  },
  {
    name: "Assignment",
    columns: 2,
    fields: ["assignment_group", "assigned_to", "escalation"],
  },
  {
    name: "Resolution",
    columns: 1,
    fields: ["resolution_code", "close_notes"],
    condition: "state=6^ORstate=7", // Only show for resolved/closed
  },
]

form.setValue("sections", JSON.stringify(sections))

// Related lists
form.setValue("related_lists", "incident.task_sla,incident.sys_attachment")

// Enable Agent Assist
form.setValue("agent_assist_enabled", true)

form.insert()

コンテキストサイドパネル(ES5)

サイドパネルの構成
// Side panel configuration (ES5 ONLY!)
var panel = new GlideRecord("sys_aw_contextual_side_panel")
panel.initialize()

panel.setValue("workspace", workspaceSysId)
panel.setValue("table", "incident")
panel.setValue("name", "Incident Context")

// Tabs
var tabs = [
  {
    id: "agent_assist",
    label: "Agent Assist",
    icon: "lightbulb-outline",
    component: "agent-assist",
  },
  {
    id: "caller_info",
    label: "Caller Info",
    icon: "user",
    component: "custom-caller-info",
  },
  {
    id: "related",
    label: "Related Records",
    icon: "link",
    component: "related-records",
  },
  {
    id: "activity",
    label: "Activity",
    icon: "history",
    component: "activity-stream",
  },
]

panel.setValue("tabs", JSON.stringify(tabs))
panel.setValue("default_tab", "agent_assist")

panel.insert()
カスタムパネルコンポーネント(ES5)
// Widget for side panel (ES5 ONLY!)
// Server Script
;(function () {
  // Get current record from context
  var recordSysId = input.sys_id
  var tableName = input.table

  if (tableName === "incident" && recordSysId) {
    var gr = new GlideRecord("incident")
    if (gr.get(recordSysId)) {
      // Get caller information
      data.caller = {
        name: gr.caller_id.getDisplayValue(),
        email: gr.caller_id.email.getDisplayValue(),
        phone: gr.caller_id.phone.getDisplayValue(),
        location: gr.caller_id.location.getDisplayValue(),
        vip: gr.caller_id.vip.getDisplayValue() === "true",
      }

      // Get caller's open incidents
      data.openIncidents = []
      var incidents = new GlideRecord("incident")
      incidents.addQuery("caller_id", gr.getValue("caller_id"))
      incidents.addQuery("active", true)
      incidents.addQuery("sys_id", "!=", recordSysId)
      incidents.orderByDesc("opened_at")
      incidents.setLimit(5)
      incidents.query()

      while (incidents.next()) {
        data.openIncidents.push({
          sys_id: incidents.getUniqueValue(),
          number: incidents.getValue("number"),
          short_description: incidents.getValue("short_description"),
          state: incidents.state.getDisplayValue(),
        })
      }
    }
  }
})()

エージェントアシスト(ES5)

エージェントアシストの構成
// Agent Assist configuration (ES5 ONLY!)
var config = new GlideRecord("sys_aw_agent_assist_config")
config.initialize()

config.setValue("workspace", workspaceSysId)
config.setValue("table", "incident")
config.setValue("name", "Incident Agent Assist")
config.setValue("active", true)

// Recommendations sources
config.setValue("show_knowledge", true)
config.setValue("show_similar_incidents", true)
config.setValue("show_solutions", true)
config.setValue("show_macros", true)

// Knowledge search configuration
config.setValue("knowledge_bases", kbSysIds) // Comma-separated
config.setValue("knowledge_search_fields", "short_description,description")

config.insert()
類似レコードのスクリプト(ES5)
// Find similar incidents for Agent Assist (ES5 ONLY!)
var SimilarIncidentFinder = Class.create()
SimilarIncidentFinder.prototype = {
  initialize: function () {},

  /**
   * Find similar resolved incidents
   */
  findSimilar: function (incidentSysId) {
    var current = new GlideRecord("incident")
    if (!current.get(incidentSysId)) {
      return []
    }

    var similar = []
    var keywords = this._extractKeywords(current.getValue("short_description"))

    // Search resolved incidents
    var gr = new GlideRecord("incident")
    gr.addQuery("state", "IN", "6,7") // Resolved or Closed
    gr.addQuery("sys_id", "!=", incidentSysId)

    // Match by category
    if (current.category) {
      gr.addQuery("category", current.getValue("category"))
    }

    // Match by CI
    if (current.cmdb_ci) {
      gr.addOrCondition("cmdb_ci", current.getValue("cmdb_ci"))
    }

    // Keyword matching
    for (var i = 0; i < keywords.length && i < 3; i++) {
      gr.addOrCondition("short_description", "CONTAINS", keywords[i])
    }

    gr.setLimit(10)
    gr.orderByDesc("resolved_at")
    gr.query()

    while (gr.next()) {
      var score = this._calculateSimilarity(current, gr)
      if (score > 0.3) {
        similar.push({
          sys_id: gr.getUniqueValue(),
          number: gr.getValue("number"),
          short_description: gr.getValue("short_description"),
          resolution_code: gr.resolution_code.getDisplayValue(),
          close_notes: gr.getValue("close_notes"),
          score: Math.round(score * 100),
        })
      }
    }

    // Sort by similarity score
    similar.sort(function (a, b) {
      return b.score - a.score
    })

    return similar.slice(0, 5)
  },

  _extractKeywords: function (text) {
    var stopWords = ["the", "is", "at", "which", "on", "a", "an", "and", "or", "not", "to", "for"]
    var words = text.toLowerCase().split(/\s+/)
    var keywords = []

    for (var i = 0; i < words.length; i++) {
      var word = words[i].replace(/[^a-z0-9]/g, "")
      if (word.length > 3 && stopWords.indexOf(word) === -1) {
        keywords.push(word)
      }
    }

    return keywords
  },

  _calculateSimilarity: function (source, target) {
    var score = 0

    // Category match
    if (source.getValue("category") === target.getValue("category")) {
      score += 0.3
    }

    // Subcategory match
    if (source.getValue("subcategory") === target.getValue("subcategory")) {
      score += 0.2
    }

    // CI match
    if (source.getValue("cmdb_ci") === target.getValue("cmdb_ci")) {
      score += 0.3
    }

    // Keyword overlap
    var sourceKeywords = this._extractKeywords(source.getValue("short_description"))
    var targetKeywords = this._extractKeywords(target.getValue("short_description"))
    var overlap = 0

    for (var i = 0; i < sourceKeywords.length; i++) {
      if (targetKeywords.indexOf(sourceKeywords[i]) !== -1) {
        overlap++
      }
    }

    if (sourceKeywords.length > 0) {
      score += 0.2 * (overlap / sourceKeywords.length)
    }

    return score
  },

  type: "SimilarIncidentFinder",
}

ワークスペースの UI アクション(ES5)

ワークスペースアクションの作成
// Create workspace-specific UI action (ES5 ONLY!)
var action = new GlideRecord("sys_aw_action")
action.initialize()

action.setValue("name", "Quick Resolve")
action.setValue("label", "Quick Resolve")
action.setValue("workspace", workspaceSysId)
action.setValue("table", "incident")

// Action type
action.setValue("action_type", "form") // form, list, both
action.setValue("order", 100)

// Condition
action.setValue("condition", "current.active == true && current.state != 6")

// Client action (opens modal)
action.setValue(
  "client_script",
  "function onClick() {\n" +
    "    spModal.open({\n" +
    '        title: "Quick Resolve",\n' +
    '        widget: "quick-resolve-modal",\n' +
    "        widgetInput: { table: g_form.getTableName(), sys_id: g_form.getUniqueValue() }\n" +
    "    }).then(function(result) {\n" +
    "        if (result) {\n" +
    '            g_form.setValue("state", 6);\n' +
    '            g_form.setValue("resolution_code", result.code);\n' +
    '            g_form.setValue("close_notes", result.notes);\n' +
    "            g_form.save();\n" +
    "        }\n" +
    "    });\n" +
    "}",
)

// Icon and style
action.setValue("icon", "check-circle")
action.setValue("button_class", "btn-success")

action.insert()

MCP ツールの統合

利用可能なツール
ツール用途
snow_artifact_manageワークスペースウィジェットの検索(action: "find")
snow_query_tableワークスペーステーブルのクエリ
snow_artifact_manageワークスペースウィジェットのデプロイ(action: "create")
snow_execute_scriptワークスペーススクリプトのテスト
ワークフローの例
// 1. Find workspaces
await snow_query_table({
  table: "sys_aw_workspace",
  query: "active=true",
  fields: "name,title,primary_table,url",
})

// 2. Get list configurations
await snow_query_table({
  table: "sys_aw_list",
  query: "workspace.name=IT Service Desk Workspace",
  fields: "name,table,filter,columns",
})

// 3. Test similar incident finder
await snow_execute_script({
  script: `
        var finder = new SimilarIncidentFinder();
        var similar = finder.findSimilar('incident_sys_id');
        gs.info('Found: ' + similar.length);
    `,
})

ベストプラクティス

  1. ロールベース - 特定のロール向けに設計します
  2. 効率的なリスト - フィルターと列を最適化します
  3. コンテキストパネル - 関連情報にアクセスできるようにします
  4. エージェントアシスト - ナレッジ/類似レコードを有効にします
  5. アクション - よく使うタスクを効率化します
  6. パフォーマンス - コンポーネントを遅延読み込みします
  7. モバイル対応 - レスポンシブレイアウトをテストします
  8. ES5 のみ - 最新の JavaScript 構文は使用しません

コメント