mcpField
P
problem-management

problem-management

github.com/serac-labsクラウド・DevOps7762 回インストール9 週間前に更新
リポジトリ

problem-management について

ServiceNow の開発者や運用担当者向けの Problem Management スキルです。問題のライフサイクル、根本原因分析、既知エラー、解決手順を ES5 のコード例とともに扱います。

主な機能

  1. 01問題ライフサイクルの管理
  2. 02インシデントから問題を作成
  3. 03根本原因の分析結果を記録
  4. 04既知エラーと回避策の管理
  5. 05問題の照会とスクリプトのテスト

活用シーン

  1. 01関連インシデントを問題として集約
  2. 02繰り返すインシデントの原因を分析
  3. 03既知エラーを記録して後から検索
  4. 04問題管理の自動化スクリプトをテスト

MCP 対応

README には、問題と既知エラーの照会、開発成果物の検索、スクリプトのテスト、ビジネスルールの作成に使う MCP ツールが記載されています。

インストール

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

$npx skills add https://github.com/serac-labs/serac --skill problem-management

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

詳細

ServiceNow の問題管理

問題管理では、インシデントの根本原因を特定し、恒久的な解決策を実施します。

問題のライフサイクル

New (1)
    ↓
Assess (2)
    ↓
Root Cause Analysis (3)
    ↓
Fix in Progress (4) ← Known Error Database
    ↓
Resolved (5)
    ↓
Closed (6)

Cancelled (7) ← Can occur from any state

主要なテーブル

テーブル用途
problem問題レコード
problem_task問題の調査タスク
known_error既知エラーデータベース(KEDB)
incident関連するインシデント

問題の作成(ES5)

インシデントから問題を作成
// Create problem from multiple incidents (ES5 ONLY!)
function createProblemFromIncidents(incidentSysIds, problemData) {
  // Create problem
  var problem = new GlideRecord("problem")
  problem.initialize()

  problem.setValue("short_description", problemData.title)
  problem.setValue("description", problemData.description)
  problem.setValue("category", problemData.category)
  problem.setValue("subcategory", problemData.subcategory)

  // Set impact based on related incidents
  var highestImpact = 3
  for (var i = 0; i < incidentSysIds.length; i++) {
    var incident = new GlideRecord("incident")
    if (incident.get(incidentSysIds[i])) {
      var incImpact = parseInt(incident.getValue("impact"), 10)
      if (incImpact < highestImpact) {
        highestImpact = incImpact
      }
    }
  }
  problem.setValue("impact", highestImpact)
  problem.setValue("urgency", highestImpact)

  // Assignment
  if (problemData.assignmentGroup) {
    problem.setValue("assignment_group", problemData.assignmentGroup)
  }

  // Affected CI
  if (problemData.cmdb_ci) {
    problem.setValue("cmdb_ci", problemData.cmdb_ci)
  }

  var problemSysId = problem.insert()

  // Link incidents to problem
  for (var j = 0; j < incidentSysIds.length; j++) {
    var incidentToLink = new GlideRecord("incident")
    if (incidentToLink.get(incidentSysIds[j])) {
      incidentToLink.setValue("problem_id", problemSysId)
      incidentToLink.work_notes = "Linked to Problem: " + problem.getValue("number")
      incidentToLink.update()
    }
  }

  return {
    sys_id: problemSysId,
    number: problem.getValue("number"),
  }
}
予防的な問題の作成
// Identify patterns for proactive problems (ES5 ONLY!)
function identifyProactiveProblems() {
  var LOG_PREFIX = "[ProactiveProblem] "
  var threshold = 5 // Minimum incidents to trigger

  // Find recurring incident patterns
  var ga = new GlideAggregate("incident")
  ga.addQuery("opened_at", ">=", gs.daysAgo(30))
  ga.addQuery("active", false) // Only closed incidents
  ga.addNotNullQuery("cmdb_ci")
  ga.addAggregate("COUNT")
  ga.groupBy("cmdb_ci")
  ga.groupBy("category")
  ga.groupBy("subcategory")
  ga.addHaving("COUNT", ">", threshold)
  ga.orderByAggregate("COUNT", "DESC")
  ga.query()

  var patterns = []

  while (ga.next()) {
    var count = parseInt(ga.getAggregate("COUNT"), 10)
    var ci = ga.getValue("cmdb_ci")
    var category = ga.getValue("category")
    var subcategory = ga.getValue("subcategory")

    // Check if problem already exists
    if (!problemExistsForPattern(ci, category, subcategory)) {
      patterns.push({
        cmdb_ci: ci,
        ci_name: ga.cmdb_ci.getDisplayValue(),
        category: category,
        subcategory: subcategory,
        incident_count: count,
      })

      gs.info(
        LOG_PREFIX +
          "Pattern found: " +
          count +
          " incidents for CI " +
          ga.cmdb_ci.getDisplayValue() +
          " (" +
          category +
          "/" +
          subcategory +
          ")",
      )
    }
  }

  return patterns
}

