| name | research-v3 |
| description | AWS service capability discovery producing research.json with structured capabilities, API surface, and existing mitigations. Uses MCP servers for AWS documentation, service authorization, and best practices. Downstream skills (Validate, Map) consume this output. Split into 3 focused sub-skills for better accuracy; this skill orchestrates and merges. |
| disable-model-invocation | false |
| argument-hint | [./input-folder] [--service=<name>] |
Service Approval — Researcher v3
Analyze the target AWS service to discover its security capabilities and API surface.
Produce a single structured output consumed by downstream skills.
Output: .service-approval/<slug>/02-research/research.json
Sub-Skill Architecture
This skill is split into 3 core sub-skills plus 1 optional enrichment sub-skill:
| Sub-Skill | Focus | Output | Required |
|---|
research-mitigations | Input parsing, mitigations, assets, assessment gaps | research-mitigations.json | Yes |
research-capabilities | IAM, encryption, network, logging, org-policies via MCP | research-capabilities.json | Yes |
research-api-surface | Complete API operations, parameters, cross-ref validation | research-api-surface.json | Yes |
research-attack-surface | MITRE ATT&CK Cloud techniques, STRIDE bridge | research-attack-surface.json | No |
When invoked directly (via /research), this skill runs all sub-skills sequentially
and merges their outputs. When invoked by the orchestrator, the orchestrator dispatches
sub-skills in parallel (all 4 if ATT&CK enrichment is enabled) and calls the merge step below.
Prerequisites
from tools.paths import phase_dir, mcp_log
research_dir = phase_dir(slug, "research")
research_dir.mkdir(parents=True, exist_ok=True)
mcp_log(slug).touch()
MCP refresh decision (REQUIRED — ask the user when a prior research.json exists):
research-capabilities and research-api-surface call awsknowledge / kb-search /
aws-documentation MCPs for every capability and API parameter. If a prior research.json
exists, ask the user before re-running:
if [ -f .service-approval/<slug>/02-research/research.json ]; then
mtime=$(stat -f "%Sm" -t "%Y-%m-%d" .service-approval/<slug>/02-research/research.json 2>/dev/null \
|| stat -c "%y" .service-approval/<slug>/02-research/research.json | cut -d' ' -f1)
echo "Existing research.json dated $mtime"
fi
A prior research.json exists (dated {mtime}). Re-running all 3 sub-skills will re-call
every MCP for the target service (~20-40 calls).
(a) Re-run all sub-skills (recommended if AWS service has had recent releases,
input files changed, or >7 days old)
(b) Reuse existing research.json (faster; only safe if upstream SAR/inputs unchanged)
Which? [a / b]
If (b), skip to merge step and proceed to /validate with the existing research.json.
Do not silently reuse. Log the decision to mcp-calls.log.
Inputs
Parse arguments:
./input-folder (optional) — read all .md, .json, .xlsx, .pdf, .docx files
--service=<name> — AWS service identifier (e.g., bedrock-agentcore, lambda, s3)
If neither is provided, ask the user for a service name before proceeding.
Direct Invocation Mode (Sequential)
When this skill is invoked directly (not by orchestrator):
Step 1: Run research-mitigations
Follow ALL instructions in skills/research-mitigations/SKILL.md.
Pass: ./input-folder, --service.
Wait for .service-approval/<slug>/02-research/research-mitigations.json.
Step 2: Run research-capabilities
Follow ALL instructions in skills/research-capabilities/SKILL.md.
Pass: --service.
Wait for .service-approval/<slug>/02-research/research-capabilities.json.
Step 3: Run research-api-surface
Follow ALL instructions in skills/research-api-surface/SKILL.md.
Pass: --service.
Wait for .service-approval/<slug>/02-research/research-api-surface.json.
Step 3b (optional): Run research-attack-surface
Follow ALL instructions in skills/research-attack-surface/SKILL.md.
Pass: --service.
Wait for .service-approval/<slug>/02-research/research-attack-surface.json.
This step is optional — skip if ATT&CK enrichment is not needed.
Step 4: Merge
Run the merge step below.
Merge Step
This step combines the 3 sub-skill outputs into the final research.json.
It can be invoked by the orchestrator after all 3 sub-skills complete.
Prerequisites for merge
test -f .service-approval/<slug>/02-research/research-mitigations.json && echo "mitigations: OK" || echo "ERROR"
test -f .service-approval/<slug>/02-research/research-capabilities.json && echo "capabilities: OK" || echo "ERROR"
test -f .service-approval/<slug>/02-research/research-api-surface.json && echo "api-surface: OK" || echo "ERROR"
All 3 files must exist before merging.
Merge Logic
import json
mit = json.load(open('.service-approval/<slug>/02-research/research-mitigations.json'))
cap = json.load(open('.service-approval/<slug>/02-research/research-capabilities.json'))
api = json.load(open('.service-approval/<slug>/02-research/research-api-surface.json'))
for a in mit.get('assets_partial', []):
if 'resource_type' in a and 'cfn_type' not in a:
a['cfn_type'] = a.pop('resource_type')
if 'display_name' in a and 'name' not in a:
a['name'] = a.pop('display_name')
ops = api.get('api_surface', {}).get('operations', [])
if not ops:
ops = api.get('operations', [])
if ops:
api.setdefault('api_surface', {})['operations'] = ops
op ops:
op op:
op[] = op.pop()
():
result = []
s (sources []):
(s, ):
result.append(s)
(s, ) s:
result.append(s[])
result
assets_by_cfn = {}
a mit.get(, []):
cfn = a.get(, )
assets_by_cfn[cfn] = a
service = mit.get(, cap.get(, api.get(, )))
op api.get(, {}).get(, []):
op_name = op.get(, )
op_name.startswith():
resource_name = op_name[:]
found =
cfn assets_by_cfn:
resource_name.lower() cfn.lower() resource_name.lower() assets_by_cfn[cfn].get(, ).lower():
found =
found:
cfn_prefix =
sar =
:
sar = json.load(())
cfn_prefix = sar.get(, )
:
cfn_prefix:
existing_cfn assets_by_cfn:
parts = existing_cfn.split()
(parts) >= :
cfn_prefix = parts[]
cfn_type = cfn_prefix
assets_by_cfn[cfn_type] = {
: resource_name,
: cfn_type,
: ,
: [],
: [],
:
}
enc = cap.get(, {}).get(, {}).get(, {})
per_resource_enc = {e.get(, ): e e enc.get(, [])}
cfn, asset assets_by_cfn.items():
asset.get(, ).startswith():
name_lower = asset.get(, ).lower()
matched =
enc_rt, enc_info per_resource_enc.items():
name_lower enc_rt.lower():
key_types = enc_info.get(, [])
key_types:
asset[] =
:
asset[] =
matched =
matched:
asset[] =
ops = api.get(, {}).get(, [])
op ops:
op_name = op.get(, )
cfn, asset assets_by_cfn.items():
resource_name = cfn.split()[-] cfn
resource_name.lower() op_name.lower():
asset:
asset[] = []
op_name.startswith((, , , , )):
op_name asset[]:
asset[].append(op_name)
GENERIC_OPS = {, , ,
, , }
deduped = {}
cfn, asset assets_by_cfn.items():
eps = (asset.get(, [])) - GENERIC_OPS
merged =
existing_cfn, existing_asset deduped.items():
existing_eps = (existing_asset.get(, [])) - GENERIC_OPS
eps existing_eps (eps & existing_eps) > ((eps), (existing_eps)) * :
(eps) > (existing_eps):
deduped[cfn] = asset
deduped[existing_cfn]
merged =
merged:
deduped[cfn] = asset
assets_by_cfn = deduped
research = {
: ,
: mit.get(, cap.get(, api.get(, ))),
: cap.get(, []),
: (assets_by_cfn.values()),
: cap.get(, {}),
: api.get(, {}),
: mit.get(, []),
: mit.get(, []),
: ((
normalize_sources(cap.get(, [])) +
normalize_sources(api.get(, []))
))
}
():
result = []
item (poa_list []):
(item, ): result.append(item)
(item, ):
name = item.get(, item.get(, ))
name: result.append(name)
result
cap_poa = normalize_poa(cap.get(, {}).get(, {}).get(, []))
api_poa = normalize_poa(api.get(, {}).get(, []))
merged_poa = ((cap_poa) | (api_poa))
merged_poa:
research[].setdefault(, {})[] = merged_poa
os
atk_path =
os.path.isfile(atk_path):
atk = json.load((atk_path))
research[] = {
: atk.get(, []),
: atk.get(, []),
: atk.get(, {}),
: atk.get(, {})
}
json.dump(research, (, ), indent=)
Merged Output Validation
Validation runs automatically via PostToolUse hook (validate_state.py Check S9).
If CRITICAL errors exist (especially condition key cross-reference failures), the merge
step must report them. The orchestrator should re-dispatch the api-surface sub-skill with
the missing action list.
MCP Log Verification
Validation runs automatically via PostToolUse hook (validate_state.py Check S9).
Print Summary
Research v3 complete:
Output: .service-approval/<slug>/02-research/research.json (schema v3.0)
Sub-skills:
- Mitigations: {N} mitigations, {N} assessment gaps
- Capabilities: {N} condition keys, {N} resources
- API Surface: {N} operations ({N}/{N} completeness)
Merged:
- Assets: {N}
- Operations: {N}
- Mitigations: {N}
- Doc sources: {N}
- MCP calls: {N}