FastMCP 4 client OAuth with Sume: request mcp:read, add write later
Use FastMCP's OAuth helper in Python to sign in to Sume's hosted MCP with mcp:read first, then ask for mcp:write only for the script that spends.

Pass an OAuth object to the FastMCP Client with the scopes you want: OAuth(scopes=["mcp:read"]) for a read-only script, or mcp:read plus mcp:write for one that creates media. The client opens a browser, you approve Sume's consent page, and the token lives in memory unless you give it storage.
FastMCP's updates page (read 2026-10-07) lists 4.0.11 on Oct 4, 2026. The OAuth documentation (read 2026-10-07) is the source for the client calls below.
Which FastMCP calls this uses
The FastMCP docs show two forms. The short form is Client(url, auth="oauth"). The explicit form imports OAuth from fastmcp.client.auth and accepts scopes, optional client_id and client_secret, a token_storage object and a callback_port.
The docs also state that tokens are held in memory by default and that a local callback server handles the redirect after the browser opens. That is fine for a notebook. For a scheduled job with no browser, use a Sume API key instead; see the related post on passing the key as a string.
A read-only first run
The script below connects with read scope only and calls mcp_health, the free check that reports which credential the session uses. Wrap the async code in asyncio.run, because Python does not allow top-level await in a script.
import asyncio
from fastmcp import Client
from fastmcp.client.auth import OAuth
async def main():
auth = OAuth(scopes=["mcp:read"])
async with Client("https://mcp.sume.com/mcp", auth=auth) as client:
health = await client.call_tool("mcp_health", {})
print(health.content)
tools = await client.list_tools()
print(sorted(t.name for t in tools)[:10])
asyncio.run(main())What Sume does with the scopes
Sume's authorization server supports two scopes. mcp:read is required and gives read-only tools. mcp:write is opt-in and always includes read. There is no mcp:paid scope. On the consent page, Read is locked on and the Write toggle is off by default, so asking for mcp:write in code still lets the person decline it on the page.
A read-only session that calls generate_image gets insufficient_scope. That is the correct result, so a script should treat it as a prompt to request write, not as an outage.
| Script does | Scopes to request | Notes |
|---|---|---|
| Lists jobs, reads results, checks balance | mcp:read | Safe to run unattended-ish; no spend |
| Creates images, audio or video | mcp:read, mcp:write | Every call needs idempotency_key |
| Runs in CI with no browser | Use an API key | Full tool set; keep it in an env var |
Add the write scope only for the spending script
Keep two scripts. The reader requests mcp:read. The writer requests both scopes, passes an idempotency_key on each paid call, calls with dry_run first and sets max_spend_usd on the arguments. Sume enforces max_spend_usd only when you send it, so a missing value is not a safe default.
Persist tokens with token_storage only if the file or store is protected like a password. Sume's OAuth and API keys page says an OAuth token is not an API key and should not be pasted into prompts or forwarded to other providers.
When the browser step is a problem
The OAuth flow needs a browser on the machine that runs the script, because FastMCP starts a local callback server and opens a page. On a remote box or in CI that does not work. Use a Sume API key header there, keep it in an environment variable and give the job a key of its own.
On a laptop, set a fixed callback_port if your firewall or a container needs to know it ahead of time. Do not log the token object, and do not reuse one token store between people.
Checks before you ship it
Run tools_list after login and compare the count to what you expect for the granted scope. Run the writer once with dry_run and read the cost it reports. If either step differs from your assumption, fix the script before it runs on a schedule.
Sources
Related posts
More in Developers
- Fix an underexposed photo: curves first, AI edit only if needed
Underexposed photo? Try Pillow autocontrast and gamma for free, then an ideogram/ideogram-v4.5 edit at $0.075 only if noise or colour needs more.
- How do I fix one sentence in finished AI narration without redoing it?
Retake just the wrong sentence with a one-cent TTS job, then splice it into the original file with a $0.01 Timeline audio concat using source_in and duration.
- Set your Format run timeout from expires_at, not a guess
A non-terminal Format run receipt carries expires_at, the deadline after which Sume force-finalizes it as failed. Derive your wait from it and read queue.state.
- Format run stuck on processing: stalled or slow? Read events_url
A Format run can show processing for a long time. The last at value on events_url is the progress clock: if it stops moving for minutes, the run is stalled.
Written by Sume