// threat brief
MCP Python SDK unbounded session accumulation: a client that can reach a stateful Streamable HTTP endpoint can grow server memory without limit
- CVSS
- 6.9 MEDIUM
- CISA KEV
- Not listed
- Weakness
- CWE-400 · CWE-770 · CWE-772
- Topics
- AI security
AI review 2026-10-19 (not human-reviewed)Command tests: none yetReproduced: not yetVerification record ↓
In mcp 1.8.0–1.29.1 and 2.0.0–2.1.1, stateful Streamable HTTP servers never freed sessions, so a client reaching the endpoint could grow memory until restart.
| Who is affected | Servers using the PyPI package mcp 1.8.0 through 1.29.1 or 2.0.0 through 2.1.1 that serve Streamable HTTP in stateful mode (the default) where clients you don't fully trust can reach the endpoint. Servers using only stdio or SSE, or passing stateless_http=True on 1.12.0 or later, are not affected. |
|---|---|
| What happens | Server memory can keep growing until the service becomes unavailable (denial of service); only restarting the process frees it. According to the CVE record's scoring, data confidentiality and integrity are not affected. |
| What to do now | Upgrade mcp to 1.30.0 (1.x) or 2.2.0 (2.x) or later. If you can't upgrade yet, pass stateless_http=True (1.12.0 or later) where you need no per-session state, or restrict who can reach the endpoint. |
| Affected versions | 1.8.0 through 1.29.1; 2.0.0 through 2.1.1 (stateful Streamable HTTP servers only) |
| Fixed versions | 1.30.0 or later (1.x); 2.2.0 or later (2.x) |
| CVSS vector | Show full vectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N |
| Timeline |
|
Blue team playbook
Check, fix / mitigate, respond, harden. Matching the affected versions does not mean you were compromised, and a check that finds nothing does not prove you are safe; each item says what it can and cannot show. Tagged steps point to where the attack flow below can be stopped.
- 01Check your version
Run this on the host that runs the MCP server, after activating the virtual environment the server actually uses (or inside its container); otherwise you may see another Python environment's version. It only reads the version and changes nothing. mcp 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1 are in the affected range.
Read-only checkpip show mcp | grep -i '^version' - 02Check whether you serve stateful Streamable HTTP
FastMCP (1.x), MCPServer and Server.streamable_http_app() (2.x), and StreamableHTTPSessionManager all default to stateful mode. Servers using only stdio or SSE, or passing stateless_http=True on 1.12.0 or later, are not affected. Also check whether clients you don't fully trust can reach the endpoint. Run this from the root of your own MCP server project's source tree (read-only). Code that uses StreamableHTTPSessionManager directly passes stateless=, not stateless_http, so search for both (spaces around the equals sign are matched too); if found, check whether the value is True. The command skips .venv, venv, site-packages, node_modules and .git so it doesn't pick up the mcp package's own source or examples and mislead you; if your virtual environment lives in a folder with another name, add an exclusion for it. PlainCVE notes that this is only a first check. Settings made through environment variables or config files won't show up, so check those separately; servers using only stdio or SSE are not affected even if nothing is found. Only if you serve Streamable HTTP and nothing is found are you likely using the default stateful mode.
Read-only checkgrep -rnE --exclude-dir=.venv --exclude-dir=venv --exclude-dir=site-packages --exclude-dir=node_modules --exclude-dir=.git "stateless(_http)?[[:space:]]*=" . - 03After upgrading, watch for the session-cap warning
The documentation in the 2.x fix commit d2290ca says that when the session cap is hit and a new session is refused, a WARNING "Refusing to open a new session" is logged. The 1.x backport (PR #3426) does not mention this message, so whether 1.30.0 logs the same line is unconfirmed. Frequent warnings may mean someone is opening sessions in bulk, or simply that the cap is too low for your load. Run this on the server host, replacing /path/to/logs/ with your actual log directory; it searches uncompressed log files in that directory and its subfolders and only reads files. PlainCVE's note: compressed rotated files (such as .gz) are not searched by this command, and logs sent only to stdout, journald or container logs are not in that directory, so check those places separately.
Read-only checkgrep -rn "Refusing to open a new session" /path/to/logs/
- 01On 1.x (1.8.0 through 1.29.1): upgrade to 1.30.0 or later
Changes your environment: run it on the host that runs the MCP server, after activating the virtual environment the server uses (or inside its container). This command stays on 1.x and does not cross a major version. Fixed versions close sessions after 30 minutes idle by default, cap how many exist at once, and discard sessions as soon as they end.
Changes your environmentpip install -U "mcp>=1.30.0,<2" - 02On 2.x (2.0.0 through 2.1.1): upgrade to 2.2.0 or later
Changes your environment: run it on the host that runs the MCP server, after activating the virtual environment the server uses (or inside its container). Only for users already on 2.x; PlainCVE notes that running this on 1.x would move you across a major version to 2.x, so 1.x users should use the previous card. Same fixes as above.
Changes your environmentpip install -U "mcp>=2.2.0" - 03Can't upgrade yet: go stateless or restrict access
Official advice: pass stateless_http=True (1.12.0 or later) where you need no per-session state, or restrict who can reach the endpoint.
- 01If memory keeps growing, restart and upgrade
The maintainers say only a restart reclaims this memory. A restart is temporary; upgrade afterwards. No source reports attacks in the wild, and a third party saw sessions pile up during normal use, so memory growth does not necessarily mean an attack.
- 01Don't set session_idle_timeout back to None
The release notes say passing None restores the old behavior. PlainCVE's note: that also brings back never-expiring idle sessions, so only do it if you truly need to.
- 02Restrict network reach, not just authentication
The maintainers note authentication limits who can open sessions, not how many. Some retention remains after the fix: each live session can hold up to roughly twice the 4 MiB body limit. PlainCVE suggests also monitoring the MCP server's memory use.
Full remediation steps and notes
| Current version | Upgrade to | Command |
|---|---|---|
| 1.8.0 through 1.29.1 | 1.30.0 or later | pip install -U "mcp>=1.30.0,<2" |
| 2.0.0 through 2.1.1 | 2.2.0 or later | pip install -U "mcp>=2.2.0" |
Behavior change to know before upgrading. The official 1.30.0 release notes say a stateful session with nothing in flight for 30 minutes is closed; the client’s next request gets a 404 and must initialize again. Open streams and in-progress requests keep a session alive. If your clients don’t re-initialize automatically, test first. Passing None restores the old behavior, but PlainCVE notes that this also makes idle sessions never expire again.
Users on 1.27 through 1.29.1. The session_idle_timeout option has existed since 1.27 but was off by default; PlainCVE suggests upgrading anyway, since the fix also covers DELETE and refused sessions.
Attack flow
Read left to right: this is the order the attack happens in. A blue shield means the step can be stopped; the earlier, the better. Click an icon for details.
Read top to bottom: this is the order the attack happens in. A blue shield means the step can be stopped; the earlier, the better. Tap an icon for details.
- Attacker
A client reaches the server's Streamable HTTP endpoint
According to the CVE record's scoring, no privileges or user interaction are needed. The maintainers note that authentication only limits who can open sessions, not how many a token (login credential) holder can open.
Defense: Restrict who can reach the endpoint; use stateless_http=True (1.12.0 or later) where no session state is needed.
- Attacker
Keeps opening new sessions with ordinary short requests
Older versions had no limit on how many sessions could exist, and each session kept its parsed initialize parameters.
Defense: After upgrading, at most max_sessions sessions (default 10000) can exist; a request for another gets a 503.
- Your system
Ended or refused sessions stay registered
After a client DELETE ended a session, its entry was not removed; a session whose first request was refused also stayed registered with a running task.
Defense: Fixed versions discard a session as soon as it ends, whether by DELETE, expiry or a refused opening request.
- Your system
Idle sessions never expire
Older versions had no idle expiry by default; a session_idle_timeout option has existed since 1.27 but defaulted to off (None).
Defense: Fixed versions close a session after 1800 seconds (30 minutes) idle by default.
- Your system
Memory grows without limit and the service may go down
Only restarting the process reclaims the memory. The impact is on availability only.
Defense: A restart reclaims memory for now, but upgrading is the real fix.
Who is affected
| Condition | Affected? |
|---|---|
| mcp 1.8.0 through 1.29.1, serving Streamable HTTP in stateful mode (the default) | Affected |
| mcp 2.0.0 through 2.1.1, serving Streamable HTTP in stateful mode (the default) | Affected |
| mcp 1.30.0 or later (1.x) or 2.2.0 or later (2.x) | Fixed |
1.12.0 or later with stateless_http=True (no sessions kept) | Not affected |
| Only the stdio or SSE transports | Not affected |
Components that default to stateful mode: FastMCP (1.x), MCPServer and Server.streamable_http_app() (2.x), and StreamableHTTPSessionManager. The maintainers put it this way: you are affected if you serve MCP over Streamable HTTP in stateful mode and clients you don’t fully trust can reach the endpoint. According to the CVE record’s scoring, no privileges or user interaction are needed; the maintainers also point out that authentication (for example requiring a token, i.e. a login credential) limits who can open sessions, not how many one holder can open.
How it works
MCP (Model Context Protocol) is a standard that lets AI call external tools, and Streamable HTTP is one way it travels over HTTP. In stateful mode the server creates a “session” for each client: a record that remembers that client’s state.
In older versions these records went in but never came out. The CVE record and the maintainer advisory describe several reasons:
- no idle expiry and no limit on the total number of sessions;
- a client
DELETEended a session but left its entry registered; - a session whose first request was refused stayed registered with a running task;
- each session kept its parsed
initializeparameters.
The result: a client that could reach the endpoint could grow server memory without limit using ordinary short requests, and only a restart reclaimed it. The impact is on availability only (denial of service); the CVE record’s scoring shows no confidentiality or integrity impact, and nothing points to code execution or data exposure.
The flawed approach (conceptual illustration, not the original source code):
Show code example(python)
sessions = {}
def open_session(req):
sessions[new_id()] = Session(req.initialize_params) # no limit
def close_session(sid):
sessions[sid].stop() # stopped, but never removed from sessionsThe fixed approach:
Show code example(python)
def open_session(req):
if len(sessions) >= max_sessions: # default 10000
return 503
sessions[new_id()] = Session(req.initialize_params)
def close_session(sid): # reached on DELETE, expiry, or a refused open
sessions.pop(sid).stop()
# In the background: close after session_idle_timeout (default 1800 s) idle; the ID then answers 404The fix bounds retention rather than removing it: each live session still holds its initialize parameters, up to roughly twice the 4 MiB request body limit.
Timeline
| Date | Event |
|---|---|
| 2026-04-16 | Project issue #2455 opened: session_idle_timeout is not exposed in the high-level API; PR #3395 lists it as one of the issues it fixes |
| 2026-07-03 | A third party opens PR #3059 in the project repo: in stateful mode, GET/DELETE requests without a session ID are refused but leave their sessions and background tasks behind, accumulating over time (that page contains attack code; we do not link it). This is only one of the causes listed in the CVE and the advisory (refused opening requests); it mentions in passing that only the off-by-default session_idle_timeout reaps these sessions, but it does not cover cleanly DELETEd sessions staying registered or the missing session-count limit, and does not call it a security issue. The maintainers say in PR #3426 that #3059 covered only part of this |
| 2026-07-07 | CVE ID reserved |
| 2026-07-30 | A third party publicly reports in project issue #3228 (that page contains attack code; we do not link it). The original report covers several of the causes listed in the CVE and the advisory: refused Streamable HTTP requests leave registered, unterminated sessions behind; the real availability gap is the missing session-count cap plus an idle reaper (session_idle_timeout) that is off by default and cannot be switched on through the documented API; and legitimate traffic alone can already grow sessions without bound. The report as opened does not cover cleanly DELETEd sessions staying registered; that was added in a comment in the same thread on 2026-08-13 (see below). The reporter says it is not filed as a security issue |
| 2026-08-13 | A third party publicly reports in project issue #3300 that cleanly ended (DELETE) sessions are not removed from the registry (that page contains attack code; we do not link it). It covers only this cause, not a session-count limit, and does not call it a security issue. The same day, a comment on issue #3228 also notes that a successful DELETE does not deregister the session and the registry grows without bound |
| 2026-08-26 | 2.x fix PR #3395 opened, publicly explaining that at stock settings a session whose client never sent DELETE stayed registered until the process exited, and fixing idle expiry, the session cap, and DELETE/refused-request leftovers together; the PR files it as a bug fix and does not call it a security issue. Merged 2026-08-28 |
| 2026-09-02 | 1.x backport PR #3426 merged |
| 2026-09-07 | Maintainer publishes v1.30.0 and v2.2.0 on GitHub (release pages show 7 Sep; year inferred from PR #3426 being merged on 2026-09-02) |
| 2026-09-30 | Maintainer GitHub advisory GHSA-84m7-p3x7-pcfv published, fully describing the vulnerability; this page uses this date as first public disclosure (the earlier of the official advisory and the CVE record). The earlier project issue #3228 (2026-07-30) already publicly described several causes, but as opened it did not cover all of them and was not filed as a security issue, so under PlainCVE’s conservative default it stays in the timeline |
| 2026-10-14 | CVE record published |
| 2026-10-15 | GitHub Advisory Database adds the reviewed entry GHSA-84m7-p3x7-pcfv (published_at 2026-10-15T20:13:13Z, severity medium, listing both CVSS 3.1 7.5 and CVSS 4.0 6.9, CWE-400/770/772); not in CISA KEV as of this date |
| 2026-10-17 | NVD status “Awaiting Analysis” (not yet analysed) |
Our analysis: the fixes shipped about three weeks before the advisory and about five weeks before the CVE. No source links that gap to any exploitation.
Further reading
- Scoring differences: the CVE record (CNA) gives CVSS 4.0 6.9 (Medium) with availability impact Low; the maintainer advisory gives CVSS 3.1 7.5 with availability impact High and rates it high. NVD has not analysed it yet. This page uses the CVE record’s score.
- CWE differences: the CVE record lists CWE-400 (uncontrolled resource consumption); the maintainer advisory lists CWE-770 (allocation of resources without limits or throttling). Both list CWE-772.
- Real-world observation: a third-party operator running mcp-proxy reported (mcp-proxy issue #252) sessions piling up without limit during normal use on mcp 1.26.0, which went away on 1.30.0. This shows the problem can bite without an attacker, but it is not evidence of an attack.
- Exploitation status: as of 2026-10-15 it is not in CISA KEV, no source reports exploitation in the wild, and none says it was a zero-day.
- Fix commits: 2.x is d2290ca (PR #3395); 1.x is 92120b4 (PR #3426, a backport of #3395).
Verification Checked item by item against sources by AI under our review rules, all passed; not human-reviewed and not yet reproduced in our lab
| AI review | 2026-10-19 · review rules |
|---|
Verification records describe the environment and the result only, never reproduction steps or code that could attack other people’s systems. See our policy.
Sources
- AdvisoryGHSA-84m7-p3x7-pcfv — Unbounded session accumulation in Streamable HTTP transport · modelcontextprotocol (GitHub), 2026-09-30 · accessed 2026-10-15
- CVE / NVD / OSVCVE-2026-59951 Record · CVE Program, 2026-10-14 · accessed 2026-10-15
- CVE / NVD / OSVNVD API - CVE-2026-59951 · NIST · accessed 2026-10-15
- Patch / releaseExpire idle Streamable HTTP sessions by default and cap concurrent sessions (PR #3395) · modelcontextprotocol (GitHub), 2026-08-26 · accessed 2026-10-15
- Patch / releaseCommit d2290ca3434731b68ea3e2270bc06a6e6575931b (2.x fix) · modelcontextprotocol (GitHub) · accessed 2026-10-15
- Patch / releasev1.x backport of #3395 (PR #3426) · modelcontextprotocol (GitHub) · accessed 2026-10-15
- Patch / releasemcp v2.2.0 release · modelcontextprotocol (GitHub), 2026-09-07 · accessed 2026-10-15
- Patch / releasemcp v1.30.0 release · modelcontextprotocol (GitHub), 2026-09-07 · accessed 2026-10-15
- Othermcp-proxy issue #252: sessions accumulate without bound on mcp 1.26.0 · sparfenyuk/mcp-proxy (GitHub) · accessed 2026-10-15
- OtherIssue #2455: session_idle_timeout not exposed · modelcontextprotocol (GitHub), 2026-04-16 · accessed 2026-10-17
- OtherGitHub Advisory Database query for CVE-2026-59951 · GitHub, 2026-10-15 · accessed 2026-10-17
Press brief
In one sentence
The MCP Python SDK (mcp on PyPI) is a Python implementation of the Model Context Protocol, used to build MCP servers; MCP is a protocol that lets AI call external tools and data. In its default stateful mode, its Streamable HTTP transport never cleared out old sessions, so a client that could reach the endpoint could grow server memory without limit until a restart. Fixed versions 1.30.0 and 2.2.0 are out; as of 2026-10-15 the flaw is not in CISA's Known Exploited Vulnerabilities catalog, and no source reports attacks in the wild.
Key facts
- CNA score
- CVSS 4.0 6.9 (Medium) cveawg.mitre.org
- Maintainer score
- CVSS 3.1 7.5 (High) github.com
- Fixed versions
- 1.30.0 (1.x), 2.2.0 (2.x) github.com
- Advisory published
- 2026-09-30 (GHSA-84m7-p3x7-pcfv) github.com
- CVE published
- 2026-10-14 cveawg.mitre.org
Confirmed
- In affected versions (1.8.0–1.29.1, 2.0.0–2.1.1), stateful Streamable HTTP servers kept sessions without limit, and only a restart reclaimed the memory.
- The official fixes are 1.30.0 and 2.2.0; servers using only stdio/SSE or stateless_http=True (1.12.0+) are not affected.
- As of 2026-10-15 it is not in the CISA KEV catalog.
- A third-party operator saw sessions pile up on 1.26.0 during normal use, which stopped on 1.30.0; this is not evidence of an attack.
Unconfirmed / disputed
- No source reports exploitation in the wild, and none says it was exploited before a patch existed (zero-day).
- Severity differs: the CNA gives CVSS 4.0 6.9 (Medium), the maintainer advisory CVSS 3.1 7.5 (High); NVD has not analysed it yet.
- Weakness classes differ: the CVE record lists CWE-400, the maintainer advisory CWE-770; both list CWE-772.
Quotable line
Nobody breaks in here; it's a hotel that keeps checking guests in and never checks anyone out, until it runs out of rooms.
Images
Download the share image (PNG) · Attack flow diagram (downloadable)
Please credit "PlainCVE" and link to this page. Full citation format is under "How to cite" below. Found a factual error? Report it.
MCP Python SDK unbounded session accumulation: a client that can reach a stateful Streamable HTTP endpoint can grow server memory without limit The MCP Python SDK (mcp on PyPI) is a Python implementation of the Model Context Protocol, used to build MCP servers; MCP is a protocol that lets AI call external tools and data. In its default stateful mode, its Streamable HTTP transport never cleared out old sessions, so a client that could reach the endpoint could grow server memory without limit until a restart. Fixed versions 1.30.0 and 2.2.0 are out; as of 2026-10-15 the flaw is not in CISA's Known Exploited Vulnerabilities catalog, and no source reports attacks in the wild. Key facts: - CNA score:CVSS 4.0 6.9 (Medium)(https://cveawg.mitre.org/api/cve/CVE-2026-59951) - Maintainer score:CVSS 3.1 7.5 (High)(https://github.com/modelcontextprotocol/python-sdk/security/advisories/GHSA-84m7-p3x7-pcfv) - Fixed versions:1.30.0 (1.x), 2.2.0 (2.x)(https://github.com/modelcontextprotocol/python-sdk/security/advisories/GHSA-84m7-p3x7-pcfv) - Advisory published:2026-09-30 (GHSA-84m7-p3x7-pcfv)(https://github.com/modelcontextprotocol/python-sdk/security/advisories/GHSA-84m7-p3x7-pcfv) - CVE published:2026-10-14(https://cveawg.mitre.org/api/cve/CVE-2026-59951) Confirmed: - In affected versions (1.8.0–1.29.1, 2.0.0–2.1.1), stateful Streamable HTTP servers kept sessions without limit, and only a restart reclaimed the memory. - The official fixes are 1.30.0 and 2.2.0; servers using only stdio/SSE or stateless_http=True (1.12.0+) are not affected. - As of 2026-10-15 it is not in the CISA KEV catalog. - A third-party operator saw sessions pile up on 1.26.0 during normal use, which stopped on 1.30.0; this is not evidence of an attack. Unconfirmed / disputed: - No source reports exploitation in the wild, and none says it was exploited before a patch existed (zero-day). - Severity differs: the CNA gives CVSS 4.0 6.9 (Medium), the maintainer advisory CVSS 3.1 7.5 (High); NVD has not analysed it yet. - Weakness classes differ: the CVE record lists CWE-400, the maintainer advisory CWE-770; both list CWE-772. 「Nobody breaks in here; it's a hotel that keeps checking guests in and never checks anyone out, until it runs out of rooms.」— PlainCVE https://plaincve.date/en/vulns/cve-2026-59951-mcp-python-sdk-session-exhaustion
How to cite this page
This article is CC BY 4.0. Please keep the attribution and link when republishing.
PlainCVE Team (2026). "MCP Python SDK unbounded session accumulation: a client that can reach a stateful Streamable HTTP endpoint can grow server memory without limit". PlainCVE. https://plaincve.date/en/vulns/cve-2026-59951-mcp-python-sdk-session-exhaustion (accessed YYYY-MM-DD)BibTeX
@misc{cve202659951mcppythonsdksessionexhaustion2026,
title = {MCP Python SDK unbounded session accumulation: a client that can reach a stateful Streamable HTTP endpoint can grow server memory without limit},
author = {PlainCVE Team},
year = {2026},
howpublished = {PlainCVE},
url = {https://plaincve.date/en/vulns/cve-2026-59951-mcp-python-sdk-session-exhaustion},
note = {Updated 2026-10-19}
}