Mcp server security
Agent Skills for designing and implementing Model Context Protocol (MCP) servers - server architecture, tools, resources, prompts, transports, authorization, testing, and deployment.
npx -y skills add Paldom/mcp-server-skills --skill mcp-server-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 17 days oldThe repository was created 17 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Hardens MCP servers against MCP-specific attacks - tool poisoning, rug pulls, prompt injection via tool results, session hijacking, SSRF, command injection - with guardrails-in-code, sandboxing, audit logging, kill switches. Use for securing, hardening, threat-modeling, or security-reviewing an MCP server or vetting a third-party one. Not for OAuth/token wiring or generic appsec scans.
SKILL.md
7.1 KB, as published. Nobody here has run it
mcp-server-security
MCP security is not ordinary API security: the caller is a manipulable LLM, the
server's own metadata is executable-ish content in someone else's context, and
agents retry, parallelize, and obey instructions found in data. This skill
encodes the attack classes documented in real disclosures and the defenses that
counter them. Auth mechanics (OAuth, audiences, tokens) live in
mcp-server-auth; this skill assumes identity exists and hardens everything else.
When NOT to use
- Implementing OAuth/discovery/token validation →
mcp-server-auth. - Building the server / transport wiring →
mcp-server-implementation(the Origin/localhost/session-ID floors are set there; this skill explains the attacks behind them). - Generic web/infra scanning, container CVEs, non-MCP pentesting.
Workflow
1. Threat-model against the MCP taxonomy
Walk references/threats.md (each attack: mechanism, real incident, defenses).
Summary table:
| Attack | One-line mechanism | Canonical defense |
|---|---|---|
| Tool poisoning | hostile instructions in descriptions/schema fields the user never sees | pin + hash descriptions; scan in CI; client-side re-approval |
| Rug pull | benign tool swapped post-approval via list_changed | version-pin servers; alert on description drift |
| Injection via tool results | fetched/user content carries instructions the model obeys | delimit + sanitize all external content; treat output as untrusted input |
| Confused deputy | server's own credential exercised on behalf of the wrong caller | per-client consent; audience validation (see mcp-server-auth) |
| Session hijacking | guessable/replayed session IDs | crypto-random IDs bound to user identity; sessions never = auth |
| SSRF | tool or discovery fetches attacker-chosen URLs | scheme/host allowlists; block private + metadata ranges pre-fetch |
| Command injection | tool args reaching a shell / stdio params executed | no shell-out with user input; safe APIs; treat local servers as code execution |
| Supply chain | malicious/compromised server package | pin versions; review; sandbox; registries don't vet for you |
| Denial of wallet | unbounded expensive calls (OWASP LLM10) | per-identity quotas, cost caps, retry-after |
2. Guardrails in code, not prompts
Test: if the system prompt were deleted, would the constraint still hold?
Amount caps, tenancy checks, jurisdiction rules, "never delete without
confirmation" — all if + refuse in the handler, never instructions in a
description. Make reads easy, writes expensive:
- Destructive tools: explicit confirmation gate (elicitation where supported,
or a two-step confirm token), honest
destructiveHint, separate write scope. - Idempotency keys on every mutating tool — agents retry by design; store
(caller, key)with a TTL, return the original result on replay. - Validate inputs at two layers: schema at the protocol boundary, business rules in the handler (path traversal: resolve + verify inside root; never interpolate into SQL/shell).
3. Contain the blast radius
- Run servers sandboxed: container/micro-VM, non-root, read-only FS where possible, minimal egress allowlist. Treat third-party local servers as arbitrary code execution on the host — because stdio servers are exactly that (the SDKs' param-to-shell behavior is documented as by-design).
- Secrets: environment/secret manager only; never in code, client-config args, or logs. Short-lived credentials where the backend allows.
- Per-tool rate limits AND per-identity cost quotas; a kill switch per tool (feature flag/Redis set) that disables it without a redeploy.
- Egress DLP for tools returning records: redact PII/credentials fields the caller's scope doesn't justify.
4. Audit everything, tamper-evidently
One audit event per tool call: caller identity, tool, sanitized args (or a safe summary), outcome, latency, session — to a separate append-only store (SIEM), not the app database. A web-access log is not an audit trail; compliance regimes (SOC 2, HIPAA, PCI) expect attributable, tamper-evident records. Never log tokens or raw secrets. Alert on: guardrail-block spikes, description drift, per-caller cost anomalies, session-length outliers.
5. Sanitize the model-facing surface
- Wrap external/tool-fetched content in explicit delimiters
(
<external_data source="...">) and screen for injection phrases before returning — code-level, applied unconditionally; neither delimiting nor screening is sufficient alone. - Never echo unfiltered third-party content into errors or descriptions.
- Annotations are UX hints; clients must not trust them from untrusted servers — never rely on your own annotations as an access control.
6. Gate it in CI
python3 "${CLAUDE_SKILL_DIR}/scripts/security_checklist.py" --repo .
prints the deterministic checklist (pinning, secrets, drift detection,
guardrail evals) with PASS/FAIL per item where detectable, TODO where
judgment is needed; non-zero exit on failures. Add
description-drift scanning (e.g. mcp-scan-class tooling) and a guardrail
regression eval (a prohibited action must be refused) to the pipeline —
methodology in mcp-server-testing.
Output spec
A hardened server: taxonomy reviewed with written dispositions; destructive tools confirmed + idempotent; two-layer validation; sandboxed runtime with minimal egress; per-tool limits + kill switches; separate audit store wired; external content delimited/sanitized; CI running the checklist + drift detection; secrets and logs clean.
Gotchas
- Stronger models are MORE injectable via tool results, not less — better instruction-following widens this attack. Never reason "the model will ignore it".
- Vet third-party servers like dependencies with hands: review, pin the version, hash the tool descriptions, sandbox. Registries (official one included) do not security-review listings.
- Several defenses are the client's job (consent UI, re-approval on change, sandboxing the server process) — a perfect server still needs a competent host; say so in your security docs.
- Institutional baselines exist — NSA's MCP CSI, OWASP GenAI's secure-MCP
guide, CSA's agentic-security practices — cite them to auditors instead of
arguing from blogs (index in
references/threats.md). - Real incidents to learn from, not repeat: postmark-mcp (npm server silently BCC'ing mail), CVE-2025-6514 (mcp-remote RCE), CVE-2025-49596 (old Inspector), SSRF-via-discovery in both major SDKs.
Pointers
references/threats.md— full taxonomy with mechanisms, disclosed incidents/CVEs, defenses, and the institutional-guidance index.scripts/security_checklist.py— deterministic CI checklist runner.