From soundcheck
Audits MCP tool handlers and schemas for vulnerabilities like shell injection, arbitrary file access, hardcoded secrets, and unconstrained inputs. Use when defining MCP servers or Claude Code extensions with FS/shell/network access.
npx claudepluginhub thejefflarson/soundcheck --plugin soundcheckThis skill uses the workspace's default tool permissions.
Prevents MCP tool handlers from being exploited via malicious inputs, hardcoded secrets,
Audits .mcp.json MCP server configurations for security issues like hardcoded secrets, shell injection patterns, unpinned versions, unapproved servers, and env var usage.
Implements secure Model Context Protocol (MCP) servers and clients for AI tool integration, including tool registration, transport layers (stdio, HTTP, WebSocket), and security hardening with TDD.
Provides security patterns for MCP servers including OAuth 2.0, rate limiting, input validation, and audit logging. Activates on MCP security, authentication, OAuth mentions.
Share bugs, ideas, or general feedback.
Prevents MCP tool handlers from being exploited via malicious inputs, hardcoded secrets, unrestricted file access, or shell injection. A compromised MCP server gives attackers direct access to the host environment.
open(inputs["path"]) — arbitrary file read from tool parameter with no allowlistsubprocess.run(inputs["cmd"], shell=True) — shell injection from tool inputapi_key = "sk-abc123..." — hardcoded secret in handler or tool definition{"type": "string"} with no maxLength, pattern, or enum — unconstrained inputFlag the vulnerable code and explain the risk. Then suggest a fix that establishes these properties:
maxLength, pattern,
enum, or additionalProperties: false. A bare "type": "string" is a blank
check. The schema is the first line of defense because the model generated the
input.../, symlink escape, and
absolute paths.shell=True with string input.
If the tool must run a command, the command name comes from a static
allowlist and arguments go through argv, not string interpolation.insecure-plugin-design skill.Anchor — shape, not implementation:
schema: { filename: { type: string, maxLength: 128, pattern: "^[\\w-]+\\.(txt|csv)$" } }
API_KEY = load_from_env("SERVICE_API_KEY")
def read_file(inputs):
target = (ALLOWED_ROOT / inputs["filename"]).resolve()
require(target.is_relative_to(ALLOWED_ROOT))
audit_log("read_file", inputs["filename"])
return target.read()
Confirm the response:
os.environshell=True with string inputmaxLength, pattern, or enum