mcpField
I
issue-summarization

issue-summarization

github.com/happy-technologies-llcセキュリティ4141 回インストール2 日前に更新
リポジトリ

issue-summarization について

ServiceNow GRC の課題を要約するスキルで、コンプライアンス担当者、監査担当者、経営層向けの報告に利用できます。課題をリスク、統制、コンプライアンス上のギャップと関連付け、詳細や傾向指標、監査委員会向けの要約を作成します。

主な機能

  1. 01課題と関連リスク・統制を集約
  2. 02事業影響とコンプライアンス上のギャップを分析
  3. 03経営層向け要約と傾向指標を作成
  4. 04規制条項に関連した説明文を作成

活用シーン

  1. 01監査委員会や取締役会向け資料を準備
  2. 02四半期または年次のコンプライアンスレビュー
  3. 03内部・外部監査の指摘事項を要約

MCP 対応

MCP ツールで ServiceNow GRC の課題、リスク、統制を照会し、バックグラウンドスクリプトで指標を集計してレポート内容を作成します。

インストール

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

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

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

詳細

GRC 問題の要約

概要

このスキルは、ServiceNow のガバナンス、リスク、コンプライアンス(GRC)問題の状況に応じた要約を生成します。以下を対象とします。

  • 関連するリスク、統制、コンプライアンス上のギャップとともに GRC 問題データを集約する
  • 問題をプロファイルやポリシー違反と関連付けて、ビジネスへの影響を評価する
  • 監査委員会や取締役会への報告に適した、経営層向けの要約を生成する
  • 問題の件数、経過期間、解決率を示す傾向分析を作成する
  • 規制上の引用に関連付けたコンプライアンスギャップの説明を作成する

使用する場面:

  • 経営層向けの説明資料や監査委員会の報告書を準備するとき
  • 関係者が GRC の状況をまとめて把握する必要があるとき
  • 四半期または年次のコンプライアンスレビュー中
  • 内部監査または外部監査の指摘事項を要約するとき
  • 問題の状況概要が求められるリスク委員会の会議前

前提条件

  • ロール: sn_grc.viewer、sn_grc.manager、sn_compliance.manager、または admin
  • プラグイン: com.sn_grc、com.sn_compliance、com.sn_risk
  • アクセス権: sn_grc_issue、sn_grc_risk、sn_compliance_control、sn_grc_profile テーブルへの読み取りアクセス権
  • 知識: 組織のコンプライアンスフレームワークと報告要件に関する理解

主な GRC テーブル

テーブル目的主なフィールド
sn_grc_issueGRC の問題と指摘事項number、short_description、state、priority、risk_rating、profile、item、category、source
sn_grc_riskプロファイルに関連付けられたリスクレコードnumber、short_description、risk_score、state、treatment、residual_risk、inherent_risk
sn_compliance_controlコンプライアンス統制number、short_description、state、control_objective、owner、effectiveness、test_result
sn_compliance_policyコンプライアンスポリシーnumber、short_description、state、owner、policy_statement、effective_date
sn_grc_profileGRC エンティティプロファイルnumber、short_description、profile_type、applies_to
sn_compliance_citation規制上の引用number、short_description、reference、regulation、authority

手順

Step 1: 要約対象の GRC 問題を取得する

対象範囲(すべての未解決の問題、特定のカテゴリ、または期間)で GRC 問題を絞り込んでクエリします。

MCP を使用(Claude Code/Desktop):

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: active=true^ORDERBYDESCpriority
  fields: sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source,sys_created_on,sys_updated_on,closed_at
  limit: 100

REST API を使用:

GET /api/now/table/sn_grc_issue?sysparm_query=active=true^ORDERBYDESCpriority&sysparm_fields=sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source&sysparm_limit=100
Step 2: 関連するリスクの背景情報を収集する

各問題について、関連するリスクレコードを取得し、リスク状況を把握します。

