Blog · Open-Source Guide
MCP server security for open-source maintainers: what reviewers check in 2026
The Anthropic Skills Directory now requires a security review before listing. If you maintain an open-source MCP server, you will eventually need to pass this review — or have your users run SkillAudit before trusting your server enough to install it. This guide walks you through what reviewers actually look for, why servers get rejected, and how to write the security documentation that prevents 80% of rejections before they reach a human reviewer.
Why open-source MCP servers get rejected
The Anthropic Skills Directory review process is not purely adversarial — reviewers are not trying to fail your server, they're trying to give users a trust signal. But in 2026, with over 36.7% of community MCP servers containing SSRF vulnerabilities and 43% with unsafe command-exec paths, the bar for listing has risen sharply.
The most common rejection reasons, in order of frequency:
- No SECURITY.md — reviewers can't tell whether security issues have been considered at all. This is a process signal that makes reviewers look harder at the code.
- Tool parameters accepted into shell commands without sanitization — command injection via
exec()or template literal interpolation into shell strings. This is the fastest way to a hard rejection. - Credential leakage in debug output — environment variables or API keys logged at startup, echoed in error messages, or returned in tool responses.
- Overly broad permission request — requesting filesystem access when you only need to read one specific path, or requesting network access when you only call one known endpoint.
- No runnable example in README — this is a documentation failure that signals the server hasn't been fully tested end-to-end.
The fastest path to a listing: Run npx skillaudit scan github:your-org/your-server before submitting. A SkillAudit grade of B or higher on all six axes clears most rejection criteria before a human reviews your code.
The six review axes — what each one checks
SkillAudit grades on the same axes the directory review uses. Understanding what each axis looks for helps you prioritize your pre-submission hardening.
| Axis | What the reviewer checks | Common failure | Min grade to list |
|---|---|---|---|
| Security | SSRF, command injection, prompt injection via tool parameters, path traversal, unsafe eval/exec | User-controlled input flows into exec() or fetch() without validation |
C |
| Permissions hygiene | Declared permissions vs permissions actually used in code | Requests fs:read:* but only reads one config file |
B |
| Credential exposure | Env var logging, token echoing in errors, secrets in responses | console.log(process.env) at startup; secret in stack trace |
B |
| Maintenance | Last commit date, open CVEs in dependencies, npm audit score | Critical or high CVEs in node_modules; last commit > 6 months ago |
C |
| Client compatibility | Works on Claude Code, Cursor, Windsurf, Codex; transport declared in config | Hardcoded stdio transport without SSE fallback; no mcp.json |
B |
| Documentation | README with runnable example, SECURITY.md with disclosure process, CHANGELOG | No SECURITY.md; README shows installation but not a complete working example | C |
The SECURITY.md that satisfies reviewers
A missing SECURITY.md is a hard signal to reviewers that security is an afterthought. A good SECURITY.md is not long — the purpose is to prove that you have a disclosure process, that you take reports seriously, and that users know what to do if they find a vulnerability.
Here is a minimal template that covers every point a reviewer looks for:
# SECURITY.md
## Supported versions
| Version | Supported |
|---------|-----------|
| 1.x | ✅ |
| < 1.0 | ❌ |
## Reporting a vulnerability
**Do not open a public GitHub issue for security vulnerabilities.**
Email security@your-domain.com with subject "SECURITY: <brief description>".
If you don't get a response within 72 hours, open a GitHub issue with the
title "SECURITY: please check email" (no details in the issue body).
We will:
- Acknowledge receipt within 24 hours
- Provide an initial assessment within 5 business days
- Notify you when a fix is released
We follow a 90-day coordinated disclosure timeline.
## Security model
This MCP server:
- Accepts tool calls via stdio transport only (no network listener)
- Reads from ${ALLOWED_PATH} only; no write operations
- Makes outbound requests to api.example.com only
- Does not log or persist tool parameter values
## Known limitations
- Tool parameters are validated for type and range but are not
cryptographically authenticated — the trust boundary is the MCP client.
- This server is designed for single-user environments. Multi-tenant
deployment requires additional isolation not provided by default.
## Dependency scanning
Run `npm audit` for the current advisory status of this package's
dependencies. We update dependencies on a monthly cadence and immediately
for critical CVEs.
The security model section is the most valuable part. Reviewers need to verify your declared security boundaries against the code. The clearer your model, the faster the review. "This server reads from $ALLOWED_PATH only" tells a reviewer exactly where to look and what to verify — instead of reading every file access in the codebase.
The CONTRIBUTING.md security section
Most MCP servers have a CONTRIBUTING.md that tells contributors how to run tests and submit PRs. Very few have a security section. A security section in CONTRIBUTING.md serves two purposes: it tells contributors what patterns are forbidden, and it signals to reviewers that security requirements are enforced at the contribution level, not just at the maintainer's discretion.
Add this section to your CONTRIBUTING.md:
## Security requirements for contributions
All PRs to this project must meet the following requirements.
The CI pipeline runs SkillAudit on every PR; a grade below B on any
security axis will block merge.
### Forbidden patterns (auto-rejected by CI)
**Command injection surface:**
- `exec()`, `execSync()`, or `spawn()` with a template literal string
that incorporates any function parameter value
- `eval()` or `Function()` constructor with any external input
**SSRF surface:**
- `fetch()`, `axios.get()`, `http.request()` where the URL or hostname
incorporates any function parameter value without allowlist validation
**Credential exposure:**
- `console.log()` calls that reference `process.env`, `config`, or
any variable name containing `key`, `token`, `secret`, or `password`
- Stack traces returned directly to MCP tool callers
**Path traversal:**
- `fs.readFile()`, `fs.readFileSync()`, `require()` where the path
incorporates any function parameter value without `path.resolve()` +
startsWith(ALLOWED_BASE_DIR) validation
### Required for any new tool
1. Parameter validation using `zod` or equivalent schema validation
before any logic runs
2. A unit test that verifies the tool returns an error (not throws)
when given a parameter designed to trigger the forbidden patterns above
3. Documentation of what resources the tool accesses, in the tool's
description string — not just in the README
### Security review process
For changes that affect authentication, permission boundaries, or
external communication, request a security review by adding the label
`security-review` to your PR. A maintainer will review with SkillAudit's
full scan output before merging.
Pre-submission self-assessment
Before you submit to the Anthropic Skills Directory, run through this checklist. Every item marked as a rejection risk below has historically caused directory submissions to be declined.
grep -rn "exec(\|execSync(\|spawn(" src/. For every hit, verify the argument is a hardcoded array (not a string template). If any argument incorporates a function parameter, rewrite it using execFile() with an array arg.
grep -rn "fetch(\|axios\." src/. For every hit where the URL includes a parameter value, verify there is an allowlist check before the call. Regex-matching a hostname is not sufficient — use an explicit new URL() parse and compare hostname against a static allowlist.
grep -rn "console.log\|console.error" src/ | grep -i "env\|config\|key\|token\|secret". Remove any log statement that could print a credential. Use structured logging that explicitly excludes sensitive fields.
mcp.json or server manifest, review every permission you declare. For each one, grep for the corresponding API call and verify it is actually used. Remove any permission that has no matching call site. Reviewers diff declared vs used.
@modelcontextprotocol/sdk, the zod schema on the tool definition is the place to do this. A tool that accepts string and passes it directly to a downstream call is a prompt-injection surface.
ls SECURITY.md. If missing, add it using the template above. Reviewers check for this file in the first pass.
npm audit --audit-level=high. Any critical or high severity finding in your dependency tree will trigger a maintenance axis failure. If you have unresolvable findings, document them in SECURITY.md under "Known limitations."
mcp.json file must declare the transport type (stdio or sse) and the command or endpoint. Client compatibility testing requires a machine-readable config. Without it, automated compatibility checks fail.
Getting a SkillAudit badge before submission
The most practical pre-submission action is to run SkillAudit on your own server and use the grade report to guide your hardening. The report is public by default for open-source repos, which means you can link to it in your directory submission — reviewers see the same report you see and can verify your fixes in the history.
A SkillAudit badge in your README is also a trust signal for users. When someone finds your server in a search result or awesome-mcp list, the badge tells them you have met a minimum security bar before they have to read the code themselves.
[](https://skillaudit.dev/report/github/your-org/your-server)
If your server earns an A grade on Security and Permissions Hygiene, the badge is green. B is blue. C is yellow. D or F is red. Most users on Anthropic's communities have started treating a green or blue badge the same way they treat Snyk's badge — as evidence the maintainer cares about the supply chain.
What happens after you submit
The Anthropic Skills Directory review process has three stages:
- Automated scan — their tooling runs a set of static checks similar to what SkillAudit does. This takes minutes. If you get a rejection at this stage, the feedback will reference specific files and lines. Fix those and resubmit — automated rejections are fast to address.
- Human review — a reviewer reads your README, SECURITY.md, and the tool handler implementations. This is where they check whether your declared security model matches what the code actually does. The SECURITY.md security model section you wrote earlier is exactly what they read here.
- Listing decision — if both stages pass, your server is listed. The listing includes your SkillAudit grade if you've run a scan. If you get rejected at the human review stage, you receive specific feedback items. Each one maps to one of the six axes. Fix the items and resubmit.
The average time from submission to decision is 3–5 business days when the automated scan passes. Servers that fail the automated scan and resubmit typically add 5–7 days per resubmission cycle. Running SkillAudit before submission is the fastest way to compress that cycle to a single submission.
Maintaining your listing over time
A listing is not permanent. The directory re-scans listed servers monthly. Your server will be de-listed if:
- A critical CVE is introduced in your dependency tree and not remediated within 30 days
- Your last commit is more than 12 months ago (maintenance axis)
- A user reports a security issue that is reproducible and not addressed within 90 days
- Your security model declaration (in SECURITY.md or README) is found to be inaccurate
The easiest way to maintain your listing is to set up SkillAudit's GitHub Action CI gate — it runs on every PR and will catch grade regressions before they reach your default branch. Combined with Dependabot for dependency updates, the monthly re-scan becomes a non-event rather than an emergency.
Summary: the pre-submission checklist in one place
Here is every action item from this guide, in priority order:
- Add SECURITY.md with disclosure process and security model
- Add security requirements section to CONTRIBUTING.md
- Grep for
exec(,execSync(,spawn(— verify no string template interpolation of parameter values - Grep for
fetch(,axios.— verify URL construction uses an explicit allowlist - Grep for
console.lognear env/config/key/token/secret — remove or filter - Audit declared permissions vs used permissions — remove unused
- Add zod schema validation to every tool handler
- Add a complete, runnable example to README
- Run
npm audit --audit-level=high— resolve or document findings - Verify
mcp.jsonexists with transport declaration - Run SkillAudit scan — resolve findings until Security and Permissions Hygiene reach B or above
- Add SkillAudit badge to README
- Submit to Anthropic Skills Directory
This order matters. Items 1–4 are rejection criteria; items 5–10 are warning criteria; items 11–13 are submission logistics. Fix in order and you'll clear the automated scan on the first pass.
If you're not sure whether a specific pattern in your codebase is a rejection risk, run the SkillAudit scan and look at the Security axis detail. The report shows exactly which file:line triggered the finding and what the remediation is. For open-source repos the scan is free with no account required.