skills/secops/metrics-analysis/SKILL.md
Analyze security operations metrics including MTTD, MTTR, incident volume trends, false positive rates, and analyst workload distribution
npx skillsauth add happy-technologies-llc/happy-servicenow-skills metrics-analysisInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides a structured approach to analyzing key security operations metrics in ServiceNow. It enables SOC managers and analysts to measure detection and response effectiveness, identify trends, and optimize resource allocation.
Key capabilities:
When to use: During periodic SOC performance reviews (weekly, monthly, quarterly), management reporting, capacity planning, or when optimizing detection and response processes.
sn_si.manager, sn_si.admin, report_adminsn_si_incident, sn_si_task, sn_ti_observable, sys_audit tables; access to Aggregate APIQuery incident counts by time period to establish volume trends.
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: sn_si_incident
query: opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)
fields: sys_id,number,category,subcategory,priority,state,opened_at,resolved_at,closed_at,close_code,assignment_group
limit: 1000
Using REST (Aggregate API for counts):
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_count=true&sysparm_group_by=category
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_count=true&sysparm_group_by=priority
MTTD measures the elapsed time between when a threat first appears and when it is detected. Use observable first_seen timestamps compared to incident opened_at.
Using MCP:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var gr = new GlideRecord('sn_si_incident');
gr.addQuery('opened_at', '>=', gs.daysAgo(30));
gr.addQuery('state', 'IN', '6,7'); // resolved or closed
gr.query();
var totalDetectTime = 0;
var count = 0;
while (gr.next()) {
var obs = new GlideRecord('sn_ti_observable');
obs.addQuery('security_incident', gr.sys_id);
obs.orderBy('first_seen');
obs.setLimit(1);
obs.query();
if (obs.next() && obs.first_seen) {
var firstSeen = new GlideDateTime(obs.first_seen);
var opened = new GlideDateTime(gr.opened_at);
var diff = GlideDateTime.subtract(firstSeen, opened);
totalDetectTime += diff.getNumericValue();
count++;
}
}
var avgMs = count > 0 ? totalDetectTime / count : 0;
var avgHours = (avgMs / 3600000).toFixed(2);
gs.info('MTTD: ' + avgHours + ' hours (based on ' + count + ' incidents)');
Using REST (manual calculation):
GET /api/now/table/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^stateIN6,7&sysparm_fields=sys_id,number,opened_at,resolved_at&sysparm_limit=500
# For each incident, query earliest observable:
GET /api/now/table/sn_ti_observable?sysparm_query=security_incident=[SYS_ID]^ORDERBYfirst_seen&sysparm_fields=first_seen&sysparm_limit=1
MTTR measures elapsed time from incident creation to resolution.
Using MCP:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var gr = new GlideRecord('sn_si_incident');
gr.addQuery('opened_at', '>=', gs.daysAgo(30));
gr.addQuery('resolved_at', '!=', '');
gr.query();
var totalResponseTime = 0;
var count = 0;
while (gr.next()) {
var opened = new GlideDateTime(gr.opened_at);
var resolved = new GlideDateTime(gr.resolved_at);
var diff = GlideDateTime.subtract(opened, resolved);
totalResponseTime += diff.getNumericValue();
count++;
}
var avgMs = count > 0 ? totalResponseTime / count : 0;
var avgHours = (avgMs / 3600000).toFixed(2);
gs.info('MTTR: ' + avgHours + ' hours (based on ' + count + ' incidents)');
Using REST (Aggregate API):
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^resolved_atISNOTEMPTY&sysparm_avg_fields=business_duration&sysparm_count=true
Calculate the percentage of incidents closed as false positives or non-incidents.
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: sn_si_incident
query: closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^close_code=false_positive
fields: sys_id,number,category,subcategory,assignment_group,closed_at
limit: 500
Tool: SN-Query-Table
Parameters:
table_name: sn_si_incident
query: closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)
fields: sys_id
limit: 1
Using REST:
# Total closed incidents
GET /api/now/stats/sn_si_incident?sysparm_query=closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_count=true
# False positives
GET /api/now/stats/sn_si_incident?sysparm_query=closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^close_code=false_positive&sysparm_count=true
Measure incident distribution across analysts and assignment groups.
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: sn_si_incident
query: opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^assigned_toISNOTEMPTY
fields: assigned_to,assignment_group,priority,state
limit: 1000
Using REST (Aggregate API):
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)^assigned_toISNOTEMPTY&sysparm_count=true&sysparm_group_by=assigned_to
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_count=true&sysparm_group_by=assignment_group
Identify shifts in attack types over time by comparing category distributions.
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: sn_si_incident
query: opened_atONLast 90 days@javascript:gs.daysAgoStart(90)@javascript:gs.daysAgoEnd(0)
fields: sys_id,category,subcategory,priority,opened_at
limit: 2000
Using REST:
# Current month
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_count=true&sysparm_group_by=category
# Previous month
GET /api/now/stats/sn_si_incident?sysparm_query=opened_atBETWEENjavascript:gs.daysAgoStart(60)@javascript:gs.daysAgoEnd(30)&sysparm_count=true&sysparm_group_by=category
Analyze how quickly security incident tasks are completed.
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: sn_si_task
query: closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)
fields: sys_id,number,short_description,state,priority,assigned_to,opened_at,closed_at,work_duration
limit: 500
Using REST:
GET /api/now/stats/sn_si_task?sysparm_query=closed_atONLast 30 days@javascript:gs.daysAgoStart(30)@javascript:gs.daysAgoEnd(0)&sysparm_avg_fields=work_duration&sysparm_count=true&sysparm_group_by=priority
Generate a comprehensive metrics summary.
=== SECURITY OPERATIONS METRICS REPORT ===
Period: 2026-02-19 to 2026-03-19 (30 days)
Generated: 2026-03-19
VOLUME METRICS:
Total Incidents: 156 (prev period: 142, +9.8%)
- Critical/P1: 4 (2.6%)
- High/P2: 18 (11.5%)
- Medium/P3: 67 (42.9%)
- Low/P4: 67 (42.9%)
By Category:
- Phishing: 52 (33.3%) ↑ from 28.2%
- Malware: 28 (17.9%) ↓ from 22.5%
- Unauthorized Access: 24 (15.4%) → stable
- Data Loss: 12 (7.7%) → stable
- Vulnerability Exploit: 18 (11.5%) ↑ from 8.5%
- Other: 22 (14.1%)
RESPONSE METRICS:
Mean Time to Detect (MTTD): 4.2 hours (target: <2h, prev: 5.1h)
Mean Time to Respond (MTTR): 18.6 hours (target: <24h, prev: 22.3h)
Mean Time to Contain (MTTC): 2.8 hours (target: <4h, prev: 3.5h)
QUALITY METRICS:
False Positive Rate: 23.1% (target: <20%, prev: 25.6%)
Reopened Incidents: 3 (1.9%)
Escalation Rate: 14.1%
WORKLOAD DISTRIBUTION:
| Analyst | Incidents | Avg Resolution | Utilization |
|---------|-----------|----------------|-------------|
| T. Williams | 32 | 14.2h | 85% |
| K. Chen | 28 | 16.8h | 78% |
| A. Johnson | 31 | 19.1h | 82% |
| M. Davis | 25 | 21.5h | 70% |
| L. Park | 22 | 15.3h | 65% |
| Unassigned | 18 | N/A | - |
KEY OBSERVATIONS:
1. MTTD improved 18% but still above 2-hour target
2. Phishing volume increasing - consider enhanced email filtering
3. False positive rate improving but above 20% threshold
4. Workload imbalance: T. Williams carrying 20% of all incidents
5. 18 unassigned incidents need immediate attention
RECOMMENDATIONS:
1. Tune phishing detection rules to reduce false positives
2. Rebalance workload: redistribute from T. Williams to L. Park
3. Investigate MTTD gap: review SIEM alert correlation rules
4. Staff augmentation may be needed if volume trend continues
| Tool | Purpose | |------|---------| | SN-Query-Table | Retrieve incident and task records for metric calculation | | SN-Execute-Background-Script | Run server-side scripts for complex metric calculations | | SN-Add-Work-Notes | Document metrics findings on relevant records |
Cause: No observables with first_seen timestamps are linked to incidents, or the timestamp predates the incident.
Solution: Verify that threat intelligence integrations are populating first_seen on observables. If first_seen is after opened_at, MTTD may be negative; use the incident opened_at as the detection time in those cases.
Cause: Encoded queries may not match the expected date ranges, or display values differ from stored values. Solution: Test the query in the ServiceNow UI list filter first. Ensure date formats match the instance timezone.
Cause: The assigned_to field may be cleared when incidents are resolved or closed.
Solution: Use sys_audit to find the last assigned analyst before closure, or query the assigned_to field with sysparm_display_value=true to get display names.
Cause: PA indicators may use different data collection schedules, filters, or calculation methods. Solution: Align your query filters with the PA indicator definitions. Check PA data collection job schedules and ensure they have run recently.
Example 1: Monthly SOC Performance Report
Example 2: Quarterly Executive Dashboard
secops/correlation-insights - Measure correlation effectiveness as a detection metricsecops/incident-summarization - Include metrics context in incident summariessecops/post-incident-analysis - Use PIR data to improve detection metricssecops/shift-handover - Include shift-level metrics in handover reportsreporting/trend-analysis - General ServiceNow trend analysis techniquestesting
Manage supplier onboarding, qualification, performance monitoring, and offboarding with auditable lifecycle controls
tools
Identify emerging risks, prioritize intake signals, and route candidates into formal GRC risk assessment workflows
documentation
Screen inbound documents for completeness, policy risk, and routing readiness before extraction or case workflows
testing
Generate concise task summaries with status, timeline, blockers, SLA risk, and recommended next actions