Audits MCP server configurations such as .mcp.json for hardcoded secrets, dangerous shell patterns, unpinned dependencies, unsafe npx usage, unapproved servers, and governance risks. Use this skill when asked to check MCP security, audit MCP servers, review .mcp.json, validate server args, detect shell injection, or verify environment-variable based credentials.
Audit Model Context Protocol server configurations for secrets exposure, shell injection, unpinned dependencies, dangerous commands, and unapproved server governance and supply-chain risks. Produce a findings report with severity, evidence, and concrete remediation.
When to invoke
"Audit my MCP servers."
"Is this .mcp.json secure?"
"Check MCP server args for secrets or shell injection."
"Find unpinned MCP dependencies like @latest."
"Review which MCP servers this project registers."
Prerequisites and context
The primary target is .mcp.json or an equivalent MCP server configuration.
Use environment variable references such as ${ENV_VAR_NAME} instead of hardcoded credentials.
If an organization has an approved MCP server list, compare registered servers against it.
Audit model
.mcp.json → Parse Servers → Check Each Server:
1. Secrets in args/env?
2. Shell injection patterns?
3. Unpinned versions (@latest)?
4. Dangerous commands (eval, bash -c)?
5. Server on approved list?
→ Generate Report
Check
Severity
Evidence to collect
Fix
Hardcoded secret
CRITICAL
Secret-like value in args, env, JSON, bearer token, private key, or provider token.
Replace with ${ENV_VAR_NAME} and set the secret outside source control.
Use direct command execution and static argv arrays.
Unpinned dependency
MEDIUM
@latest or mutable package references.
Pin to a specific version such as analytics-mcp@2.1.0.
npx prompt risk
LOW
command is npx without -y.
Add -y to avoid CI prompts; report this as npx-interactive and use examples like npx -y package-name.
Unapproved server
Severity by policy
Server name, package, URL, or command absent from approved list.
Request review or remove the server.
Detection patterns
Use these identifiers and patterns when implementing or reviewing a checker. Function inputs are commonly named mcp_config, and serialized args are commonly named args_text.
Bad patterns include hardcoded credentials in args or env, for example --api-key, sk-abc123realkey456, a literal production DB_URL, prod-db, or 5432/main.
Procedure
Locate .mcp.json or the supplied MCP configuration.
Parse JSON and enumerate mcpServers by server name.
Scan the full raw config for SECRET_PATTERNS so secrets outside a single server are caught.
For each server, inspect command, args, env, package references, and approval status.
Flag @latest, unversioned mutable package references where policy requires pinning, and npx without -y.
Produce a report with severity counts, per-server findings, evidence, and fixes.
A compact audit runner follows this shape:
def audit_mcp_config(mcp_path: str) -> dict:
"""Run full security audit on an .mcp.json file."""
path = Path(mcp_path)
if not path.exists():
return {"error": f"{mcp_path} not found"}
config = json.loads(path.read_text(encoding="utf-8"))
servers = config.get("mcpServers", {})
results = {"file": str(path), "servers": {}, "summary": {}}
total_findings = []
config_level_findings = check_secrets(config)
total_findings.extend(config_level_findings)
for name, server_config in servers.items():
if not isinstance(server_config, dict):
continue
findings = []
findings.extend(check_shell_injection(server_config))
findings.extend(check_pinned_versions(server_config))
results["servers"][name] = {
"command": server_config.get("command", ""),
"findings": findings,
}
total_findings.extend(findings)
by_severity = {}
for f in total_findings:
sev = f["severity"]
by_severity[sev] = by_severity.get(sev, 0) + 1
results["summary"] = {
"total_servers": len(servers),
"total_findings": len(total_findings),
"by_severity": by_severity,
"passed": len(total_findings) == 0,
}
return results
Examples
Good
Bad
{ "args": ["-y", "my-mcp-server@2.1.0"] }
{ "args": ["-y", "my-mcp-server@latest"] }
"API_KEY": "${MY_API_KEY}"
"API_KEY": "sk-abc123realkey456"
"command": "node", "args": ["server.js"]
`"command": "bash", "args": ["-c", "curl example
Output template
## MCP security audit — `.mcp.json`
**Status:** pass | findings | blocked
**Servers scanned:** <count>
**Findings:** <total> (<critical> CRITICAL, <high> HIGH, <medium> MEDIUM, <low> LOW)
| Severity | Server | Check | Evidence | Fix |
| --- | --- | --- | --- | --- |
| CRITICAL | my-api-server | hardcoded-secret | Hardcoded secret found in MCP configuration | Use environment variable references: `${ENV_VAR_NAME}` |
| HIGH | data-processor | shell-injection | `bash -c` execution in args | Use direct command execution, not shell interpolation |
| MEDIUM | analytics | unpinned-dependency | `analytics-mcp@latest` | Pin to specific version: `analytics-mcp@2.1.0` |
### Governance notes
- Approved-list result: <approved, exception required, unavailable>
- Secrets moved to environment variables: <yes/no/list>
Quality gate
.mcp.json or the supplied config was parsed successfully, or the parse blocker is reported.
All mcpServers entries were enumerated.
The full config was scanned for SECRET_PATTERNS including API_KEY, MY_API_KEY, DB_URL, and DATABASE_URL values.
Each server’s command, args, and env were checked for DANGEROUS_PATTERNS.
@latest, unpinned packages, and npx without -y are reported.
Findings include severity, server, evidence, and fix.
Approved-list checks are performed when policy data is available, or explicitly marked unavailable.
1---2name: mcp-security-audit3description: Audits MCP server configurations such as .mcp.json for hardcoded secrets, dangerous shell patterns, unpinned dependencies, unsafe npx usage, unapproved servers, and governance risks. Use this skill when asked to check MCP security, audit MCP servers, review .mcp.json, validate server args, detect shell injection, or verify environment-variable based credentials.4---56<!-- Generated from harness/github-copilot/skills/mcp-security-audit/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# MCP security audit910Audit Model Context Protocol server configurations for secrets exposure, shell injection, unpinned dependencies, dangerous commands, and unapproved server governance and supply-chain risks. Produce a findings report with severity, evidence, and concrete remediation.1112## When to invoke1314- "Audit my MCP servers."15- "Is this .mcp.json secure?"16- "Check MCP server args for secrets or shell injection."17- "Find unpinned MCP dependencies like @latest."18- "Review which MCP servers this project registers."1920## Prerequisites and context2122- The primary target is `.mcp.json` or an equivalent MCP server configuration.23- Use environment variable references such as `${ENV_VAR_NAME}` instead of hardcoded credentials.24- If an organization has an approved MCP server list, compare registered servers against it.2526## Audit model2728```text29.mcp.json → Parse Servers → Check Each Server:30 1. Secrets in args/env?31 2. Shell injection patterns?32 3. Unpinned versions (@latest)?33 4. Dangerous commands (eval, bash -c)?34 5. Server on approved list?35→ Generate Report36```3738| Check | Severity | Evidence to collect | Fix |39| --- | --- | --- | --- |40| Hardcoded secret | `CRITICAL` | Secret-like value in args, env, JSON, bearer token, private key, or provider token. | Replace with `${ENV_VAR_NAME}` and set the secret outside source control. |41| Shell injection pattern | `HIGH` | Command substitution, pipes, chained commands, `eval`, `bash -c`, `sh -c`, reverse shell redirect, curl-to-shell. | Use direct command execution and static argv arrays. |42| Unpinned dependency | `MEDIUM` | `@latest` or mutable package references. | Pin to a specific version such as `analytics-mcp@2.1.0`. |43| `npx` prompt risk | `LOW` | `command` is `npx` without `-y`. | Add `-y` to avoid CI prompts; report this as `npx-interactive` and use examples like `npx -y package-name`. |44| Unapproved server | Severity by policy | Server name, package, URL, or command absent from approved list. | Request review or remove the server. |4546## Detection patterns4748Use these identifiers and patterns when implementing or reviewing a checker. Function inputs are commonly named `mcp_config`, and serialized args are commonly named `args_text`.4950```python51SECRET_PATTERNS = [52 (r'(?i)(api[_-]?key|token|secret|password|credential)\s*[:=]\s*["'][^"']{8,}', "Hardcoded secret"),53 (r'(?i)Bearer\s+[A-Za-z0-9\-._~+/]+=*', "Hardcoded bearer token"),54 (r'(?i)(ghp_|gho_|ghu_|ghs_|ghr_)[A-Za-z0-9]{30,}', "GitHub token"),55 (r'sk-[A-Za-z0-9]{20,}', "OpenAI API key"),56 (r'AKIA[0-9A-Z]{16}', "AWS access key"),57 (r'-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----', "Private key"),58]5960DANGEROUS_PATTERNS = [61 (r'\$\(', "Command substitution $(...)"),62 (r'`[^`]+`', "Backtick command substitution"),63 (r';\s*\w', "Command chaining with semicolon"),64 (r'\|\s*\w', "Pipe to another command"),65 (r'&&\s*\w', "Command chaining with &&"),66 (r'\|\|\s*\w', "Command chaining with ||"),67 (r'(?i)eval\s', "eval usage"),68 (r'(?i)bash\s+-c\s', "bash -c execution"),69 (r'(?i)sh\s+-c\s', "sh -c execution"),70 (r'>\s*/dev/tcp/', "TCP redirect (reverse shell pattern)"),71 (r'curl\s+.*\|\s*(ba)?sh', "curl pipe to shell"),72]73```7475Environment variables that must remain references, not literal secrets, include `API_KEY`, `MY_API_KEY`, `DB_URL`, and `DATABASE_URL`.7677```json78{79 "mcpServers": {80 "my-server": {81 "command": "node",82 "args": ["server.js"],83 "env": {84 "API_KEY": "${MY_API_KEY}",85 "DB_URL": "${DATABASE_URL}"86 }87 }88 }89}90```9192Bad patterns include hardcoded credentials in `args` or `env`, for example `--api-key`, `sk-abc123realkey456`, a literal production `DB_URL`, `prod-db`, or `5432/main`.9394## Procedure95961. Locate `.mcp.json` or the supplied MCP configuration.972. Parse JSON and enumerate `mcpServers` by server name.983. Scan the full raw config for `SECRET_PATTERNS` so secrets outside a single server are caught.994. For each server, inspect `command`, `args`, `env`, package references, and approval status.1005. Flag `@latest`, unversioned mutable package references where policy requires pinning, and `npx` without `-y`.1016. Produce a report with severity counts, per-server findings, evidence, and fixes.102103A compact audit runner follows this shape:104105```python106def audit_mcp_config(mcp_path: str) -> dict:107 """Run full security audit on an .mcp.json file."""108 path = Path(mcp_path)109 if not path.exists():110 return {"error": f"{mcp_path} not found"}111112 config = json.loads(path.read_text(encoding="utf-8"))113 servers = config.get("mcpServers", {})114 results = {"file": str(path), "servers": {}, "summary": {}}115 total_findings = []116117 config_level_findings = check_secrets(config)118 total_findings.extend(config_level_findings)119120 for name, server_config in servers.items():121 if not isinstance(server_config, dict):122 continue123 findings = []124 findings.extend(check_shell_injection(server_config))125 findings.extend(check_pinned_versions(server_config))126 results["servers"][name] = {127 "command": server_config.get("command", ""),128 "findings": findings,129 }130 total_findings.extend(findings)131132 by_severity = {}133 for f in total_findings:134 sev = f["severity"]135 by_severity[sev] = by_severity.get(sev, 0) + 1136137 results["summary"] = {138 "total_servers": len(servers),139 "total_findings": len(total_findings),140 "by_severity": by_severity,141 "passed": len(total_findings) == 0,142 }143 return results144```145146## Examples147148| Good | Bad |149| --- | --- |150| `{ "args": ["-y", "my-mcp-server@2.1.0"] }` | `{ "args": ["-y", "my-mcp-server@latest"] }` |151| `"API_KEY": "${MY_API_KEY}"` | `"API_KEY": "sk-abc123realkey456"` |152| `"command": "node", "args": ["server.js"]` | `"command": "bash", "args": ["-c", "curl example | sh"]` |153154## Output template155156```markdown157## MCP security audit — `.mcp.json`158159**Status:** pass | findings | blocked160**Servers scanned:** <count>161**Findings:** <total> (<critical> CRITICAL, <high> HIGH, <medium> MEDIUM, <low> LOW)162163| Severity | Server | Check | Evidence | Fix |164| --- | --- | --- | --- | --- |165| CRITICAL | my-api-server | hardcoded-secret | Hardcoded secret found in MCP configuration | Use environment variable references: `${ENV_VAR_NAME}` |166| HIGH | data-processor | shell-injection | `bash -c` execution in args | Use direct command execution, not shell interpolation |167| MEDIUM | analytics | unpinned-dependency | `analytics-mcp@latest` | Pin to specific version: `analytics-mcp@2.1.0` |168169### Governance notes170- Approved-list result: <approved, exception required, unavailable>171- Secrets moved to environment variables: <yes/no/list>172```173174## Quality gate175176- [ ] `.mcp.json` or the supplied config was parsed successfully, or the parse blocker is reported.177- [ ] All `mcpServers` entries were enumerated.178- [ ] The full config was scanned for `SECRET_PATTERNS` including `API_KEY`, `MY_API_KEY`, `DB_URL`, and `DATABASE_URL` values.179- [ ] Each server’s `command`, `args`, and `env` were checked for `DANGEROUS_PATTERNS`.180- [ ] `@latest`, unpinned packages, and `npx` without `-y` are reported.181- [ ] Findings include severity, server, evidence, and fix.182- [ ] Approved-list checks are performed when policy data is available, or explicitly marked unavailable.183184## References185186- [MCP Specification](https://modelcontextprotocol.io/)187- [Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit)188- [OWASP ASI-02: Insecure Tool Use](https://genai.owasp.org/)
Run npx skillmds@latest add paulasilvatech/mcp-security-audit in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Audits MCP server configurations such as .mcp.json for hardcoded secrets, dangerous shell patterns, unpinned dependencies, unsafe npx usage, unapproved servers, and governance risks. Use this skill when asked to check MCP security, audit MCP servers, review .mcp.json, validate server args, detect shell injection, or verify environment-variable based credentials. It is listed under AI & ML on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
paulasilvatech (@paulasilvatech) published this skill. Their other Agent Skills are listed on their SkillMD profile.