function problemExistsForPattern(ci, category, subcategory) {
  var problem = new GlideRecord("problem")
  problem.addQuery("cmdb_ci", ci)
  problem.addQuery("category", category)
  problem.addQuery("subcategory", subcategory)
  problem.addQuery("state", "NOT IN", "5,6,7") // Not resolved/closed/cancelled
  problem.query()
  return problem.hasNext()
}

根本原因分析(ES5)

RCA ワークフロー
// Start RCA process (ES5 ONLY!)
function startRootCauseAnalysis(problemSysId) {
  var problem = new GlideRecord("problem")
  if (!problem.get(problemSysId)) {
    return false
  }

  // Move to RCA state
  problem.setValue("state", 3) // Root Cause Analysis
  problem.update()

  // Create RCA tasks
  var tasks = [
    { title: "Gather incident data", order: 100 },
    { title: "Interview stakeholders", order: 200 },
    { title: "Review system logs", order: 300 },
    { title: "Identify contributing factors", order: 400 },
    { title: "Document root cause", order: 500 },
  ]

  for (var i = 0; i < tasks.length; i++) {
    createProblemTask(problemSysId, tasks[i])
  }

  return true
}

function createProblemTask(problemSysId, taskData) {
  var task = new GlideRecord("problem_task")
  task.initialize()
  task.setValue("problem", problemSysId)
  task.setValue("short_description", taskData.title)
  task.setValue("order", taskData.order)
  task.setValue("state", 1) // New
  return task.insert()
}
根本原因を記録
// Document RCA findings (ES5 ONLY!)
function documentRootCause(problemSysId, rcaData) {
  var problem = new GlideRecord("problem")
  if (!problem.get(problemSysId)) {
    return false
  }

  // Set root cause fields
  problem.setValue("cause_notes", rcaData.rootCause)
  problem.setValue("u_contributing_factors", rcaData.contributingFactors)

  // 5 Whys analysis
  if (rcaData.fiveWhys) {
    problem.setValue("u_five_whys", JSON.stringify(rcaData.fiveWhys))
  }

  // Set root cause category
  problem.setValue("u_root_cause_category", rcaData.category)

  // Document fix
  problem.setValue("fix_notes", rcaData.proposedFix)

  // Move to Fix in Progress if ready
  if (rcaData.readyToFix) {
    problem.setValue("state", 4) // Fix in Progress
  }

  problem.update()

  // Add to work notes
  problem.work_notes =
    "Root Cause Documented:\n" +
    "-------------------\n" +
    rcaData.rootCause +
    "\n\n" +
    "Proposed Fix:\n" +
    "-------------\n" +
    rcaData.proposedFix
  problem.update()

  return true
}

既知エラーデータベース(ES5)

既知エラーを作成
// Create Known Error from Problem (ES5 ONLY!)
function createKnownError(problemSysId, workaroundInfo) {
  var problem = new GlideRecord("problem")
  if (!problem.get(problemSysId)) {
    return null
  }

  // Create Known Error
  var ke = new GlideRecord("known_error")
  ke.initialize()

  // Copy from problem
  ke.setValue("short_description", problem.getValue("short_description"))
  ke.setValue("description", problem.getValue("description"))
  ke.setValue("cause_notes", problem.getValue("cause_notes"))
  ke.setValue("cmdb_ci", problem.getValue("cmdb_ci"))
  ke.setValue("category", problem.getValue("category"))
  ke.setValue("subcategory", problem.getValue("subcategory"))

  // Workaround
  ke.setValue("workaround", workaroundInfo.description)
  ke.setValue("u_workaround_effectiveness", workaroundInfo.effectiveness)

  // Link to problem
  ke.setValue("problem", problemSysId)

  // Set state
  ke.setValue("state", "published")

  var keSysId = ke.insert()

  // Update problem with known error link
  problem.setValue("known_error", keSysId)
  problem.setValue("known_error_state", "accepted")
  problem.update()

  return {
    sys_id: keSysId,
    number: ke.getValue("number"),
  }
}
既知エラーを検索
// Search KEDB for matching workarounds (ES5 ONLY!)
var KEDBSearch = Class.create()
KEDBSearch.prototype = {
  initialize: function () {},

  /**
   * Find matching known errors for an incident
   */
  findForIncident: function (incidentSysId) {
    var incident = new GlideRecord("incident")
    if (!incident.get(incidentSysId)) {
      return []
    }

    var matches = []

    // Search by CI
    if (incident.cmdb_ci) {
      var ciMatches = this._searchByCI(incident.getValue("cmdb_ci"))
      matches = matches.concat(ciMatches)
    }

    // Search by category
    var catMatches = this._searchByCategory(incident.getValue("category"), incident.getValue("subcategory"))
    matches = matches.concat(catMatches)

    // Search by keywords
    var keywordMatches = this._searchByKeywords(incident.getValue("short_description"))
    matches = matches.concat(keywordMatches)

    // Deduplicate
    return this._deduplicate(matches)
  },

  _searchByCI: function (ciSysId) {
    var results = []
    var ke = new GlideRecord("known_error")
    ke.addQuery("cmdb_ci", ciSysId)
    ke.addQuery("state", "published")
    ke.query()

    while (ke.next()) {
      results.push(this._toObject(ke, "CI Match"))
    }
    return results
  },

  _searchByCategory: function (category, subcategory) {
    var results = []
    var ke = new GlideRecord("known_error")
    ke.addQuery("category", category)
    if (subcategory) {
      ke.addQuery("subcategory", subcategory)
    }
    ke.addQuery("state", "published")
    ke.query()

    while (ke.next()) {
      results.push(this._toObject(ke, "Category Match"))
    }
    return results
  },

  _searchByKeywords: function (description) {
    var results = []
    var keywords = description.split(" ").filter(function (w) {
      return w.length > 3
    })

    var ke = new GlideRecord("known_error")
    ke.addQuery("state", "published")

    var qc = null
    for (var i = 0; i < keywords.length && i < 5; i++) {
      if (qc === null) {
        qc = ke.addQuery("short_description", "CONTAINS", keywords[i])
      } else {
        qc.addOrCondition("short_description", "CONTAINS", keywords[i])
      }
    }
    ke.query()

    while (ke.next()) {
      results.push(this._toObject(ke, "Keyword Match"))
    }
    return results
  },

  _toObject: function (gr, matchType) {
    return {
      sys_id: gr.getUniqueValue(),
      number: gr.getValue("number"),
      title: gr.getValue("short_description"),
      workaround: gr.getValue("workaround"),
      matchType: matchType,
    }
  },

  _deduplicate: function (matches) {
    var seen = {}
    var unique = []
    for (var i = 0; i < matches.length; i++) {
      if (!seen[matches[i].sys_id]) {
        seen[matches[i].sys_id] = true
        unique.push(matches[i])
      }
    }
    return unique
  },

  type: "KEDBSearch",
}