MCP を使用:

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_risk
  query: profile=[issue_profile_sys_id]^active=true
  fields: sys_id,number,short_description,risk_score,state,treatment,residual_risk,inherent_risk,category,owner
  limit: 50

REST API を使用:

GET /api/now/table/sn_grc_risk?sysparm_query=profile=[profile_sys_id]^active=true&sysparm_fields=sys_id,number,short_description,risk_score,state,treatment,residual_risk,inherent_risk,category&sysparm_limit=50
Step 3: コンプライアンスギャップと統制の不備を特定する

問題のプロファイルに関連付けられた、コンプライアンスに準拠していない状態の統制をクエリします。

MCP を使用:

Tool: SN-Query-Table
Parameters:
  table_name: sn_compliance_control
  query: profile=[issue_profile_sys_id]^stateNOT INcompliant,passed^active=true
  fields: sys_id,number,short_description,state,control_objective,owner,effectiveness,test_result,test_date
  limit: 50

REST API を使用:

GET /api/now/table/sn_compliance_control?sysparm_query=profile=[profile_sys_id]^stateNOT INcompliant,passed^active=true&sysparm_fields=sys_id,number,short_description,state,control_objective,owner,effectiveness,test_result&sysparm_limit=50
Step 4: 集計指標を含む経営層向け要約を生成する

統計と傾向を含む包括的な要約を作成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var summary = {
      generated_date: new GlideDateTime().toString(),
      issue_overview: { total_open: 0, critical: 0, high: 0, medium: 0, low: 0, overdue: 0 },
      by_category: {},
      by_source: {},
      risk_context: { avg_risk_score: 0, max_risk_score: 0, total_risks: 0 },
      compliance_gaps: { failing_controls: 0, untested_controls: 0 },
      aging: { under_30_days: 0, days_30_to_60: 0, days_60_to_90: 0, over_90_days: 0 },
      resolution_rate: { closed_last_30: 0, opened_last_30: 0 }
    };

    var now = new GlideDateTime();
    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('active', true);
    gr.query();

    var riskScores = [];
    while (gr.next()) {
      summary.issue_overview.total_open++;

      // Priority distribution
      var pri = gr.priority.toString();
      if (pri == '1') summary.issue_overview.critical++;
      else if (pri == '2') summary.issue_overview.high++;
      else if (pri == '3') summary.issue_overview.medium++;
      else summary.issue_overview.low++;

      // Overdue check
      if (gr.due_date.toString() && new GlideDateTime(gr.due_date.toString()).compareTo(now) < 0) {
        summary.issue_overview.overdue++;
      }

      // Category breakdown
      var cat = gr.category.getDisplayValue() || 'Uncategorized';
      summary.by_category[cat] = (summary.by_category[cat] || 0) + 1;

      // Source breakdown
      var src = gr.source.getDisplayValue() || 'Manual';
      summary.by_source[src] = (summary.by_source[src] || 0) + 1;

      // Aging analysis
      var created = new GlideDateTime(gr.sys_created_on.toString());
      var ageDays = gs.dateDiff(created.toString(), now.toString(), true);
      if (ageDays < 30) summary.aging.under_30_days++;
      else if (ageDays < 60) summary.aging.days_30_to_60++;
      else if (ageDays < 90) summary.aging.days_60_to_90++;
      else summary.aging.over_90_days++;

      // Collect risk scores from related risks
      var risk = new GlideRecord('sn_grc_risk');
      risk.addQuery('profile', gr.profile.toString());
      risk.addQuery('active', true);
      risk.query();
      while (risk.next()) {
        var score = parseInt(risk.risk_score.toString()) || 0;
        riskScores.push(score);
        summary.risk_context.total_risks++;
        if (score > summary.risk_context.max_risk_score) summary.risk_context.max_risk_score = score;
      }
    }

    // Average risk score
    if (riskScores.length > 0) {
      var total = 0;
      for (var i = 0; i < riskScores.length; i++) total += riskScores[i];
      summary.risk_context.avg_risk_score = Math.round(total / riskScores.length);
    }

    // Compliance gap count
    var ctrl = new GlideAggregate('sn_compliance_control');
    ctrl.addQuery('active', true);
    ctrl.addQuery('state', 'NOT IN', 'compliant,passed');
    ctrl.addAggregate('COUNT');
    ctrl.query();
    if (ctrl.next()) summary.compliance_gaps.failing_controls = parseInt(ctrl.getAggregate('COUNT'));

    // Resolution rate (last 30 days)
    var closed = new GlideAggregate('sn_grc_issue');
    closed.addQuery('closed_at', '>=', gs.daysAgo(30));
    closed.addAggregate('COUNT');
    closed.query();
    if (closed.next()) summary.resolution_rate.closed_last_30 = parseInt(closed.getAggregate('COUNT'));

    var opened = new GlideAggregate('sn_grc_issue');
    opened.addQuery('sys_created_on', '>=', gs.daysAgo(30));
    opened.addAggregate('COUNT');
    opened.query();
    if (opened.next()) summary.resolution_rate.opened_last_30 = parseInt(opened.getAggregate('COUNT'));

    gs.info('EXECUTIVE GRC ISSUE SUMMARY:\n' + JSON.stringify(summary, null, 2));
  description: "GRC: Generate executive summary of open issues with metrics"
