About metrics-analysis
metrics-analysis is a security operations metrics skill for ServiceNow SOC managers and analysts. It evaluates detection and response performance using measures such as MTTD, MTTR, false-positive rates, workload distribution, and historical trends.
Key features
- 01Calculate MTTD and MTTR
- 02Track incident volume by category and priority
- 03Measure false-positive rates and impact
- 04Analyze analyst workload distribution
- 05Compare historical baselines and targets
- 06Generate management reports and dashboards
Use cases
- 01Run weekly or monthly SOC reviews
- 02Prepare security operations reports for management
- 03Plan staffing capacity and resource allocation
- 04Improve detection and response processes
MCP support
The documented workflow uses MCP tools to query ServiceNow incident and task records and run background scripts for metric calculations.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill metrics-analysisAfter installing, type/metrics-analysisin Claude Code to confirm it loaded.
Details
From the project READMESecurity Operations Metrics Analysis
Overview
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:
- Calculate Mean Time to Detect (MTTD) and Mean Time to Respond (MTTR) across incident populations
- Track incident volume trends by category, priority, and time period
- Measure false positive rates and their impact on analyst productivity
- Analyze analyst workload distribution and identify bottlenecks
- Benchmark current performance against historical baselines and industry targets
- Generate metrics dashboards and reports for management review
When to use: During periodic SOC performance reviews (weekly, monthly, quarterly), management reporting, capacity planning, or when optimizing detection and response processes.
Prerequisites
- Roles:
sn_si.manager,sn_si.admin,report_admin - Access: Read access to
sn_si_incident,sn_si_task,sn_ti_observable,sys_audittables; access to Aggregate API - Plugins: Security Incident Response (sn_si), Performance Analytics (optional but recommended)
- Data: Sufficient incident history (minimum 30 days recommended for trend analysis)
Procedure
Step 1: Calculate Incident Volume by Period
Query 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
Step 2: Calculate Mean Time to Detect (MTTD)
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
Step 3: Calculate Mean Time to Respond (MTTR)
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
Step 4: Measure False Positive Rate
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
Step 5: Analyze Analyst Workload Distribution
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
Step 6: Track Incident Category Trends
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
Step 7: Measure Task Completion Efficiency
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
Step 8: Compile Metrics Report
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 Usage
| 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 |
Best Practices
- Establish baselines before setting targets; use 90 days of historical data minimum
- Segment metrics by priority since P1 and P4 incidents have vastly different acceptable response times
- Track trends over time rather than focusing on single-period snapshots
- Automate data collection using scheduled background scripts or Performance Analytics indicators
- Exclude outliers when calculating averages; a single month-long incident can skew MTTR significantly
- Report false positive rates by source to identify which detection rules need tuning
- Align metrics with business outcomes by connecting MTTD/MTTR to business impact duration
- Review workload distribution monthly to prevent analyst burnout and ensure equitable assignment
Troubleshooting
MTTD Calculation Returns Zero
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.
Aggregate API Returns Unexpected Counts
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.
Workload Metrics Show All Incidents as Unassigned
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.
Metrics Differ from Performance Analytics
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.
Examples
Example 1: Monthly SOC Performance Report
- Calculate 30-day incident volume: 156 total across all categories
- Compute MTTD (4.2h), MTTR (18.6h), and MTTC (2.8h)
- Determine false positive rate: 36 false positives out of 156 total (23.1%)
- Analyze workload across 5 analysts and identify imbalance
- Compare all metrics against previous period and targets
- Generate report with trend arrows and recommendations
Example 2: Quarterly Executive Dashboard
- Pull 90-day incident data segmented by month for trend charts
- Calculate month-over-month MTTD improvement: 6.3h → 5.1h → 4.2h
- Identify top 3 categories driving volume increase
- Benchmark MTTR against industry average (24h for mid-size organizations)
- Project staffing needs based on volume trend (+9.8%/month)
- Present findings with recommended budget for additional analyst headcount
Related Skills
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 techniques