Python MCP SDK 2.3 subscriptions=False: a Sume wrapper that pulls
MCPServer(subscriptions=False) turns off push subscriptions in the Python SDK. A wrapper over Sume jobs can do without them by waiting in bounded slices.

You can run a Sume-backed MCP server with MCPServer(subscriptions=False) because the Sume Developer API has no streaming transport to forward to the client. The Python SDK 2.3.0 option disables subscriptions, and a wrapper can instead expose one tool that submits and one that waits in short slices.
The release notes list the option next to max_sse_event_size and a registration-time failure for tools with an invalid x-mcp-header.
What Sume provides to wait on
The Developer API has no SSE or WebSocket transport today. GET /v1/jobs/:id/events is a pull snapshot, not a stream. The two ways to learn an outcome are polling the job and a signed webhook to a public HTTPS URL.
Webhooks deliver terminal events only: job.completed, job.failed and job.canceled. There are no progress or partial deliveries. For an agent talking to your server over MCP, polling is the simpler path because no inbound endpoint is needed.
Two tools instead of a subscription
Keep each slice shorter than your MCP client's tool timeout. Sume's own hosted jobs_wait caps a slice at 55 seconds for the same reason.
| Tool | What it does | Returns |
|---|---|---|
| submit_clip | POST the Sume generate route with mode async and an Idempotency-Key | The job id and status_url |
| wait_clip | GET the status route, then sleep up to a bounded slice | terminal flag, result_ready, next_poll_after_seconds |
| fetch_clip | GET the result route once result_ready is true | Sume media URLs |
A bounded wait in Python
This function reads the status endpoint until the job is terminal or the slice ends. It uses only the standard library, and the environment variable holds the API key.
import json
import os
import time
import urllib.request
API = "https://api.sume.com/v1"
def get_status(job_id: str) -> dict:
req = urllib.request.Request(
f"{API}/jobs/{job_id}/status",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
)
with urllib.request.urlopen(req, timeout=15) as resp:
return json.load(resp)
def wait_slice(job_id: str, slice_seconds: float = 40.0) -> dict:
deadline = time.monotonic() + slice_seconds
delay = 2.0
while True:
status = get_status(job_id)
if status.get("terminal"):
return status
pause = status.get("next_poll_after_seconds") or delay
if time.monotonic() + pause >= deadline:
return status
time.sleep(pause)
delay = min(delay * 2, 15.0)
Rules that keep it safe
- If the slice ends, return the job id and status to the model and let it call the wait tool again. Never resubmit the paid request.
- Send the same
Idempotency-Keywhen you retry a submit after a network failure, so the retry returns the original job. - Honor
next_poll_after_secondswhen it is present; use backoff when it is not. - A client-side timeout does not cancel the job. It keeps running and billing until it finishes or is cancelled before it starts.
Sources
Related posts
More in Developers
- Python MCP SDK v1 now gets security fixes only: use v2 for new clients
The Python MCP SDK v1.x line gets security fixes only; v2 added MRTR and the 2026-07-28 spec. What that means when you connect a client to Sume's hosted MCP.
- MCP tasks/cancel vs Sume jobs_cancel: cancel works only before start
TypeScript SDK 2.3.0 adds tasks/get and tasks/cancel. Sume's jobs_cancel is narrower: it succeeds only before generation starts, then returns 409.
- MCP spec timeline: 2025-11-25, RC on May 29, stable on July 28, 2026
The MCP 2026-07-28 spec went stable on July 28, 2026, 60 days after its May 29 RC, replacing 2025-11-25. Dates, and how to check what you run.
- MCP tasks extension and Sume job statuses: mapping for render tools
In MCP 2026-07-28 tasks are an extension polled with tasks/get. Map task handles, polling, update and list onto Sume job ids, status reads and jobs_cancel.
Written by Sume