Skip to content

· 11 min read · Nadeera Silva

AI & Automation

MCP Vulnerability Roundup: November 2026 Server CVEs

17 MCP server and SDK CVEs from early November 2026, the bug patterns behind them, and a tested path and SSRF guard plus a hardening checklist.

A teal MCP connector hub with cables running to a folder, a database, a globe, a terminal window, a key and a gear, where small robotic hands patch amber cracks in three cables with shields, standing for fixing MCP server vulnerabilities in file, network, shell and auth paths

Between 1 and 10 November 2026, the National Vulnerability Database published around 30 CVE records matching the keyword MCP. If you run an MCP server, or an app that exposes MCP tools, the obvious question is which mcp vulnerability affects you and what to do about it. I went through the records and the original advisories, kept the ones that hit real MCP code paths, and grouped them by bug class. The same six or seven mistakes keep coming back, so for MCP security the hardening checklist at the end is more useful than any single patch.

What happened in early November

Three things landed in the same week.

  1. Official SDK fixes reached NVD. The MCP TypeScript SDK published a set of advisories in October, and the CVE records went live on NVD on 5 November. They cover cross-client message delivery, token audience checks, redirect leaks and a memory exhaustion bug. This is the closest thing to an "Anthropic MCP vulnerability" in this batch: the bugs are in the official SDK, not in the protocol spec, and they are fixed in @modelcontextprotocol/sdk 1.32.0 and the 2.3.0 packages.
  2. Vendor MCP servers shipped fixes. AWS published bulletin 2026-164-AWS on 2 November for a path traversal in the AWS Serverless MCP Server. IBM's ContextForge CVE reached NVD on 8 November (the IBM bulletin is dated 27 October), and the Dynatrace MCP server CVEs on 9 November.
  3. Apps with an MCP endpoint bolted on got caught. CMSs, dashboards, scrapers and databases added MCP tools that skipped checks their normal HTTP API already had.

I covered the critical FastMCP Prefab UI sandbox escape separately in CVE-2026-120338: check and fix Prefab UI, so it isn't repeated here.

MCP vulnerability list: November 2026

CVEPackageAffectedFixedCVSSBug class
CVE-2026-111770@modelcontextprotocol/server2.0.0 to 2.2.02.3.0 (plus code change)7.7Shared transport leak
CVE-2026-111768@modelcontextprotocol/sdk, /serversdk 1.3.0 to 1.31.x, server 2.0.0 to 2.2.0sdk 1.32.0, server 2.3.0 (plus config)7.5Memory exhaustion DoS
CVE-2026-111771@modelcontextprotocol/sdk, /server, /expresssdk 1.6.0 to 1.31.x, server 2.0.0 to 2.2.0sdk 1.32.0, server 2.3.0, express 2.0.2 (plus config)6.8Token audience not checked
CVE-2026-111769@modelcontextprotocol/sdk, /clientsdk below 1.32.0, client 2.0.0 to 2.2.xsdk 1.32.0, client 2.3.06.5Headers and tokens leak on redirect
CVE-2026-121419SurrealDB3.1.0 to 3.2.x3.3.08.1Session hijack
CVE-2026-123272mark3labs/mcp-go0.48.0 to 1.2.01.2.18.0OAuth resource confusion
CVE-2026-123271ChatCLI1.159.0 to 1.215.11.215.28.0OAuth resource confusion
CVE-2026-123811@dynatrace-oss/dynatrace-mcp-serverup to 1.8.72.0.07.5No auth in HTTP mode
CVE-2026-102610fast-agent-mcpup to 0.7.200.7.216.4No auth, shell exec
CVE-2026-123022AgentScopeup to 2.0.11none yet7.3Command injection
CVE-2026-116635awslabs.aws-serverless-mcp-serverbelow 0.2.10.2.16.1Path traversal
CVE-2026-102943Daguup to 2.10.72.11.06.5Path traversal, authz bypass
CVE-2026-121458Instaticbelow 0.0.210.0.216.5Tool skips app authz
CVE-2026-120450LaraDashboard1.4.0 to 1.4.81.4.94.3Tool skips app authz
CVE-2026-18484IBM ContextForge MCP Gateway1.0.0 to 1.0.111.1.06.5Tool name collision DoS
CVE-2026-122140Trawl1.5.0 to 1.9.31.10.05.3SSRF via IPv4-mapped IPv6
CVE-2026-120492Chainlit2.12.0none yet5.4SSRF allowlist bypass

The recurring bug patterns

MCP request flow from client to side effects, with the November 2026 MCP vulnerabilities placed at the client, session, auth, tool handler and side-effect layers
Where each bug class sits in one MCP tools/call request

Path traversal from tool input

