Developer Guide · Documentation
How to write a SkillAudit-ready SECURITY.md for your MCP server
A SECURITY.md file is one of the highest-leverage, lowest-effort improvements you can make to an MCP server before publishing. It takes 15–30 minutes to write, it directly improves your Documentation Completeness sub-score, and it tells buyers exactly what they need to know before trusting your server with production credentials. This guide explains section by section what SkillAudit's scanner checks, what reviewers look for, and provides a complete template you can adapt for any MCP server.
Why SECURITY.md matters more for MCP servers than for ordinary libraries
A typical open-source library SECURITY.md covers one thing: how to report a vulnerability. That's its whole job, and a three-line file is usually fine.
MCP servers are different. An MCP server isn't a passive library — it's a process that runs on an engineer's laptop (or your CI runner), holds OAuth tokens and API keys, executes code on behalf of an LLM, and makes outbound network requests to upstream services. Before anyone installs it with claude plugin install github:your-org/your-mcp-server, they have legitimate questions that a README doesn't answer:
- What credentials does this server actually need, and what scope?
- Does it log anything? Does that include the tool arguments an LLM passes to it?
- What happens if the upstream API returns malicious content?
- How fast will you patch it if a vulnerability is reported?
- Has it ever been audited by a third party?
A SECURITY.md that answers these questions converts an unknown into a known. It's not security theater — it's the artifact that turns "I found this on GitHub" into "I know what I'm installing."
How SkillAudit scores it
SECURITY.md is evaluated as part of the Documentation Completeness sub-score (out of 100). A missing SECURITY.md is a 12-point deduction from that sub-score. A present but thin SECURITY.md (missing required sections) earns partial credit. A complete, well-structured SECURITY.md can add up to 18 points to Documentation Completeness. Since Documentation Completeness is one of six sub-scores that roll into your letter grade, the compound effect on your overall grade can shift you by an entire letter in a mid-range score band (C→B or B→A).
The six sections SkillAudit checks
SkillAudit's scanner looks for six named sections in your SECURITY.md. It identifies them by heading keyword matching — so the exact heading text is flexible, but the concepts must be present. Here's what each section needs to contain and why.
List every tool your server registers, with a one-line description of what it does and what system it touches. This sounds obvious, but most SECURITY.md files omit it entirely — leaving buyers to infer the attack surface from tool names alone.
The scanner checks that this section exists and contains tool names that match the server's actual registration list. Discrepancies (a tool in the code that isn't mentioned in SECURITY.md) are flagged as a documentation gap finding, not a security finding, but they signal to buyers that the SECURITY.md is stale.
What to include: tool name, one-line description, what upstream system it calls, whether the call is read-only or write, and any tool arguments that are passed directly to an external system without validation.
List every credential your server requires, the minimum OAuth scope or IAM permission needed for it to function, and what an over-scoped credential can do. This is the most-read section for team buyers running your server in a corporate environment.
The scanner checks for the presence of this section and for signals that you've documented scope specificity: phrases like "read-only", minimum required scopes, or named IAM permissions. A section that says only "Requires a GitHub token" without specifying scope earns partial credit.
What to include: environment variable name, credential type (OAuth token, API key, service account), minimum scope or permission required, what a compromised or over-scoped credential can reach, and whether the server stores or logs the credential anywhere.
This is the classic SECURITY.md section: how should a researcher report a vulnerability? For MCP servers, the stakes are higher than for ordinary libraries because a SSRF or prompt-injection finding in an MCP server can be exploited immediately against users who've already installed it.
The scanner checks for an email address or private disclosure mechanism (GitHub private security advisories, HackerOne, etc.) and for a response time commitment. If your SECURITY.md says "open a GitHub issue" for security reports, that's treated as a missing section — public disclosure before a patch is a non-starter.
What to include: a private email address or GitHub Security Advisories link, a committed response time (acknowledge within 48h is the standard), your target patch window for critical findings (7 days is reasonable), and whether you publish CVEs after patching.
What does your server log? Does it log tool arguments? Does it log the content of responses from upstream APIs? In a typical MCP deployment, tool arguments contain whatever context the LLM assembled from the user's conversation — which may include PII, internal URLs, authentication tokens, or confidential business data. Buyers need to know whether your server might log any of this.
The scanner checks for the presence of this section and for explicit statements about what is or isn't logged. "We do not log tool arguments" is the ideal statement. "We log request metadata for debugging" is acceptable if you clarify what metadata means. No section at all leaves buyers to infer from source code.
What to include: what you log (request timestamps, tool names called, response latencies), what you explicitly do not log (tool arguments, upstream API response content), whether logs are stored locally or transmitted to a remote service, and the retention period.
Every MCP server has a trust model: it trusts the LLM's tool call arguments to some degree, it trusts upstream API responses to some degree, and it may or may not sanitize content before returning it to the LLM. This section is where you document the security boundaries you've explicitly chosen not to harden, and why.
This might feel like an invitation to list your vulnerabilities — but it's actually the opposite. Documenting known limitations honestly signals that you've thought about the threat model. Buyers expect limitations. What they don't expect is discovering a limitation in production that you never disclosed.
What to include: whether upstream API responses are sanitized before being returned to the LLM (prompt injection protection), whether tool arguments are validated as strictly typed or treated as raw strings, any known behaviors that could be surprising in a multi-agent context, and any out-of-scope items (e.g., "we do not protect against a compromised LLM").
If your server has been independently audited — by SkillAudit, by a security firm, or by a knowledgeable community contributor — document it here. Link to the public report (or summary) and note what was found and fixed. Buyers making a decision about whether to trust a server give significant weight to independent review evidence.
This section is optional in the sense that its absence isn't penalized — but its presence can differentiate your server from semantically similar alternatives that have no review evidence. If your SkillAudit scan returned a grade of A or B, embedding the badge in README.md and linking to the report from this section is the highest-signal use of the audit you paid for.
The full SECURITY.md template
Copy this template, fill in the bracketed sections, and delete the parenthetical guidance after each heading. The comments in angle brackets are for your reference and should be removed before committing.
# Security Policy
## Scope — what this server exposes
This MCP server registers the following tools:
| Tool | Description | Upstream system | Read/Write |
|------|-------------|-----------------|------------|
| `tool_name_1` | One-line description | GitHub API | Read |
| `tool_name_2` | One-line description | Filesystem (scoped to working directory) | Write |
<!-- List every tool registered in src/index.ts or server.ts. -->
<!-- Note any tools that pass LLM-supplied arguments directly to external systems. -->
## Credentials and permissions
This server requires the following credentials:
**`GITHUB_TOKEN`** — GitHub personal access token or OAuth token
- Minimum required scope: `repo:read` (for read-only tools) / `repo` (if write tools are enabled)
- What a compromised token can reach: repositories accessible to the token owner
- Storage: loaded from environment at startup; never written to disk or logged
- The server does not store, cache, or transmit this token outside the authenticated API calls it makes on your behalf
<!-- If your server requires multiple credentials, add a block for each one. -->
<!-- Be specific about minimum scope — "admin" or "full access" is a red flag for buyers. -->
## Reporting a vulnerability
**Do not open a public GitHub issue for security vulnerabilities.**
To report a security issue, email **security@your-domain.com** (or use
[GitHub Private Security Advisories](https://github.com/your-org/your-repo/security/advisories/new)).
We will acknowledge your report within **48 hours** and aim to release a patch within
**7 days** for Critical/High findings, **30 days** for Medium findings.
We follow [coordinated disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure):
we will credit reporters by name (or pseudonym) in the release notes unless you request anonymity.
## Data handling and logging
This server logs the following at runtime:
- Request timestamps
- Tool names invoked (e.g., `list_repos`)
- Response status codes from upstream APIs
- Unhandled exception stack traces (to stderr)
**This server does NOT log:**
- Tool arguments passed by the LLM
- Content of upstream API responses
- User prompts or conversation context
- Authentication tokens or credential values
Logs are written to stderr (standard Claude Code behavior) and are not transmitted to any
remote service. No telemetry is collected.
## Known limitations and trust boundaries
**Prompt injection via upstream content:** This server returns upstream API responses
directly to the LLM without sanitizing for adversarial instruction content. If an upstream
API response contains prompt-injection text (e.g., a malicious GitHub issue body), the LLM
may act on it. This is a known limitation of the current implementation. Users in
high-sensitivity environments should review upstream content sources.
**Tool argument validation:** Tool arguments supplied by the LLM are validated for type
and basic format (required fields, string length). They are not validated for authorization
(whether the LLM has the right to perform the requested operation). Authorization is
delegated to the upstream API.
**Out of scope:** We do not protect against a compromised LLM or a compromised Claude
Code installation. Our threat model assumes the LLM runtime is trusted.
## Audit history
| Date | Auditor | Scope | Grade | Report |
|------|---------|-------|-------|--------|
| 2026-05-15 | SkillAudit | Full scan (Security, Permissions, Credentials, Maintenance, Compatibility, Documentation) | B+ | [Public report](https://skillaudit.dev/audits/your-server-id) |
<!-- Remove this section if your server hasn't been audited. -->
<!-- Add a row each time you get a new audit or re-scan after remediation. -->
## Supported versions
We provide security patches for:
| Version | Supported |
|---------|-----------|
| 2.x | ✅ Yes |
| 1.x | ⚠️ Critical only (until 2026-12-31) |
| < 1.0 | ❌ No |
The minimum viable SECURITY.md
If you need to pass the SkillAudit Documentation Completeness check quickly — a directory submission deadline is tomorrow, or you've got a C grade you want to lift before sharing a report — here is the shortest SECURITY.md that earns the required credit:
# Security Policy
## Scope
This server exposes [N] tools: [tool_1], [tool_2], [tool_3].
It calls [upstream API] on your behalf using the credential in `ENV_VAR_NAME`.
## Credentials
**`ENV_VAR_NAME`** — requires [minimum-scope] access to [what].
Never logged or stored beyond the in-process environment.
## Reporting vulnerabilities
Email security@[your-domain] — do not open a public issue.
We acknowledge within 48h and patch Critical findings within 7 days.
## Logging
We log tool names and timestamps. We do not log tool arguments or API response content.
This covers the four required and recommended sections at minimum length. It won't earn full Documentation Completeness credit, but it removes the 12-point SECURITY.md-absent deduction and partially addresses the required sections.
The 15-minute trap
The minimum viable SECURITY.md is for emergencies. A thin SECURITY.md that says only "email us for vulnerabilities" but says nothing about what credentials the server holds, what it logs, or what its known limitations are will be noticed by buyers doing due diligence. The full template above takes 30–45 minutes for a server you understand well. Spending that time once is worth it — a well-written SECURITY.md travels with every link share, every directory listing, and every audit report.
Seven SECURITY.md mistakes that drop your grade
Public issue = public disclosure before a patch exists. SkillAudit flags this as a missing vulnerability reporting section, not a partial one. Use GitHub Private Security Advisories or an email address instead.
"Requires a GitHub token with repo access" when only repo:read is actually needed. This is a Permissions Hygiene finding: you're requesting more scope than necessary, which increases the blast radius of a compromised credential. Document — and actually request — the minimum scope.
SkillAudit cross-references tool names in SECURITY.md against the server's actual tool registrations. A mismatch generates a documentation gap finding and signals to reviewers that the file is stale. Always update the Scope table when you add or rename tools.
If your server logs tool arguments for debugging and you don't say so in SECURITY.md, a buyer who discovers this in the source code will treat it as an undisclosed behavior. Disclose it. Explain why. If it's configurable, explain how to turn it off.
SkillAudit's Credential Exposure scan checks source code for patterns that echo or log credential values. If a code finding contradicts a SECURITY.md claim, the finding is escalated from a code issue to a trust-and-transparency issue, which affects your overall grade more severely.
"We patch all vulnerabilities within 24 hours" sounds good but if your commit history shows a 3-month average release cadence, the scanner will note the inconsistency. Set a timeline you can actually honor: 7 days for Critical is standard and credible for a solo maintainer.
You added a new tool that writes to the filesystem. You added a webhook that posts to a Slack channel. These change the scope section and potentially the credentials section. A SECURITY.md that hasn't been committed in 6 months while the codebase has changed is flagged as a Maintenance finding.
How SECURITY.md interacts with other SkillAudit sub-scores
SECURITY.md directly affects the Documentation Completeness sub-score, but its contents also interact with three other sub-scores in ways authors don't always anticipate.
Credentials sub-score: If your SECURITY.md credentials section documents minimum scope, and your actual OAuth implementation matches it, both sub-scores benefit. If the documented scope in SECURITY.md is broader than what the code actually requests, that's fine — it's conservative. If the code requests broader scope than SECURITY.md documents, that's a conflict finding.
Security sub-score: The "Known limitations" section of SECURITY.md is not used as an excuse to not fix issues. A server that acknowledges "we don't sanitize upstream API responses" in SECURITY.md but doesn't add a warning comment in the code will still receive a Security finding for the lack of sanitization. SECURITY.md documentation is additive to code-level mitigations, not a replacement for them.
Maintenance sub-score: SECURITY.md's most recent commit date is included in the maintenance timeline analysis alongside the most recent code commit, the last dependency update, and the last release. A SECURITY.md that hasn't been updated in 12 months when the rest of the codebase is active will generate a maintenance finding — the assumption is that something changed that should have updated the security documentation.
The SECURITY.md checklist before submitting to a directory
Before submitting your MCP server to Anthropic's Skills Directory, MCP Market, or any other listing that shows SkillAudit grades, run through this checklist:
- ☐ Every tool in the code appears in the Scope table
- ☐ Every credential in the code appears in the Credentials section with its minimum scope
- ☐ The vulnerability reporting section has a private email or GitHub Security Advisories link — not "open an issue"
- ☐ The logging section explicitly says what you do and don't log (especially tool arguments)
- ☐ The known limitations section mentions whether you sanitize upstream API response content
- ☐ No claim in SECURITY.md is contradicted by a finding in the SkillAudit report
- ☐ The SECURITY.md commit date is within 30 days of your most recent code commit
Run the scan first
The most effective workflow is: run a SkillAudit scan → read the Documentation Completeness section of the report → fix the specific gaps listed → commit → re-scan. The report will tell you exactly which sections are missing or thin, which is faster than checking the checklist above manually. The Free plan includes enough audits to use this workflow without a subscription.
What buyers look for beyond the scanner
A SECURITY.md that passes the scanner is a floor, not a ceiling. When a team buyer is evaluating whether to allow a community MCP server in their organization — and the security questionnaire comes out — there are three things they look at beyond the scanner grade:
Tone and specificity: A SECURITY.md written by someone who has actually thought about the threat model reads differently from one generated by an AI and committed without review. Specific minimum OAuth scopes, honest acknowledgment of prompt injection limitations, concrete patch timelines — these signal a maintainer who understands what they've built.
Consistency with the code: A buyer doing due diligence will skim the source code alongside SECURITY.md. If SECURITY.md says "we never log credentials" and the code has a debug block that logs process.env, the SECURITY.md is worse than no SECURITY.md — it's an active misrepresentation. Consistency matters more than completeness.
Evidence of updates after incidents: If your git log shows a security patch commit followed by a SECURITY.md update, that is strong positive evidence. It shows that your disclosure process is real, not aspirational. Buyers who've been burned by a supply chain compromise look specifically for this pattern.
For a detailed treatment of what procurement-level reviews cover beyond the scanner, see the vendor security questionnaire. For how a CISO-level review of your server maps SECURITY.md contents to organizational risk, see the CISO briefing. For the remediation path from a C grade (which often includes a missing SECURITY.md) to an A grade, see the week-by-week remediation plan.
FAQ
Does adding SECURITY.md alone change my letter grade?
It depends on where your Documentation Completeness sub-score sits and how much it drags your overall grade. For servers with strong Security, Permissions, and Credential scores but a weak Documentation score, adding a complete SECURITY.md can shift the overall grade by one letter. For servers with existing code-level findings, SECURITY.md improves the Documentation sub-score but doesn't address those findings — the remediation order matters. The C-to-A remediation plan gives the full prioritization order.
What if my server doesn't require any credentials?
Still include a Credentials section that explicitly says so: "This server requires no credentials. It does not make authenticated outbound API calls." That statement is itself valuable — it tells buyers the server can't exfiltrate credentials because it doesn't hold any. A server with no credentials section at all leaves the question open.
Does the scanner read the SECURITY.md content or just check for the file's presence?
It reads the content. The scanner checks for the presence of each named section (by heading keyword) and within each section checks for signal phrases that indicate content quality. A SECURITY.md with six headings but no body text under any of them earns near-zero credit. A SECURITY.md with two thorough sections earns more than one with six thin ones.
Can I use a SECURITY.md from a SECURITY.md generator?
Yes, with edits. Most SECURITY.md generators produce a solid Vulnerability Reporting section but omit the Scope, Credentials, Logging, and Known Limitations sections entirely — because those are specific to MCP servers and not covered by general-purpose templates. Start from a generator output, then add the four MCP-specific sections.
How often should I update SECURITY.md?
At minimum: whenever you add or remove a tool, whenever you add a new credential requirement, whenever you change what gets logged, and whenever you patch a security finding. In practice, adding a SECURITY.md update to your PR checklist for any non-trivial change ensures it stays current without requiring active maintenance effort.
Check your current Documentation Completeness score
Run a free SkillAudit scan to see exactly which sections are missing or thin in your current SECURITY.md — with specific suggestions for what to add.
Run a free audit →