On October 27, 2026, the NVD published CVE-2026-117558. The MCP server in the ai-sdlc plugin put an agent supplied repo argument straight into execSync(), "letting prompt-injected agents execute commands as the MCP server user" (CVSS 7.8). Eight more MCP CVEs from VulnCheck appeared the same day, seven of them OS command injection. By our count of NVD records, 131 CVEs mentioned MCP in October 2026, against 85 in all of 2025.

This post traces one attack chain through the four mechanisms that recur in these records and ends with the controls we would put in place for a team that ships AI agents with MCP servers.

Status on November 1, 2026: ai-sdlc fixed its bug in version 0.32.0 and Langflow closed the last of five MCP stdio command execution CVEs in 1.11.6, while esp-mcp has no fix. CVE counts come from our own queries of the NVD API, described under the first chart. We will update the post if anything changes.

Key takeaways

  • 131 CVE records in NVD mentioned MCP in October 2026, and 21 of them are tagged OS command injection (CWE-78). All of 2025 had 85. CISA lists two MCP flaws in LiteLLM, from May and July, as exploited in the wild.
  • A stdio server is a command your machine runs. In Langflow, any user who could add an MCP server could run any command on the host (CVSS 9.9). The first patch closed the API and the second (1.10.3) the execution path, and IBM later published three more command execution CVEs for the same feature, fixed in 1.11.0 and 1.11.6.
  • Tool arguments are attacker input once an agent reads untrusted text. ai-sdlc and esp-mcp passed them to a shell, and esp-mcp still has no fix.
  • Tokens get accepted for the wrong audience. The AWS API MCP Server in OAuth mode and the official MCP Python SDK both did it, and in both the patched version checks the audience only after you configure it.
  • Most fixes are ordinary engineering, such as pinned servers, no shell, a sandbox without default egress, one narrow token per server, approval for side effects and a call log.

131 CVEs in one month

CVE records whose NVD description mentions MCP, by month of publication. Our query of the NVD API on November 1, 2026.
CVE records whose NVD description mentions MCP, by month of publication. Our query of the NVD API on November 1, 2026.

We searched the NVD API month by month for "MCP" and for "Model Context Protocol", kept records whose English description contains either term as a word and dropped rejected records and Linux kernel entries about unrelated drivers such as mcp2221. That gives 725 records from January to October 2026 and 85 for 2025. Projects named like MCPHub or mcp_code_executor do not match, so the totals are a lower bound.

In October, 49 of the 131 records came from the GitHub CNA and 46 from VulnCheck, and 14 score 9.0 or higher on CVSS 3.1 or 4.0. The curve tracks disclosure activity, and one batch from a single CNA can move a month by nine records, as VulnCheck did on October 27.

Two of the 810 records from January 2025 to October 2026 are on the CISA list of known exploited vulnerabilities, both in LiteLLM and neither from October. CVE-2026-42271, added on June 8 and fixed in 1.83.7, let authenticated users run commands through MCP stdio test endpoints. CVE-2026-59822, added on September 2 and fixed in 1.84.0, let an unauthenticated attacker reach MCP tooling through an OAuth2 passthrough fallback. Which LiteLLM release to run after the poisoned 1.107.0 is in our LiteLLM supply chain write up.

Mechanism 1: a stdio server is a command your machine runs

With the stdio transport, the MCP client starts the server as a local process. The MCP security best practices say that a client offering one click setup MUST show the exact command, without truncation, and get explicit approval before running it.

Langflow let users add MCP servers from its settings page. Its advisory says the command went into bash -c "exec {command} ..." with no allowlist and ran as soon as Langflow tried to connect, "even when the UI then reports that the stdio server failed to start". With the default LANGFLOW_AUTO_LOGIN=true, an exposed instance handed out a token without credentials. CISA has listed a different Langflow RCE that starts from the same token, CVE-2026-9198 (up to 1.10.0), as exploited since August 4, 2026, and the advisory first recommends setting LANGFLOW_AUTO_LOGIN=false.

Langflow needed four releases to fix five MCP stdio command execution CVEs: 1.9.0, 1.10.3, 1.11.0 and 1.11.6. Sources: Langflow and IBM advisories, PyPI release dates.
Langflow needed four releases to fix five MCP stdio command execution CVEs: 1.9.0, 1.10.3, 1.11.0 and 1.11.6. Sources: Langflow and IBM advisories, PyPI release dates.

