opencode remote MCP entry for Sume: env syntax and a longer timeout
The hosted Sume server in opencode.json: type remote, a bearer header from an env variable, a timeout above jobs_wait's 55 s cap. Checked by a script.

In opencode, add the hosted Sume server under mcp in opencode.json as a remote entry pointing at the hosted MCP URL from the quickstart, read the API key from the environment with the {env:SUME_API_KEY} syntax, and set timeout above 55000. Per the opencode MCP servers documentation (read 2026-10-06), a remote entry takes type, url, enabled, headers, timeout and oauth, and the documented default timeout is 5000 ms (described for fetching tools). A 5 second limit is shorter than the 50 second default hold of jobs_wait, so if the same limit applies to a call, a healthy wait may look like an error; raising it is cheap insurance.
Unlike some clients, opencode does expand an environment reference inside a header, so the key never has to appear in the file.
How does the key get in without sitting in the file?
The header form is "Authorization": "Bearer {env:SUME_API_KEY}", which uses the {env:VAR_NAME} syntax that the opencode documentation shows for header values. Per Sume hosted OAuth and API keys, an API key sent as a bearer token gives the full hosted tool set, and writes and paid calls still need an idempotency_key. Set SUME_API_KEY in your shell profile or your secrets manager, not in the repository.
Should I turn OAuth off?
The oauth field is the other option. The opencode docs describe it as an object, or false to turn automatic OAuth off. If you send your own Authorization header, set "oauth": false so the client does not try a browser flow on top of it. If you prefer OAuth, drop headers entirely and sign in; the default grant is mcp:read, and write access needs the Write toggle at consent.
What does the whole entry look like, and how do I check it?
import json
cfg = json.loads("""
{
"mcp": {
"sume": {
"type": "remote",
"url": "https://mcp.sume.com/mcp",
"enabled": true,
"headers": {"Authorization": "Bearer {env:SUME_API_KEY}"},
"timeout": 90000,
"oauth": false
}
}
}
""")
e = cfg["mcp"]["sume"]
assert e["type"] == "remote" and e["url"].endswith("/mcp")
assert e["headers"]["Authorization"] == "Bearer {env:SUME_API_KEY}"
assert e["timeout"] > 55000, "jobs_wait can hold up to 55 s"
assert e["oauth"] is False
print("opencode entry ok")| Setting | Value here | Why |
|---|---|---|
| type | remote | Hosted server over HTTP |
| headers | Bearer {env:SUME_API_KEY} | Key stays in the environment |
| timeout | 90000 | Above the 55 s jobs_wait cap |
| oauth | false | A header is already sent |
How do I verify the connection?
After saving, start opencode and ask the agent to call tools_list, then account_me, which are the read-only checks in the hosted quickstart. If the server shows as failed, check the variable first: an unset SUME_API_KEY leaves an empty bearer value and the server answers with an auth error. When you later run a paid tool, pass an idempotency_key, run dry_run once, and when a wait ends with wait_slice_expired call jobs_wait again with the same job ids instead of creating the job a second time.
Two habits save time with this client. First, keep enabled true only for servers you use in the current project, because every enabled server adds its tools to the agent's context and a long tool list makes the model slower to choose. Second, if you share the file, share the {env:...} form and tell teammates which variable to set; a literal key committed to a repository must be rotated, and the key list in the Sume dashboard is where you do that. If a teammate would rather not manage a key at all, they can remove headers, set oauth back to its default and sign in through the browser, which gives them a read-only session by default.
The timeout deserves one more note. It is a request timeout, and a Sume job can outlive any one call: the job continues on the server, and jobs_wait returns a slice result you simply call again. So a value like 90000 is not a promise that renders finish in 90 seconds, only headroom for one hold of up to 55 seconds plus network time. For a clip that takes minutes, expect several waits in a row.
Sources
Related posts
More in Developers
- Fade out the end of a Short: Timeline fade_out_seconds limits
Sume Timeline fades video and audio at the ends with output.fade_in_seconds and fade_out_seconds, 0 to 5 each, summing to at most the length. Setup for Shorts.
- Pin the image model id on the queue row so a swap can't break old jobs
Store the Sume model id on each queued render row at enqueue time, then re-point only rows still carrying a retired id. Python sqlite3 sample for gpt-image-1.
- Poll many transcription jobs without a 429: next_poll_after_seconds
Poll Sume job status with the next_poll_after_seconds the API sends, back off on 429 with retry-after, and stop on a terminal status.
- Clicks or gaps when joining TTS MP3 clips: render WAV, join once
Joined MP3 voiceover clips can gap or click. Sume's Timeline audio docs explain why: MP3 adds priming padding at each edge. Keep WAV until the last step.
Written by Sume