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.

2026-06-12 · 12 min read · Open-Source, Security, MCP

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:

  1. 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.
  2. 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.
  3. Credential leakage in debug output — environment variables or API keys logged at startup, echoed in error messages, or returned in tool responses.
  4. 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.
  5. 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.

REJECTION RISK: User-controlled input in shell commands. Search your codebase: 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.
REJECTION RISK: User-controlled URL components in fetch calls. Search: 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.
REJECTION RISK: Credential leakage in startup logs. Search: 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.
!
WARNING: Overly broad permissions. In your 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.
!
WARNING: No zod/ajv validation on tool parameters. Every tool handler should validate its input schema before any logic runs. If you're using the @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.
REQUIRED: SECURITY.md present. Run ls SECURITY.md. If missing, add it using the template above. Reviewers check for this file in the first pass.
REQUIRED: Runnable example in README. The README must include a code block that shows a complete, working invocation — not just installation instructions. The test is: could a reviewer clone the repo, follow only the README, and run one tool call that produces a real response? If not, the documentation axis will fail.
REQUIRED: npm audit passes at high severity. Run 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."
REQUIRED: Transport declared in mcp.json. The 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.

[![SkillAudit Grade](https://skillaudit.dev/badge/github/your-org/your-server)](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:

  1. 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.
  2. 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.
  3. 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:

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:

  1. Add SECURITY.md with disclosure process and security model
  2. Add security requirements section to CONTRIBUTING.md
  3. Grep for exec(, execSync(, spawn( — verify no string template interpolation of parameter values
  4. Grep for fetch(, axios. — verify URL construction uses an explicit allowlist
  5. Grep for console.log near env/config/key/token/secret — remove or filter
  6. Audit declared permissions vs used permissions — remove unused
  7. Add zod schema validation to every tool handler
  8. Add a complete, runnable example to README
  9. Run npm audit --audit-level=high — resolve or document findings
  10. Verify mcp.json exists with transport declaration
  11. Run SkillAudit scan — resolve findings until Security and Permissions Hygiene reach B or above
  12. Add SkillAudit badge to README
  13. 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.