This article was originally published on LucidShark Blog.
Last week, OX Security published a disclosure that should be on every engineering team's radar. A systemic remote code execution vulnerability in Anthropic's Model Context Protocol affects every official SDK: Python, TypeScript, Java, and Rust. The blast radius: 150 million downloads, 7,000 publicly exposed servers, 10-plus CVEs spawned across downstream projects.
Anthropic's response: this is expected behavior. The protocol will not be modified.
That means the fix has to come from you. This post is the concrete checklist.
What the vulnerability does: MCP's STDIO transport mechanism executes commands before validation. The sequence is: receive command, run subprocess, then check if the process was a legitimate MCP server. If it wasn't, an error is returned, but the command has already executed. Whoever controls the content of that command field controls what runs on your machine.
Why "By Design" Changes the Threat Model
When a vendor says a behavior is by design, it terminates the normal patch cycle. There will be no CVE for the core protocol. There will be no SDK update that fixes the root issue. Downstream projects, each with their own CVE (Windsurf CVE-2026-30615, Flowise CVE-2026-40933 CVSS 10.0, LibreChat CVE-2026-22252, MCP Inspector CVE-2025-49596), will patch their individual implementations. Attackers will find new bypasses. The cycle repeats because the underlying architecture is unchanged.
Flowise tried application-layer filtering. OX Security bypassed it anyway. This is not a criticism of Flowise. It demonstrates why filtering against an architectural execution model is inherently fragile.
The threat model shift: you cannot rely on the MCP ecosystem patching its way to safety. Your team needs controls that operate outside the protocol's trust boundary entirely.
The Attack Surfaces You Are Probably Missing
Most teams think about MCP security at the server configuration level. The actual attack surfaces are broader:
Zero-click IDE injection: A malicious MCP JSON config in a repository gets picked up automatically when your IDE indexes the project. Windsurf and Cursor are both confirmed vulnerable. No user action required.
Prompt injection to config: An attacker who can influence model context (via a README, a comment, a file the agent reads) can potentially inject content that leads to malicious MCP server configuration. The model processes the content, the config gets written, STDIO executes it.
Marketplace poisoning: Researchers successfully submitted malicious MCP servers to 9 of 11 tested registries without detection. If you are pulling MCP servers from a registry, you are in this attack surface.
Slopsquatting via MCP config: MCP configurations reference package names. AI agents suggesting MCP servers can hallucinate package names. Attackers register those names. The STDIO vulnerability converts an install into execution.
The Enforcement Checklist
Work through this in order. Items 1-3 are immediate. Items 4-6 are this week. Item 7 is ongoing.
1. Audit Your Current STDIO Command Parameters
List every STDIO command your MCP configuration specifies and treat each as an untrusted execution surface.
# Find all MCP config files in your project
find . -name "*.json" | xargs grep -l '"command"' 2>/dev/null | grep -i mcp
# Or check Claude Code's config location
cat ~/.config/claude/mcp_servers.json 2>/dev/null || \
cat ~/Library/Application\ Support/claude/mcp_servers.json 2>/dev/null
For each command field you find: can this value be influenced by external content? If the answer is yes or maybe, add it to your remediation list.
2. Build a Command Allowlist
The only safe STDIO configuration is one where the set of permitted commands is explicit and minimal. Build an allowlist and reject anything outside it.
// .mcp-policy.json — document and enforce this
{
"allowed_commands": [
"npx",
"/usr/local/bin/mcp-filesystem",
"/usr/local/bin/mcp-github"
],
"blocked_patterns": [
"sh", "bash", "zsh", "fish",
"python", "python3", "node",
"curl", "wget", "nc", "ncat"
]
}
If your MCP framework supports a validation hook, wire this policy in. If it doesn't, treat the absence of validation as a gap to document and monitor.
3. Rotate Credentials That Agent Processes Can Access
Assume any credential an MCP-accessible process could have reached is potentially compromised. This includes:
API keys in environment variables available to the shell where the agent runs
SSH keys loaded into the agent
AWS/GCP/Azure credentials accessible via credential files or environment
Database connection strings in
.envfiles in scanned directories
# Audit what's in the environment your agent process inherits
env | grep -E "(KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL|API)" | cut -d= -f1
Rotate anything sensitive. Move to short-lived credentials where possible.
4. Sandbox Your MCP Services
Run MCP server processes in an environment with restricted filesystem and network access. The goal is limiting blast radius when something executes that shouldn't.
# Example: run MCP server with restricted filesystem via Docker
docker run --rm \
--read-only \
--tmpfs /tmp \
--network=none \
--cap-drop=ALL \
your-mcp-server:latest
# Or use firejail for native processes
firejail --noprofile --private --net=none npx @your-org/mcp-server
Network isolation is especially important: it breaks the C2 callback that makes most exploit payloads useful.
5. Add Pre-Push SCA for Agent Commits
MCP-driven agents write code that eventually gets committed. That code can contain hallucinated package names, known-vulnerable dependencies, or packages a prompt-injected agent was steered toward. None of these are caught by STDIO-level controls.
#!/usr/bin/env bash
# .git/hooks/pre-push
echo "Running LucidShark pre-push checks..."
# SCA: validate all dependencies against live registry
lucidshark scan --sca --block-unregistered
# Secrets: catch credentials that leaked into the diff
lucidshark scan --secrets
# SAST: catch security anti-patterns the agent introduced
lucidshark scan --sast
echo "Pre-push checks passed."
Why this matters specifically for MCP: A prompt-injected agent can be directed to add a dependency with a malicious name. The SCA check validates every package name against the live npm/PyPI registry. A package that doesn't exist, or was registered 3 days ago by an unknown account, gets blocked before the commit reaches your remote.
6. Disable STDIO MCP in High-Risk Environments
If you run agentic workflows in environments where external content reaches model context (reading issues, processing emails, indexing untrusted repositories), consider disabling STDIO MCP entirely in those environments and using only managed, network-based MCP implementations with explicit authentication.
The trade-off is capability for safety. In a sandboxed dev environment with controlled inputs, STDIO is manageable. In a CI pipeline that processes arbitrary pull requests, it is not.
7. Monitor Tool Invocation Patterns
Establish a baseline of normal MCP tool call patterns for your workflow, then alert on deviations. Unexpected system commands, file system traversal outside normal working directories, or network calls to unfamiliar endpoints are all indicators of a compromised session.
# Log all MCP tool invocations if your framework supports it
# Claude Code example: check session transcript for unexpected tool calls
grep -A2 '"tool_name"' ~/.config/claude/sessions/*.json | \
grep -v -E "(read_file|write_file|list_directory|bash)" | \
grep "tool_name"
What Not to Do
A few approaches that sound right but do not address the root issue:
Relying solely on application-layer input filtering. Flowise did this. It was bypassed. The execution model means filtering is always playing catch-up with bypass techniques.
Trusting MCP marketplace listings. 9 of 11 tested registries accepted malicious submissions without review. Listing presence is not safety verification.
Assuming the model will self-detect the issue. Five AI agent failures, zero self-detections in a 36-day study published this week. The model does not have reliable awareness that it has been manipulated.
Waiting for Anthropic to patch the protocol. The "by design" response is definitive. Architecture-level protection has to come from your stack.
The One Layer That Cannot Be Bypassed via MCP
Every control in this checklist except git-layer enforcement can potentially be circumvented if an attacker gains sufficient control over model context or MCP configuration. But a pre-push hook runs as a separate process, initiated by the git client, with its own execution context. An MCP server cannot instruct a git hook not to run. A prompt-injected agent cannot disable a pre-push check it has no visibility into.
That asymmetry is why LucidShark focuses on the git layer: SCA, SAST, secrets detection, and lint checks that run at commit time, outside the agent's execution path, against the actual artifact the agent produced. The MCP RCE changes the threat model for what happens during an agent session. It does not change what happens at the git boundary.
Install LucidShark: npm install -g lucidshark then lucidshark init to configure pre-push hooks in under 2 minutes. Scans run locally, no data leaves your machine. lucidshark.com
Top comments (0)