get_ioc_matches only outputs the first artifactIndicator field and drops match metadata
secops_mcp 0.1.3. secops_mcp/tools/ioc_matches.py::get_ioc_matches builds the output with next(iter(...)) on artifactIndicator, so when a match has multiple indicator fields (common: domain + url, or hashSha256 + fileName) only the first is reported. The formatter also writes only Type / Value / Sources — first/last seen, associated entity, category, severity, and confidence are all dropped.
A tier-1 SOC agent that uses this tool gets (type, value, sources) and cannot tell when the IoC fired, who/what triggered it, or how confident the source is.
Current code (ioc_matches.py lines 102–118):
if 'artifactIndicator' in match and isinstance(match['artifactIndicator'], dict):
# Get the first key-value pair from artifactIndicator
indicator_dict = match.get('artifactIndicator', {})
if indicator_dict:
indicator_type = next(iter(indicator_dict.keys()), 'Unknown')
indicator_value = next(iter(indicator_dict.values()), 'Unknown')
sources = match.get('sources', [])
# ...
result += f'Type: {indicator_type}\n'
result += f'Value: {indicator_value}\n'
result += f'Sources: {sources_str}\n\n'
Fix — iterate all indicator fields and surface the missing metadata:
-if 'artifactIndicator' in match and isinstance(match['artifactIndicator'], dict):
- indicator_dict = match.get('artifactIndicator', {})
- if indicator_dict:
- indicator_type = next(iter(indicator_dict.keys()), 'Unknown')
- indicator_value = next(iter(indicator_dict.values()), 'Unknown')
-
-sources = match.get('sources', [])
-
-result += f'Type: {indicator_type}\n'
-result += f'Value: {indicator_value}\n'
-result += f'Sources: {sources_str}\n\n'
+indicators = []
+if isinstance(match, dict):
+ for k, v in (match.get('artifactIndicator') or {}).items():
+ indicators.append(f'{k}={v}')
+
+sources = match.get('sources', []) if isinstance(match, dict) else []
+first_seen = match.get('firstSeenTime', 'Unknown')
+last_seen = match.get('lastSeenTime', 'Unknown')
+category = match.get('category', 'Unknown')
+severity = match.get('severity', 'Unknown')
+confidence = match.get('confidence', 'Unknown')
+associated = match.get('associatedEntity', 'Unknown')
+
+result += f'Indicator(s): {", ".join(indicators) or "Unknown"}\n'
+result += f'First Seen: {first_seen}\n'
+result += f'Last Seen: {last_seen}\n'
+result += f'Category: {category}\n'
+result += f'Severity: {severity}\n'
+result += f'Confidence: {confidence}\n'
+result += f'Associated: {associated}\n'
+result += f'Sources: {", ".join(sources) or "Unknown"}\n\n'
Verify the precise field names against secops/chronicle/ioc.py / the live API response before merging — some keys may use snake_case in the SDK shape.
get_ioc_matchesonly outputs the firstartifactIndicatorfield and drops match metadatasecops_mcp0.1.3.secops_mcp/tools/ioc_matches.py::get_ioc_matchesbuilds the output withnext(iter(...))onartifactIndicator, so when a match has multiple indicator fields (common:domain+url, orhashSha256+fileName) only the first is reported. The formatter also writes onlyType/Value/Sources— first/last seen, associated entity, category, severity, and confidence are all dropped.A tier-1 SOC agent that uses this tool gets
(type, value, sources)and cannot tell when the IoC fired, who/what triggered it, or how confident the source is.Current code (
ioc_matches.pylines 102–118):Fix — iterate all indicator fields and surface the missing metadata:
Verify the precise field names against
secops/chronicle/ioc.py/ the live API response before merging — some keys may use snake_case in the SDK shape.