Step 5: 個別の問題の詳細な要約を生成する

特定の問題について詳細な要約を作成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var issueNumber = 'ISSUE0001234'; // Replace with target issue
    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('number', issueNumber);
    gr.query();

    if (gr.next()) {
      var detail = {
        issue: {
          number: gr.number.toString(),
          title: gr.short_description.toString(),
          description: gr.description.toString(),
          state: gr.state.getDisplayValue(),
          priority: gr.priority.getDisplayValue(),
          risk_rating: gr.risk_rating.toString(),
          category: gr.category.getDisplayValue(),
          source: gr.source.getDisplayValue(),
          assigned_to: gr.assigned_to.getDisplayValue(),
          profile: gr.profile.getDisplayValue(),
          created: gr.sys_created_on.toString(),
          due_date: gr.due_date.toString()
        },
        related_risks: [],
        affected_controls: [],
        policy_references: [],
        business_impact: ''
      };

      // Related risks
      var risk = new GlideRecord('sn_grc_risk');
      risk.addQuery('profile', gr.profile.toString());
      risk.addQuery('active', true);
      risk.query();
      while (risk.next()) {
        detail.related_risks.push({
          number: risk.number.toString(),
          title: risk.short_description.toString(),
          inherent_risk: risk.inherent_risk.toString(),
          residual_risk: risk.residual_risk.toString(),
          treatment: risk.treatment.getDisplayValue()
        });
      }

      // Affected controls
      var ctrl = new GlideRecord('sn_compliance_control');
      ctrl.addQuery('profile', gr.profile.toString());
      ctrl.query();
      while (ctrl.next()) {
        detail.affected_controls.push({
          number: ctrl.number.toString(),
          title: ctrl.short_description.toString(),
          state: ctrl.state.getDisplayValue(),
          effectiveness: ctrl.effectiveness.getDisplayValue(),
          owner: ctrl.owner.getDisplayValue()
        });
      }

      // Policy references via profile
      var policy = new GlideRecord('sn_compliance_policy');
      policy.addQuery('active', true);
      policy.query();
      while (policy.next()) {
        detail.policy_references.push({
          number: policy.number.toString(),
          title: policy.short_description.toString(),
          state: policy.state.getDisplayValue()
        });
      }

      // Determine business impact narrative
      var maxRisk = 0;
      for (var i = 0; i < detail.related_risks.length; i++) {
        var rs = parseInt(detail.related_risks[i].residual_risk) || 0;
        if (rs > maxRisk) maxRisk = rs;
      }
      var failingCtrls = detail.affected_controls.filter(function(c) {
        return c.state !== 'Compliant' && c.state !== 'Passed';
      }).length;

      if (maxRisk >= 80 || failingCtrls >= 3) {
        detail.business_impact = 'HIGH - Significant compliance exposure with elevated residual risk and multiple control failures';
      } else if (maxRisk >= 50 || failingCtrls >= 1) {
        detail.business_impact = 'MEDIUM - Moderate compliance concern requiring timely remediation';
      } else {
        detail.business_impact = 'LOW - Minor finding with limited business impact';
      }

      gs.info('ISSUE DETAIL SUMMARY:\n' + JSON.stringify(detail, null, 2));
    }
  description: "GRC: Generate detailed summary for a specific issue"