The two October CVEs cover the first two fixes. CVE-2026-105740 describes the settings page, patched in 1.9.0. CVE-2026-105697 adds that configs embedded in flows or passed as tweaks still reached the same sink until 1.10.3, which validates commands right before spawn and starts the process without a shell. Both carry CVSS 9.9.

It took two more releases. IBM, the CNA for Langflow, lists command execution through the command field of MCP server configs up to 1.10.3 (CVE-2026-17623, fixed in 1.11.0), and through MCP stdio arguments and the MCP Tools component in a flow up to 1.11.5 (CVE-2026-78575, CVE-2026-81941, fixed in 1.11.6 on September 1, 2026). Run 1.13.0 or later. Langflow kept tightening stdio command checks after 1.11.6 (1.12.4, 1.13.1), and IBM lists other code execution flaws in versions up to 1.12.8, fixed in 1.13.0 (bulletin 7294467).

Even with these fixes, the advisory notes that the allowlist "still lets npx / uvx run any package by default". Multi tenant deployments should also set LANGFLOW_MCP_SERVER_ALLOWED_PACKAGES, LANGFLOW_MCP_SERVER_INTERPRETER_HARDENING=true and LANGFLOW_MCP_SERVER_DOCKER_HARDENING=true. IBM also recommends LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY=true, which blocks MCP stdio for users who are not administrators.

OX Security, a security vendor, reported the same pattern in April 2026 in LiteLLM, Flowise and others, with allowlists falling to arguments such as npx -c (OX Security). According to OX, Anthropic declined to change the protocol and described the behavior as "expected". The MCP security best practices state that "The stdio transport itself is not inherently vulnerable", then warn that when a proxy service can spawn MCP servers as child processes, "it can provide a critical escalation path from web-based attacks to full system compromise". Langflow and LiteLLM are built that way.

Mechanism 2: the config file is part of the attack surface

Whoever can write the MCP config can run code. In Windsurf, attacker controlled HTML could cause "unauthorized modification of the local MCP configuration" and register a malicious stdio server without further user interaction (CVE-2026-30615, April 2026).

October added two records of the same kind. In JetBrains Junie CLI before 26.8.3, "code execution was possible via an untrusted project's MCP server configuration" (CVE-2026-111091). Z.ai ZCode connected on its own to MCP servers declared in a repository workspace configuration (CVE-2026-88755).

In our reading, a repository that ships an MCP config deserves the same review as one that ships install scripts, because opening it in an agent can start whatever the config names.

Mechanism 3: tool arguments and tool output carry attacker text