The AWS Serverless MCP Server built a file path from the project name argument, so ../ or an absolute path could read JSON files outside the deployment status folder, and overwrite them when --enable-write was on. Dagu's MCP dagu_read operation called the spec loader directly and skipped the HTTP middleware that had fixed the same traversal earlier. The lesson: a model fills in tool arguments, and anything that can steer the model (a README, a ticket, a web page) can steer those arguments.

Tools that skip the app's own authorization

Instatic is the clearest example. A low-privilege user got a 404 from the normal REST endpoint for another user's draft, then read the same row through the content_get_document MCP tool. The capability gate checked that the caller had content.edit.own, but nobody checked who owned the row. LaraDashboard let a user with read-only template access send arbitrary email through an MCP tool. When you add MCP to an existing app, every tool is a new API endpoint and needs the same object-level checks.

SSRF and egress bypasses

Trawl's MCP URL filter matched private IPv4-mapped addresses only in dotted form (::ffff:127.0.0.1). The WHATWG URL parser normalises that to hex (::ffff:7f00:1), so every spelling slipped through, including through redirects. Chainlit's allowlist check didn't reject ;, so ..; path segments escaped a path-scoped grant on servers that strip semicolon parameters. Both are string checks where a numeric check on the resolved address was needed.

MCP OAuth token and resource confusion

In mcp-go and ChatCLI, the client trusted the resource value from the server's Protected Resource Metadata. A malicious MCP server could name a different resource on the same authorization server, get the user to consent, and replay the token there. On the server side, the TypeScript SDK's bearer auth (CVE-2026-111771) didn't check the token audience, so a token minted for another service was accepted.

Session hijack and binding

SurrealDB bound each MCP session to a fingerprint built from the access method name, not the user. Any user of the same access method who learned a victim's mcp-session-id could run tools with the victim's roles. A session ID is not a credential, and the SurrealDB code even said so in a comment; the check just compared the wrong value.

Shared transport leaks

In @modelcontextprotocol/server 2.0.0 to 2.2.0, apps that created one transport or one McpServer at startup and shared it across HTTP clients could deliver one client's responses to another. Replies were matched by JSON-RPC id, and SDK clients start at 0, so collisions happen in normal use. The README examples showed that pattern, which is why it was common. tsParticles' MCP server had the same shape: the first client to initialise claimed the shared transport and locked everyone else out.

Command execution and open listeners

AgentScope passed a user-controlled command to subprocess.Popen through its MCP workspace API. fast-agent's serve bound to 0.0.0.0:8000 with no auth, and with --shell that meant remote command execution. The Dynatrace MCP server accepted unauthenticated tools/call in HTTP mode and ran them with the server's own Dynatrace credentials.

A guard for file paths and outbound URLs

Most of the tool-level bugs above come down to two checks: keep paths inside a root, and only connect to public addresses. Here's a small Python module I use as a starting point in MCP tool handlers. It needs Python 3.9 or newer.

"""Two guards for MCP tool handlers: keep file paths inside a root, keep URLs public."""
import ipaddress
import socket
from pathlib import Path
from urllib.parse import urlsplit


class ToolInputError(ValueError):
    pass


def safe_path(root: str, user_path: str) -> Path:
    """Resolve user_path under root. Rejects '..', absolute paths and symlink escapes."""
    base = Path(root).resolve()
    # Path("/srv") / "/etc/passwd" == Path("/etc/passwd"), so resolve and check containment.
    target = (base / user_path).resolve()
    if not target.is_relative_to(base):
        raise ToolInputError(f"path escapes the allowed root: {user_path!r}")
    return target


def _is_public(ip: ipaddress._BaseAddress) -> bool:
    # ::ffff:7f00:1 is 127.0.0.1 written as IPv6. Check the IPv4 form too.
    if isinstance(ip, ipaddress.IPv6Address) and ip.ipv4_mapped:
        ip = ip.ipv4_mapped
    return ip.is_global and not ip.is_multicast


def check_url(url: str, allowed_schemes=("https",)) -> list[str]:
    """Return the public IPs a URL resolves to, or raise. Connect to these IPs only."""
    parts = urlsplit(url)
    if parts.scheme not in allowed_schemes or not parts.hostname:
        raise ToolInputError(f"scheme or host not allowed: {url!r}")
    if ";" in parts.path or "\\" in parts.path:
        raise ToolInputError(f"suspicious characters in path: {url!r}")
    try:
        infos = socket.getaddrinfo(parts.hostname, parts.port or 443, proto=socket.IPPROTO_TCP)
    except socket.gaierror as exc:
        raise ToolInputError(f"cannot resolve {parts.hostname!r}") from exc
    ips = sorted({info[4][0] for info in infos})
    for raw in ips:
        if not _is_public(ipaddress.ip_address(raw.split("%")[0])):
            raise ToolInputError(f"{parts.hostname!r} resolves to non-public {raw}")
    return ips