Step 6: 監査委員会向けの説明文を作成する

エンコードされた全文クエリを使って、テーマ別に問題を検索します:

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: priorityIN1,2^sys_created_on>=javascript:gs.monthsAgoStart(3)^123TEXTQUERY321=data privacy access controls
  limit: 25

書式を整えた説明文を作成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var narrative = [];
    narrative.push('=== GRC ISSUE SUMMARY FOR AUDIT COMMITTEE ===');
    narrative.push('Report Date: ' + new GlideDateTime().getDisplayValue());
    narrative.push('');

    // Open issue statistics
    var agg = new GlideAggregate('sn_grc_issue');
    agg.addQuery('active', true);
    agg.addAggregate('COUNT');
    agg.groupBy('priority');
    agg.query();

    narrative.push('OPEN ISSUES BY PRIORITY:');
    while (agg.next()) {
      narrative.push('  Priority ' + agg.priority.getDisplayValue() + ': ' + agg.getAggregate('COUNT') + ' issues');
    }
    narrative.push('');

    // Overdue issues
    var overdue = new GlideAggregate('sn_grc_issue');
    overdue.addQuery('active', true);
    overdue.addQuery('due_date', '<', new GlideDateTime().toString());
    overdue.addAggregate('COUNT');
    overdue.query();
    if (overdue.next()) {
      narrative.push('OVERDUE ISSUES: ' + overdue.getAggregate('COUNT'));
    }
    narrative.push('');

    // Top 5 critical issues
    narrative.push('TOP CRITICAL ISSUES:');
    var top = new GlideRecord('sn_grc_issue');
    top.addQuery('active', true);
    top.addQuery('priority', 'IN', '1,2');
    top.orderByDesc('risk_rating');
    top.setLimit(5);
    top.query();
    var idx = 1;
    while (top.next()) {
      narrative.push('  ' + idx + '. ' + top.number + ' - ' + top.short_description);
      narrative.push('     Risk Rating: ' + top.risk_rating + ' | Due: ' + top.due_date.getDisplayValue());
      idx++;
    }

    gs.info(narrative.join('\n'));
  description: "GRC: Generate audit committee narrative report"

ツールの使用方法

操作MCP ツールREST エンドポイント
問題のクエリSN-Query-TableGET /api/now/table/sn_grc_issue
問題の検索SN-Natural-Language-SearchN/A
リスクのクエリSN-Query-TableGET /api/now/table/sn_grc_risk
統制のクエリSN-Query-TableGET /api/now/table/sn_compliance_control
集計分析SN-Execute-Background-ScriptPOST /api/now/table/sys_trigger
スキーマの確認SN-Discover-Table-SchemaGET /api/now/table/sys_dictionary