In ai-sdlc up to 0.31.2, the check_issue and check_pr_status tools accepted repo as any string and built a shell command from it (issue #1970). The fix, released in 0.32.0 on October 27, does two things at once.

typescript
// check_issue in ai-sdlc, excerpts (JSON field list and exec options trimmed)

// 0.31.2: any string is accepted, then lands in a shell command
repo: z.string().optional(),
const repoFlag = repo ? `--repo ${repo}` : '';
execSync(`gh issue view ${issueNumber} ${repoFlag} --json title,body,state`);

// 0.32.0 (commit 717d7ec): owner/name pattern in the schema, argument array, no shell
repo: z.string().regex(/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/, 'repo must be in owner/name format').optional(),
execFileSync('gh', ['issue', 'view', String(issueNumber), ...(repo ? ['--repo', repo] : []), '--json', 'title,body,state']);

esp-mcp, a server for ESP32 development, has the same flaw in a wider form (CVE-2026-117567). The report in issue #49, opened on October 24, counts 6 tools and 10 injectable parameters passed to asyncio.create_subprocess_shell. On November 1 the issue was still open, and the last commit dates from December 27, 2025.

In both records, the malicious argument comes from the agent, steered by text it read: VulnCheck describes attackers "influencing agent tool calls through prompt injection". That text can arrive through tool output, as CVE-2026-75130 in Context7 shows.

Context7 delivered the Custom AI Instructions set by library owners verbatim through two read only tools. In a demo by Noma Security, which reported the flaw, the agent read .env files, sent them out as a GitHub issue and deleted local folders (Noma Security). According to Noma, the fix went live on February 23, 2026, five days after the report.

The OWASP MCP Security Cheat Sheet lists tool poisoning (instructions hidden in descriptions, schemas or return values) and rug pulls (a server changing its tools after approval). The MCP tools specification says clients "MUST consider tool annotations to be untrusted unless they come from trusted servers" and should validate tool results before passing them to the model.

Mechanism 4: tokens that are valid somewhere else

The MCP authorization specification is strict here: "MCP servers MUST NOT accept or transit any other tokens." A server that accepts a token issued for another service, or forwards the client token downstream, becomes a confused deputy: it acts with its own privileges for a caller who should not have them.

The October records add three examples from large maintainers:

  • AWS API MCP Server 1.3.0 to 1.5.x in OAuth mode (CVE-2026-115413, CVSS 7.8). With the streamable HTTP transport and OAuth, local users could run AWS operations with the server credentials, using a token issued to another application by the same identity provider. Version 1.6.0 requires operators to set the expected audience or client ID.
  • MCP Python SDK 1.10.0 to 1.29.1 and 2.0.0 to 2.1.1 (CVE-2026-116684, CVSS 6.8). Bearer authentication accepted a token issued for another service when the verifier did not check aud. Upgrading to 1.30.0 or 2.2.0 changes nothing by itself: the advisory asks you to set validate_token_resource=True with resource_server_url and to return the token audience from verify_token().
  • MCP TypeScript SDK 1.12.0 to 1.30.x, and @modelcontextprotocol/client 2.0.0 to 2.1.x (CVE-2026-104850, CVSS 7.5). In clients that use the SDK over HTTP with an authProvider, a malicious MCP server could name its own authorization server and receive the stored refresh_token and client_secret (1.x) or the configured client secret or signed assertion (1.x and 2.x), with no user interaction.

Scopes decide how much a stolen token is worth. The specification describes a stolen token carrying files:*, db:* and admin:*, and its list of common mistakes starts with publishing every possible scope and using wildcard scopes.

One attack chain, step by step

The chain below is our illustration, built from the ai-sdlc record. We know of no public report of it being used.

  1. The bait. An attacker opens an issue in a public repository where a coding agent triages issues, asking it to check a related issue in a repository whose name ends with ; curl ….
  2. The call. The agent calls check_issue with that string as repo. In ai-sdlc 0.31.2 the schema accepts any string.
  3. The shell. execSync hands the whole string to /bin/sh, which runs gh and then the attacker command.
  4. The account. The command runs as the user who started the MCP server, who holds the gh login the tool needs and, on a developer laptop, often cloud CLI credentials and project .env files.
  5. The exit. The collected secrets leave over HTTPS, which a laptop or CI runner without an egress policy allows to any host.
Each step of the chain and its control; the control for step 1 only lowers the odds. Our illustration based on CVE-2026-117558.
Each step of the chain and its control; the control for step 1 only lowers the odds. Our illustration based on CVE-2026-117558.

A sixth step is optional for the attacker: writing a stdio entry into the agent MCP config, as the Windsurf record describes, so the code runs again at the next start. The first control is the weakest one. The OWASP cheat sheet warns that filtering cannot establish that text is safe, so the controls for steps 2 to 5 live in code and infrastructure.

What we would change in an MCP setup

These are the controls we check first when a team runs agents with MCP servers.

1. Keep an allowlist of servers, pinned by version and hash. Run images by digest or install from a lockfile with integrity hashes, instead of npx -y at every start. Pin tool definitions too, as the OWASP cheat sheet recommends, to catch a rug pull.

2. Keep shells out of tool code. Pass arguments as an array (execFile, or subprocess.run with a list and no shell=True). Give every string parameter that reaches a command, a path or a URL a pattern, as the ai-sdlc fix does with repo, and reject unknown fields with additionalProperties: false.

3. Run each server in a sandbox with no default egress. Give it a separate user, a read only filesystem, no Linux capabilities and a network that reaches only the hosts the tool needs. For a stdio server, the client config can do most of this.

json
{
  "mcpServers": {
    "github-triage": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "--read-only", "--tmpfs=/tmp", "--cap-drop=ALL",
               "--security-opt=no-new-privileges", "--user=10001:10001",
               "--network=mcp-egress", "--env=HTTPS_PROXY=http://egress-proxy:3128",
               "--env=GITHUB_TOKEN",
               "ghcr.io/your-org/github-triage-mcp@sha256:<digest you reviewed>"],
      "env": { "GITHUB_TOKEN": "<from your secret manager>" }
    }
  }
}