And the tests, which pass on Python 3.13 (python3 test_mcp_guards.py, 4 tests OK):

import os
import tempfile
import unittest

from mcp_guards import ToolInputError, check_url, safe_path


class SafePathTests(unittest.TestCase):
    def setUp(self):
        self.tmp = tempfile.TemporaryDirectory()
        self.root = os.path.join(self.tmp.name, "projects")
        os.makedirs(self.root)
        os.symlink("/etc", os.path.join(self.root, "link"))

    def tearDown(self):
        self.tmp.cleanup()

    def test_normal_name_is_allowed(self):
        self.assertTrue(str(safe_path(self.root, "my-app/status.json")).startswith(self.root))

    def test_traversal_absolute_and_symlink_are_rejected(self):
        for bad in ("../../etc/passwd", "/etc/passwd", "link/passwd", "a/../../x.json"):
            with self.assertRaises(ToolInputError, msg=bad):
                safe_path(self.root, bad)


class CheckUrlTests(unittest.TestCase):
    def test_private_and_mapped_addresses_are_rejected(self):
        for bad in (
            "https://127.0.0.1/",
            "https://[::ffff:127.0.0.1]/",
            "https://[::ffff:7f00:1]/",
            "https://[0:0:0:0:0:FFFF:A9FE:A9FE]/",  # 169.254.169.254, cloud metadata
            "https://10.0.0.5/admin",
            "https://localhost/",
            "http://8.8.8.8/",  # plain http is not on the scheme allowlist
            "https://8.8.8.8/api/..;/admin",
        ):
            with self.assertRaises(ToolInputError, msg=bad):
                check_url(bad)

    def test_public_ip_literal_is_allowed(self):
        self.assertEqual(check_url("https://8.8.8.8/dns-query"), ["8.8.8.8"])


if __name__ == "__main__":
    unittest.main(verbosity=2)

How to check if your MCP server is affected

Start with the packages you actually ship:

# Node: official SDK packages (need sdk >= 1.32.0, server/client >= 2.3.0, express >= 2.0.2)
npm ls @modelcontextprotocol/sdk @modelcontextprotocol/server @modelcontextprotocol/client @modelcontextprotocol/express

# Python: vendor servers and frameworks from the table
pip show awslabs.aws-serverless-mcp-server fast-agent-mcp mcp-contextforge-gateway chainlit agentscope 2>/dev/null | grep -E "^(Name|Version)"

# Go: mcp-go needs v1.2.1 or later
go list -m github.com/mark3labs/mcp-go

Then search your own code for the patterns, not just the packages. Look for a transport or McpServer created at module level, tool handlers that query by ID without an owner filter, and open(), fetch() or subprocess calls fed directly from tool arguments. If you moved to the newer spec, my MCP 2026-07-28 stateless migration guide covers building a server per request, which also avoids the shared transport bug.

MCP security hardening checklist

  • Upgrade the official TypeScript SDK to 1.32.0 (1.x) or 2.3.0 or later (2.x), then enable the audience check and the input element bound.
  • Build one McpServer and transport per request, or per session with a session ID generator. Never share one across clients.
  • Bind each session to the authenticated user ID, and re-check the token on every request, not only at initialize.
  • Validate the token audience on the server. On the client, only accept a Protected Resource Metadata resource that matches the server URL you configured.
  • Don't follow cross-origin redirects with custom headers, session IDs or OAuth bodies attached.
  • Call the same authorization function in each MCP tool that your REST route calls. Add a test with a low-privilege user for every tool.
  • Resolve file paths and check containment after resolving. Keep write flags such as --enable-write off unless you need them.
  • Classify outbound addresses numerically, unwrap IPv4-mapped IPv6, enforce at connect time, and recheck redirects.
  • Never build shell commands from tool input. If a tool must start processes, use a fixed allowlist of executables.
  • Bind HTTP mode to 127.0.0.1 by default, require auth, and check Host and Origin headers.
  • Treat tool names and descriptions from third-party servers as untrusted input; CVE-2026-54140 in OpenHuman was a poisoned tool description.
  • Watch advisories for every MCP package you run. The same week also brought Pydantic AI 2.66 with 10 advisories.

Is MCP secure?

The protocol gives you the pieces: OAuth with resource indicators, session IDs, and per-tool schemas. Almost every bug in this batch came from implementations that used those pieces loosely or added MCP beside an existing app without carrying its checks over. My view: treat an MCP server like any public API that a semi-trusted user drives. If you'd add an ownership check, a rate limit or an egress rule to a REST endpoint, the MCP tool needs it too.

Frequently asked questions

What is an MCP vulnerability?