ベストプラクティス

  • 対象者への配慮: 対象者に応じて要約の詳しさを調整します。経営幹部には概要レベルの指標が必要ですが、コンプライアンスチームには統制レベルの詳細が必要です
  • 傾向の把握: 推移を示すため、常に傾向データ(月次比較、四半期比較)を含めます
  • リスクとの関連付け: 根本原因のパターンを示すため、問題を根底にあるリスクと関連付けます
  • 経過期間の可視化: 経営幹部向けの要約では、期限超過の問題や経過期間の区分を目立つように示します
  • 規制要件との対応付け: 可能な場合は、問題を具体的な規制要件や引用箇所に対応付けます
  • 実行につながる表現: 要約には、状況の説明だけでなく、推奨される次のステップも含めます
  • データの鮮度: レポートの生成日時と、データの最新性に関する免責事項を含めます

トラブルシューティング

問題のリスクコンテキストが空

症状: プロファイルで検索しても、問題に関連するリスクが返されません 原因: 問題がGRCプロファイルにリンクされていないか、プロファイルとの関連付けに別のフィールドが使用されている可能性があります 解決策:

Tool: SN-Discover-Table-Schema
Parameters:
  table_name: sn_grc_issue

リスクレコードにリンクする item、content、カスタム参照フィールドなど、別の関連付けフィールドを確認します。

カテゴリフィールドが空を返す

症状: 問題のカテゴリ別内訳で、すべての項目が「未分類」と表示されます 原因: category フィールドに値が入力されていないか、別のフィールド名が使われている可能性があります 解決策: sn_grc_issue の sys_choice テーブルを検索して使用可能なカテゴリ値を確認し、代わりに issue_type などのカスタムフィールドが使われていないか確認します。

集計クエリがタイムアウトする

症状: 大規模なデータセットで GlideAggregate を使用するバックグラウンドスクリプトがタイムアウトします 原因: 適切なインデックスがないまま、大量の問題レコードを処理しています 解決策: 日付範囲フィルターを追加してデータセットを絞り込むか、集計ループ内の詳細クエリで setLimit() を使用します。

例

例1:月次コンプライアンス状況の要約

シナリオ: コンプライアンスマネージャーが、リスク委員会向けの月次概要を必要としています

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: active=true^priorityIN1,2^sys_created_on>=javascript:gs.beginningOfThisMonth()^ORsys_updated_on>=javascript:gs.beginningOfThisMonth()
  limit: 50

生成された要約:

  • 未解決の問題の合計: 47件(先月の42件から増加)
  • 重大/高: 早急な対応が必要な問題が12件
  • 期限超過: 期限を過ぎた問題が8件(期限超過率17%)
  • 最多カテゴリ: アクセス制御(15件、32%)
  • 平均リスクスコア: 関連するリスクレコード全体で67/100
  • 統制の不備: 9件の統制が非準拠の状態
  • 推奨事項: 期限超過の重大な問題3件を経営幹部の責任者にエスカレーションし、第2四半期に統制の再テストを予定します
例2:監査向けの単一問題の詳細分析

シナリオ: 外部監査人が特定の指摘事項に関する詳細な背景情報を求めています

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: number=ISSUE0002341
  fields: sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source,work_notes,sys_created_on
  limit: 1

生成された詳細情報:

  • 問題: ISSUE0002341 - 保存データの暗号化が不十分
  • 発生元: 外部監査の指摘事項
  • リスク評価: 4(重大)
  • 関連リスク: リスク2件、平均残余スコア75
  • 影響を受ける統制: CTRL0001892(不合格)、CTRL0001893(未テスト)
  • ポリシー参照: POL0000145 - データ保護ポリシー
  • ビジネスへの影響: 高 - GDPR第32条に基づく規制上のリスク。年間収益の最大4%の罰金が科される可能性があります
  • 未解決日数: 45日(期限から15日超過)

関連スキル

  • grc/issue-action-plan - 要約された問題に対する是正措置計画を作成します
  • grc/risk-assessment-summarization - 問題の背景にあるリスクをより詳しく分析します
  • grc/control-objective-management - 問題に関連する統制を管理します
  • grc/regulatory-alert-analysis - 問題の規制上の要因を特定します
  • security/audit-compliance - 監査証跡とコンプライアンスレポートを提供します

参考資料

コメント