Here mcp-egress is an internal Docker network whose only way out is the egress-proxy container, which allows api.github.com, and GITHUB_TOKEN is a fine grained, read only token issued for this server alone. The env block matters: clients built on the official TypeScript SDK pass a stdio server only HOME, LOGNAME, PATH, SHELL, TERM and USER, so --env=GITHUB_TOKEN alone would forward nothing.

4. Keep secrets out of the agent context and out of server environments. The Context7 demo started by reading .env files. Give each server only the variables it needs, and keep production secrets out of directories the agent can read.

5. One token per server, with its own audience and narrow scopes. Validate aud on every MCP server you run: in the AWS and Python SDK records, the patched versions check it only once you configure the expected audience. Never forward the client token, and request scopes per operation.

6. Ask a person before side effects, and show the full arguments. The tools specification says there "SHOULD always be a human in the loop with the ability to deny tool invocations". Decide in your own code which tools write, send or delete, because readOnlyHint comes from the server.

7. Review MCP config files like code. Changes to .mcp.json, .cursor/mcp.json or .vscode/mcp.json go through pull request review. Agents should not start servers declared in a freshly cloned repository without approval, the gap behind the Junie and ZCode records.

8. Log every tool call with its full arguments, blocked ones included. The OWASP MCP Top 10 lists missing audit trails as MCP08. Redact secrets and alert on tools or hosts you have not seen before.

Pinning, approval and logging fit in a few lines of the agent host, before any call reaches a server.

typescript
// Agent host: runs before every MCP tool call. The model proposes the call, this code decides.
import { createHash } from "node:crypto";
import canonicalize from "canonicalize"; // RFC 8785 JSON canonicalization (stable key order)

type ToolCall = { server: string; tool: string; args: Record<string, unknown> };

// sha256 of the complete tool definition as the server lists it, recorded at review
const PINNED = new Map<string, string>([
  ["github-triage/check_issue", "<sha256 recorded at review>"],
  ["github-triage/comment_on_issue", "<sha256 recorded at review>"],
]);
const NEEDS_APPROVAL = new Set(["github-triage/comment_on_issue"]); // writes, sends, deletes

export async function gate(call: ToolCall, definition: object, approve: (c: ToolCall) => Promise<boolean>) {
  const key = `${call.server}/${call.tool}`;
  const digest = createHash("sha256").update(canonicalize(definition) ?? "").digest("hex");
  const entry = { at: new Date().toISOString(), key, digest, args: redactSecrets(call.args) }; // your redaction
  if (PINNED.get(key) !== digest) { audit({ ...entry, decision: "blocked: unknown or changed" }); throw new Error(`${key}: not on the allowlist or changed since review`); }
  if (NEEDS_APPROVAL.has(key) && !(await approve(call))) { audit({ ...entry, decision: "rejected" }); throw new Error(`${key}: rejected by a person`); }
  audit({ ...entry, decision: "allowed" }); // your log sink
}

What we do not know. The NVD count misses servers without a CVE. None of the October records is on the CISA list of exploited vulnerabilities, and CISA marks none of them as actively exploited, but its SSVC data shows a public proof of concept for 50 of the 131, Langflow among them (our check of the CVE records on November 9). OX Security estimates up to 200,000 vulnerable instances, a figure we could not check, and whether esp-mcp gets a fix is up to its maintainer.

How we review an MCP setup

We build AI agents and agentic workflows for production, and our review of an MCP setup follows the chain above.

  • Inventory. Every MCP config on laptops, CI runners and servers, with its transport, version source and editors.
  • Argument to sink. We trace every tool parameter to where it lands: a shell, a file path, a URL fetch or a SQL query. A string that gets there without a schema pattern is a finding.
  • Token map. Audience, scopes, lifetime and holder of every token, and whether any server forwards a token it received.
  • Planted injection. On a staging copy, we hide instructions in an issue or a document the agent reads and watch the call log and the egress proxy.

Findings come back mapped to OWASP MCP Top 10 IDs, each with a fix and a retest. The principle is the one from our Binance hack analysis: the component that executes enforces the policy, because the one that asks can be compromised. Retrieved documents carry the same risk as tool output, so our AI solutions team applies these controls to RAG pipelines in production as well.

Frequently Asked Questions

MCP security review for teams shipping AI agents

We trace every MCP tool from argument to sink and run a planted prompt injection against a staging copy of your agent.

Book a free technical validation

Sources

CVE counts come from our own queries of the NVD API on November 1, 2026 (method under the first chart). Everything else is attributed in the text.