Topic: mcp server security controls
MCP server security controls — 15 controls mapped to OWASP, NIST, and SOC 2
A compliance-ready control framework for enterprise MCP server adoption: fifteen discrete technical and procedural controls, each mapped to OWASP API Security or LLM Apps Top 10, NIST SP 800-53, and SOC 2 Trust Service Criteria, with implementation guidance and a rating for what's automatically detectable versus what requires manual review.
TL;DR
Enterprise adoption of MCP servers requires a control framework — not just a "looks safe" eyeball review. The fifteen controls below cover the full threat surface of MCP servers: outbound request safety, credential isolation, command parameterization, prompt-injection resistance, least-privilege scope, dependency hygiene, disclosure process, CI gate enforcement, and re-audit cadence. Ten of the fifteen are automatically detectable via a scanner like SkillAudit; five require procedural controls that the scanner evidence supports. Each control is mapped to at least one recognized compliance framework so you can cite it in a SOC 2 audit or an internal security review.
Why a control framework, not just a best-practices list
A "best practices" list is good for authors. A control framework is what security engineers, compliance officers, and team leads need when they're accountable for the outcome. A control has three parts that a best-practice tip typically omits: a testable definition of what "implemented" means, a mapping to a recognized framework that justifies the control's existence to an auditor, and a stated detection method that produces auditable evidence. Without those three parts, a "use an allowlist for outbound requests" recommendation is advice. With them, it's Control MCP-05: URL Allowlist Enforcement — tested by static AST scan for fetch() call sites, mapped to OWASP API5 (Broken Function-Level Authorization), producing evidence in the form of a SkillAudit grade report for every server in the approved inventory.
The fifteen controls below were derived from SkillAudit's review of 101 of the most-installed MCP servers, the OWASP API Security 2023 and LLM Apps 2025 Top 10, NIST SP 800-53 Rev 5, and SOC 2 Trust Service Criteria. Each control addresses a finding class that appears in the corpus; none is hypothetical.
The control framework
| Control ID | Control name | Frameworks | Detection | SkillAudit axis |
|---|---|---|---|---|
| MCP-01 | Outbound URL allowlist | OWASP API5, NIST SC-7 | Automated (static) | Security |
| MCP-02 | DNS rebinding protection | OWASP API5, NIST SC-7 | Automated (static) | Security |
| MCP-03 | Credential isolation (no log echoing) | OWASP API8, NIST IA-5, SOC 2 CC6 | Automated (static) | Credentials |
| MCP-04 | Typed config objects (no raw env dump) | OWASP API8, NIST IA-5 | Automated (static) | Credentials |
| MCP-05 | Parameterized command execution | OWASP API3, NIST SI-10 | Automated (static) | Security |
| MCP-06 | Prompt injection resistance | OWASP LLM01, NIST SI-10 | Automated (LLM probe) | Security |
| MCP-07 | Least-privilege scope declaration | OWASP API1, NIST AC-6, SOC 2 CC6 | Automated (static) | Permissions |
| MCP-08 | Scope-vs-handler drift detection | OWASP API1, NIST AC-6 | Automated (static) | Permissions |
| MCP-09 | Dependency advisory monitoring | OWASP API8, NIST SI-2, SOC 2 CC7 | Automated (SCA) | Maintenance |
| MCP-10 | Dependency pinning and lock files | NIST SA-12, SOC 2 CC8 | Automated (static) | Maintenance |
| MCP-11 | Active maintainer and commit recency | NIST SA-12, SOC 2 CC9 | Automated (metadata) | Maintenance |
| MCP-12 | Vulnerability disclosure policy (SECURITY.md) | NIST IR-6, SOC 2 CC7 | Automated (metadata) | Documentation |
| MCP-13 | Pre-publish security audit (min Grade B) | NIST SA-11, SOC 2 CC8 | Procedural (audit record) | All axes |
| MCP-14 | CI/CD scan gate on every PR | NIST SA-11, SOC 2 CC8 | Procedural (CI log) | All axes |
| MCP-15 | Quarterly re-audit and grade regression alert | NIST CA-7, SOC 2 CC7 | Procedural (audit history) | All axes |
Ten controls (MCP-01 through MCP-12) are automatically detectable by static analysis and metadata checks — meaning a scanner run produces the evidence record. Five controls (MCP-13 through MCP-15) are procedural — meaning the scanner audit report is the evidence record, but the control is implemented by the team's process of running the scanner, not by the server's code.
Control-by-control implementation guidance
MCP-01 — Outbound URL allowlist
What "implemented" means: Every fetch(), axios.get(), got(), and equivalent outbound HTTP call site uses a validated URL that has been checked against an explicit allowlist of permitted domains or prefixes before the request is made. The allowlist is a named constant or config value, not inline logic. Common failure pattern (corpus, 50% of SSRF-susceptible servers): a url or endpoint parameter taken directly from a tool argument is passed into a fetch call without validation. Verification: SkillAudit static pass flags every call site where a user-controlled variable reaches a fetch call without an intervening validation check; result is file-and-line cited in the audit report.
MCP-02 — DNS rebinding protection
What "implemented" means: The URL allowlist is enforced after DNS resolution, not before — i.e., the server validates that the resolved IP address is not in RFC 1918 private ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x), the 169.254.x.x IMDS range, or loopback (127.x.x.x / ::1) after each DNS lookup, not just at URL-parse time. Why post-resolution matters: an attacker who controls a DNS server can return a benign IP at parse time and a private-range IP at fetch time, bypassing a pre-resolution blocklist entirely. An allowlist that explicitly names permitted domains is not vulnerable to this if the server validates the resolved IP; a blocklist that blacklists private ranges at URL parse time is. Verification: static check for IP validation logic after DNS resolution calls.
MCP-03 — Credential isolation (no log echoing)
What "implemented" means: No log statement, error serializer, or debug output pathway writes any environment variable, API key, token, connection string, or bearer credential to stdout, stderr, or a log sink. Typed config objects strip secret fields before any logging. Common failure pattern (corpus, 38%): console.log(process.env) in a startup log, or an error serializer that includes the full config struct including connection strings. Verification: SkillAudit static pass flags process.env in log calls and config objects passed to serializers without redaction.
MCP-04 — Typed config objects (no raw env dump)
What "implemented" means: The server reads environment variables once at module init into a typed config object that exposes only the fields required by tool handlers. process.env.SECRET_KEY is consumed once; the config object exposes only config.apiKey with a get accessor that returns a masked representation for any logging purpose. Tool handler code never calls process.env directly. Why this matters: if a tool handler has a prompt-injection vulnerability that causes it to log its arguments, a handler that calls process.env.DATABASE_URL directly leaks that credential; a handler that reads from a typed config object with a non-loggable accessor does not.
MCP-05 — Parameterized command execution
What "implemented" means: Every exec(), spawn(), execSync(), spawnSync(), or subprocess.run() call passes user-controlled arguments as an argument array to the spawn call, not as interpolated strings to a shell. spawn('git', ['clone', userUrl]) is safe; exec(`git clone ${userUrl}`) is not — a user who controls userUrl can inject https://example.com; rm -rf /. Corpus prevalence: 43% of servers that execute shell commands had at least one unsafe interpolation path. Verification: static check for template literal or string concatenation inside exec/spawn call sites.
MCP-06 — Prompt injection resistance
What "implemented" means: When the server's tool responses include content fetched from a third-party service or user-supplied data, the agent processing those responses does not execute embedded adversarial instructions. This is partially a property of the server (how it structures tool output), partly a property of the surrounding agent's system prompt hardening. Why it requires active probing: static analysis cannot determine whether a given tool output structure is injection-susceptible without running a model against it. SkillAudit's 14-probe bank runs the server in a sandboxed environment and submits adversarial tool responses designed to trigger a range of injection patterns. Note on stability: this score can regress when the underlying model retrains, even without code changes — see MCP-15 re-audit control.
MCP-07 — Least-privilege scope declaration
What "implemented" means: The server's capability manifest (SKILL.md for Claude Skills, JSON-RPC tool definitions for MCP servers, OAuth scope list for OAuth-using servers) requests only the capabilities and permissions that the documented tool set requires. Common failure pattern (corpus, 25% of OAuth-using servers): a server with only read-only documented tools requests write or admin OAuth scopes. Verification: SkillAudit compares declared capabilities against the handler implementations detected in the static pass; a mismatch flags as scope-vs-handler drift.
MCP-08 — Scope-vs-handler drift detection
What "implemented" means: The declared capability manifest accurately reflects what the handlers actually do — if the manifest declares filesystem:read, the handlers must not call fs.writeFile. Drift in either direction is a control failure: over-declaring (claiming write when only read is needed) creates unnecessary exposure; under-declaring (claiming read but actually writing) means the operator cannot enforce the principal-of-least-privilege assumption. Implementation note: this control pairs with MCP-07 but is distinct — MCP-07 catches over-broad requests; MCP-08 catches mismatches between what's declared and what's implemented.
MCP-09 — Dependency advisory monitoring
What "implemented" means: The server's direct and transitive dependency tree is monitored against public advisory feeds (GitHub Advisory Database, OSV, NVD) and the operator receives notification within 48 hours of a new CVE affecting a dependency in the server's production dependency tree. Implementation options: Dependabot on the server's GitHub repo, npm audit / pip-audit in CI, OSV-Scanner as a scheduled job, or SkillAudit's dependency-advisory signal in the audit report. Evidence: a scan report with no open advisories above medium severity, or a documented and time-bounded exception for known advisories.
MCP-10 — Dependency pinning and lock files
What "implemented" means: The server's package manifest pins exact versions of direct dependencies (using package-lock.json, yarn.lock, poetry.lock, or pipfile.lock), and the lock file is committed to the repository. Pointing at a floating range (^1.2.0) or a GitHub branch without a commit SHA introduces non-reproducible builds where a dependency update can introduce a vulnerability without any change to the server's own code.
MCP-11 — Active maintainer and commit recency
What "implemented" means: The server has a named maintainer who has made at least one commit in the last 6 months, responds to issues (median issue response time under 30 days), and has not explicitly archived the repository. Why this matters for a control: an unmaintained server will not receive security patches for future vulnerabilities; approving it for production use without a maintenance exit plan creates a control gap that grows over time.
MCP-12 — Vulnerability disclosure policy (SECURITY.md)
What "implemented" means: The server's repository contains a SECURITY.md (or equivalent) that documents: a contact address for reporting vulnerabilities, a response time commitment (e.g., "we will acknowledge within 48 hours"), and a disclosure timeline (e.g., "we will publish an advisory within 90 days of a confirmed finding"). Why this is a control and not just a nice-to-have: without a disclosure policy, a researcher who finds a vulnerability in a server your team uses has no established channel — they may publish immediately, leaving you with no advance notice to patch or remove the server.
MCP-13 — Pre-publish security audit (min Grade B)
What "implemented" means: Every MCP server or Claude Skill published to a public directory or approved for team use has a SkillAudit report on record with a grade of B or above before publication or before the first team deployment. The report is linked from the server's README or from the team's internal MCP inventory. Evidence: the public audit URL (e.g., https://skillaudit.dev/audits/[server-name]) in the server's documentation or the team's approved-server list.
MCP-14 — CI/CD scan gate on every PR
What "implemented" means: A CI check runs SkillAudit (or equivalent) against the server's codebase on every pull request that modifies the server's code; a grade below the configured threshold fails the check and blocks the merge. The threshold is configured as a policy (e.g., "minimum Grade B on security and credentials axes; Grade C acceptable on compatibility axis with documented exception"). Implementation: SkillAudit's GitHub Action integration provides a skillaudit/scan-action that runs in CI and posts the grade as a check result.
MCP-15 — Quarterly re-audit and grade regression alert
What "implemented" means: Every server in the team's approved inventory is re-audited at least quarterly, and the operator has subscribed to grade-change notifications so that a regression from B to C or from A to B triggers an alert and review. Why prompt-injection makes this control non-optional: a server's prompt-injection susceptibility score can change without a code change when the underlying Claude model retrains — a server that was A on prompt-injection in Q1 may score B in Q2 on the same code, because the model's behavioral guardrails have changed. Quarterly re-audits catch this class of regression that no code-review process would detect.
Evidence map for SOC 2 Type II and ISO 27001
If you're writing a SOC 2 Type II or ISO 27001 control statement for MCP server security, the evidence you need for each control class is:
- Technical controls (MCP-01 through MCP-12): SkillAudit audit report for each server in the approved inventory, showing passing grade on the relevant axes. Report includes file-and-line citations for any findings; a clean report with no findings is the evidence of absence.
- Procedural controls (MCP-13 through MCP-15): Audit history log showing the date each server was audited, the grade at each audit, and the reviewer who approved the server for the inventory. SkillAudit's Team plan includes an audit log export for this purpose.
- SOC 2 CC6 (Logical and Physical Access Controls): MCP-03, MCP-04, MCP-07, MCP-08 are the most directly relevant; the audit reports for these axes provide the evidence for access-control related trust service criteria.
- SOC 2 CC7 (System Operations): MCP-09, MCP-12, MCP-15 are most relevant; advisory-monitoring records and re-audit logs provide the ongoing-monitoring evidence.
- SOC 2 CC8 (Change Management): MCP-13, MCP-14 are most relevant; pre-publish audit records and CI gate logs provide the change-management evidence.
Where this page sits in the cluster
This page covers the control framework layer — discrete controls for teams that need formal documentation. Sibling pages cover adjacent questions:
- MCP server security considerations — the decision-layer checklist for team leads evaluating community servers before approving them for deployment.
- MCP server security review — what a review report looks like, who performs them, how to read the grade.
- MCP server security OWASP mapping — the full category-by-category OWASP API Security 2023 and LLM Apps 2025 mapping to the MCP threat surface that underlies the framework mappings above.
- MCP server security best practices — the 12-rule author playbook; controls MCP-01 through MCP-12 map directly to rules in this playbook.
How SkillAudit covers these controls
SkillAudit provides automated detection for ten of the fifteen controls in this framework. A full audit (10–90 seconds per server) produces a graded report with per-axis sub-scores, file-and-line citations for every finding, and remediation hints. The Team plan adds: a JSON policy export that maps minimum-grade thresholds to the controls above; a GitHub Action (skillaudit/scan-action) that implements MCP-14 as a CI gate; email alerts for grade regressions that implement MCP-15; and an audit history log for SOC 2 / ISO 27001 evidence. For the five procedural controls (MCP-13 through MCP-15), the audit report and audit history log are the evidence records that satisfy the control — the process of running the scanner is the control implementation.
Related questions
Can I use this framework as-is for a SOC 2 audit, or does it need adaptation?
The framework above provides the control IDs, framework mappings, and evidence requirements that map to SOC 2 Trust Service Criteria. In practice, your auditor will ask you to translate these into your organization's control language and show evidence from your specific tool set. The SkillAudit audit report and history log are acceptable as evidence for the automated controls; the procedural controls need your internal process documentation (e.g., "our policy is: no MCP server reaches production without a SkillAudit report on record showing Grade B or above — see audit log export"). The framework gives you the structure; your internal documentation connects it to your specific environment.
Which controls are highest priority if we can only implement five first?
MCP-01 (URL allowlist), MCP-03 (credential isolation), MCP-05 (parameterized commands), MCP-07 (least-privilege scope), and MCP-13 (pre-publish audit) give you coverage of the four finding classes that produce the most severe confirmed-exploitable findings in the corpus — SSRF (50%), credential exposure (38%), command injection (43%), and scope drift (25%). A scan-before-install policy (MCP-13) ensures you catch all five of the automated controls simultaneously for every new server, making it the highest-leverage procedural control. Add MCP-15 (re-audit cadence) as a sixth if prompt-injection regression risk concerns you.
How does this control framework relate to the NIST AI RMF?
NIST AI RMF (AI Risk Management Framework) is a top-level governance structure for AI systems, not a technical control catalog. The controls in this framework are the technical implementation layer beneath NIST AI RMF's "Govern," "Map," "Measure," and "Manage" functions. MCP-13 through MCP-15 implement the "Measure" function (regular testing and re-evaluation); MCP-01 through MCP-12 implement the "Manage" function (applied controls that reduce identified risks); the inventory and evidence processes implement the "Map" and "Govern" functions. NIST AI RMF doesn't specify technical controls — this framework does.
What about MCP servers built internally — do these controls still apply?
Yes, and arguably with higher priority than for community servers. Internal MCP servers often have access to a broader credential footprint (internal APIs, databases, SSO tokens, CI/CD secrets) and don't benefit from the public scrutiny that catches obvious bugs in open-source packages. All fifteen controls apply to internal servers; MCP-12 (SECURITY.md) is adapted to require an internal contact rather than a public disclosure email; MCP-11 (active maintainer) becomes "does this server have a named owner on-call" rather than public commit recency.
Is Grade B the right minimum threshold, or should we require Grade A?
Grade B means no findings with confirmed-exploitable severity on the security and credential axes, with at most one medium-severity finding on a lower-weight axis. Grade A means no findings above low-severity on any axis. For most enterprise use cases, Grade B is the right threshold for general approval — it clears the confirmed-exploitable findings while allowing servers that have a minor documentation gap or a medium-severity best-practice deviation. Grade A is appropriate for servers with access to production credentials or broad filesystem access. The per-axis threshold approach (Block D/F on security/credentials regardless of overall grade; allow C with documented exception on maintenance/compatibility) is more nuanced than a single letter cutoff and is what SkillAudit's Team plan policy export supports.
Further reading
- MCP server security OWASP mapping: The full OWASP API Security 2023 and LLM Apps 2025 category-by-category mapping to MCP threat surfaces — the framework source for the OWASP citations above.
- MCP server security considerations: The team-lead decision checklist — same threat classes, framed as evaluation questions rather than formal controls.
- Public scan results — field data from 101 MCP servers: The corpus data that validates the prevalence numbers cited in each control's implementation guidance.
- MCP server security issue triage: What to do when a control fails and a security issue is found — the incident response counterpart to this control-implementation guide.