問題の解決(ES5)

問題を解決
// Resolve problem with fix (ES5 ONLY!)
function resolveProblem(problemSysId, resolutionData) {
  var problem = new GlideRecord("problem")
  if (!problem.get(problemSysId)) {
    return { success: false, message: "Problem not found" }
  }

  // Validate
  if (!resolutionData.fixNotes) {
    return { success: false, message: "Fix notes required" }
  }

  // Update problem
  problem.setValue("state", 5) // Resolved
  problem.setValue("fix_notes", resolutionData.fixNotes)
  problem.setValue("resolution_code", resolutionData.resolutionCode)
  problem.setValue("resolved_at", new GlideDateTime())
  problem.setValue("resolved_by", gs.getUserID())

  // Link to change if permanent fix deployed
  if (resolutionData.changeRequest) {
    problem.setValue("rfc", resolutionData.changeRequest)
  }

  problem.update()

  // Update related Known Error if exists
  if (problem.known_error) {
    var ke = new GlideRecord("known_error")
    if (ke.get(problem.getValue("known_error"))) {
      ke.setValue("state", "closed")
      ke.setValue("u_permanent_fix", resolutionData.fixNotes)
      ke.update()
    }
  }

  // Notify incident owners
  notifyLinkedIncidents(problemSysId, "Problem resolved: " + problem.getValue("number"))

  return {
    success: true,
    number: problem.getValue("number"),
  }
}

function notifyLinkedIncidents(problemSysId, message) {
  var incident = new GlideRecord("incident")
  incident.addQuery("problem_id", problemSysId)
  incident.addQuery("active", true)
  incident.query()

  while (incident.next()) {
    incident.work_notes = message
    incident.update()
  }
}

MCP ツールの統合

利用可能なツール
ツール用途
snow_query_table問題と既知エラーをクエリする
snow_artifact_manage (action=find)関連する開発アーティファクトを検索する
snow_execute_script問題のスクリプトをテストする
snow_create_business_rule問題の自動化を作成する
ワークフローの例
// 1. Find open problems
await snow_query_table({
  table: "problem",
  query: "active=true",
  fields: "number,short_description,state,assignment_group",
})

// 2. Search KEDB
await snow_query_table({
  table: "known_error",
  query: "state=published^short_descriptionLIKEemail",
  fields: "number,short_description,workaround",
})

// 3. Find recurring incidents for proactive problems
await snow_execute_script({
  script: `
        var patterns = identifyProactiveProblems();
        gs.info('Patterns found: ' + patterns.length);
    `,
})

ベストプラクティス

  1. インシデントのリンク - 関連するインシデントをすべてリンクします
  2. 根本原因に注目 - 症状ではなく、実際の原因を特定します
  3. 5 Whys 手法 - 根本原因まで掘り下げます
  4. 既知エラー - 回避策を記録します
  5. 恒久的な修正 - 変更要求にリンクします
  6. メトリクス - 問題の解決時間を追跡します
  7. 予防的な対応 - エスカレーションの前にパターンを特定します
  8. ES5 のみ - 最新の JavaScript 構文は使用しません

コメント