It is a security bug in software that speaks the Model Context Protocol, such as an MCP server, client, SDK or gateway. In November 2026 most of them were implementation bugs like path traversal, missing authorization in tools, SSRF, OAuth token confusion and session hijacking, not flaws in the protocol spec itself.

Is MCP secure?

The protocol includes OAuth, resource indicators and session IDs, but security depends on how each server uses them. Treat every MCP tool like a public API endpoint: authenticate the caller, check permissions per object, confine file paths and restrict outbound network access.

How do I check if my MCP server is affected?

List the MCP packages you ship with npm ls, pip show or go list -m and compare them with the fixed versions in the advisories. Then search your code for a server or transport created once at startup, tools that read by ID without an owner check, and file, fetch or subprocess calls built from tool arguments.

Was there an Anthropic MCP vulnerability in November 2026?

The official MCP TypeScript SDK had several CVEs whose records reached NVD on 5 November 2026, including CVE-2026-111770 for shared transports leaking replies between clients. They are fixed in @modelcontextprotocol/sdk 1.32.0 and the 2.3.0 server and client packages, but some fixes also need code or configuration changes.

Which MCP vulnerabilities from November 2026 have no fix yet?

As of 10 November 2026, AgentScope CVE-2026-123022 (command injection, up to 2.0.11) and Chainlit CVE-2026-120492 (SSRF allowlist bypass in 2.12.0) had no fixed release. Disable the affected MCP features until patches ship.

Sources

  1. GHSA-jm63-cq47-j5jp: Shared transport or server instances delivered one HTTP client's messages to another (CVE-2026-111770) (MCP TypeScript SDK on GitHub, 2026-10-15)
  2. GHSA-rvq5-wwqv-78pq: Server bearer authentication accepted tokens issued for another service (CVE-2026-111771) (MCP TypeScript SDK on GitHub, 2026-10-05)
  3. GHSA-6prh-2h8m-c8cw: Cross-origin redirects re-sent headers and request bodies (CVE-2026-111769) (MCP TypeScript SDK on GitHub, 2026-10-02)
  4. GHSA-46jr-4wx8-v453: One HTTP request could exhaust server memory during validation (CVE-2026-111768) (MCP TypeScript SDK on GitHub, 2026-10-15)
  5. AWS security bulletin 2026-164-AWS: CVE-2026-116635 in the AWS Serverless MCP Server (AWS, 2026-11-02)
  6. GHSA-r6mm-377j-qg8m: Path traversal in the AWS Serverless MCP Server (awslabs/mcp on GitHub, 2026-11-02)
  7. GHSA-pgh9-49q4-hqcx: SurrealDB MCP session hijack via BoundSubject fingerprint collision (CVE-2026-121419) (SurrealDB on GitHub, 2026-10-19)
  8. GHSA-xf6j-693p-wqxx: mcp-go OAuth client accepts an unvalidated resource (CVE-2026-123272) (mark3labs/mcp-go on GitHub, 2026-10-09)
  9. GHSA-95vg-3r9c-2v5q: ChatCLI MCP OAuth resource indicator (CVE-2026-123271) (ChatCLI on GitHub, 2026-10-11)
  10. GHSA-x5cc-8486-8qqx: Authorization bypass in Instatic AI/MCP content read tools (CVE-2026-121458) (Instatic on GitHub, 2026-11-03)
  11. GHSA-mj45-84p2-v77q: Trawl MCP public-destination bypass via IPv4-mapped IPv6 (CVE-2026-122140) (Trawl on GitHub, 2026-11-03)
  12. CVE-2026-120492: Chainlit 2.12.0 SSRF via semicolon path segment in MCP allowed_urls (NVD, 2026-11-02)
  13. CVE-2026-123022: AgentScope OS command injection in the MCP Workspace API (NVD, 2026-11-05)
  14. CVE-2026-120450: LaraDashboard improper authorization via the MCP send-email tool (NVD, 2026-11-02)
  15. Security bulletin: IBM ContextForge MCP Gateway denial of service (CVE-2026-18484) (IBM, 2026-10-27)
  16. GHSA-p7w7-4929-vpj5: Unauthenticated HTTP MCP tool invocation in the Dynatrace MCP server (CVE-2026-123811) (Dynatrace on GitHub)
  17. GHSA-9vhv-g5hf-m7m7: fast-agent serve binds to 0.0.0.0 with no authentication (CVE-2026-102610) (fast-agent on GitHub)
  18. GHSA-5m6c-38r8-5w76: Dagu MCP dagu_read path traversal (CVE-2026-102943) (Dagu on GitHub)
  19. CVE-2026-54140: OpenHuman MCP tool description injection (NVD, 2026-11-05)
  20. AgentScope pull request 3448: opt-in allowlist for stdio MCP commands (AgentScope on GitHub)
SharePost on XLinkedIn

Comments

Share a question or note about this article.

Loading comments…