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.

5 min readSume
All posts

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.

Scope choice for a FastMCP script (read 2026-10-07)
Script doesScopes to requestNotes
Lists jobs, reads results, checks balancemcp:readSafe to run unattended-ish; no spend
Creates images, audio or videomcp:read, mcp:writeEvery call needs idempotency_key
Runs in CI with no browserUse an API keyFull 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

All Developers posts

Written by Sume