OpenCode's MCP support is solid for local and remote (SSE/HTTP) servers alike, and its permission model is more granular out of the box than Claude Code's — you can allow specific tool calls per-server in config rather than approving interactively every time. That granularity is worth exploiting for Confluence, where you want read tools wide open and write tools tightly scoped. This topic covers setup, a real documentation workflow, and where OpenCode's tool-calling falls short of Claude Code's.
Installing and Connecting Confluence MCP to OpenCode
OpenCode reads MCP server definitions from opencode.json (project root) or the global ~/.config/opencode/opencode.json. Local (stdio) servers use the local type; the config shape is close to Claude Code's but not identical — notably, environment replaces env.
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"confluence": {
"type": "local",
"command": [
"docker", "run", "-i", "--rm",
"-e", "CONFLUENCE_URL",
"-e", "CONFLUENCE_USERNAME",
"-e", "CONFLUENCE_API_TOKEN",
"ghcr.io/sooperset/mcp-atlassian:latest"
],
"environment": {
"CONFLUENCE_URL": "https://yourcompany.atlassian.net/wiki",
"CONFLUENCE_USERNAME": "dat.hoang@yourcompany.com",
"CONFLUENCE_API_TOKEN": "{env:CONFLUENCE_API_TOKEN}"
},
"enabled": true
}
}
}
The {env:CONFLUENCE_API_TOKEN} syntax pulls from your shell environment at OpenCode startup rather than baking the token into the JSON file — use it even in a gitignored config, since it means the same file works across machines without editing.
Start OpenCode and check the server connected:
export CONFLUENCE_API_TOKEN="ATATT3xFfGF0...redacted"
opencode
/mcp
OpenCode's /mcp output lists each server with a connection status and tool count, same idea as Claude Code. If confluence shows failed, the most common cause in practice is the command array being malformed — OpenCode expects a flat array of strings, and a stray nested array (easy to introduce if you're copy-pasting from a Claude Code .mcp.json where args is separate from command) fails silently with a generic "spawn error."
For a uvx-based install instead of Docker:
{
"mcp": {
"confluence": {
"type": "local",
"command": ["uvx", "mcp-atlassian",
"--confluence-url", "https://yourcompany.atlassian.net/wiki",
"--confluence-username", "dat.hoang@yourcompany.com",
"--confluence-token", "{env:CONFLUENCE_API_TOKEN}"
],
"enabled": true
}
}
}
OpenCode also supports per-tool permission gating directly in config, which is worth setting up before you let it touch a real space:
{
"permission": {
"confluence_create_page": "ask",
"confluence_update_page": "ask",
"confluence_search": "allow",
"confluence_get_page": "allow"
}
}
Tips
- Double-checkcommandis a single flat array — OpenCode's config shape for local MCP servers differs subtly from Claude Code'scommand/argssplit, and copy-paste errors here fail silently.
- Set explicitpermissionrules per Confluence tool rather than relying on OpenCode's global auto-approve default, which is more permissive than you probably want for write operations.
-opencode --print-logssurfaces the raw MCP handshake output, the fastest way to debug afailedserver status.
Reading and Creating Confluence Pages from OpenCode
Read operations work about as well in OpenCode as in Claude Code — the model correctly chains confluence_search → confluence_get_page for most natural-language prompts.
Search the DOCS space for our onboarding checklist and list the steps.
Where OpenCode's tool-calling shows more friction is multi-step write operations that depend on intermediate results — creating a page under a parent it has to look up first, for instance. It's not unreliable, but it's noticeably more likely to need a follow-up nudge than Claude Code on the same prompt.
Create a page "Feature Flags — LaunchDarkly Migration" in the ENG space,
as a child of "Platform Engineering". Summarize the migration plan from
`docs/launchdarkly-migration.md`.
In testing against OpenCode 0.14, a single-shot version of this prompt sometimes creates the page at the space root instead of under the named parent, because it skips the confluence_get_page_children (or search) step needed to resolve the parent's page ID and instead calls confluence_create_page without a parent_id. Splitting it into two explicit steps is more reliable:
Step 1: Find the page ID for "Platform Engineering" in the ENG space.
Step 2: Create a new child page under that ID titled "Feature Flags —
LaunchDarkly Migration", summarizing the migration plan from
docs/launchdarkly-migration.md.
Updating pages works the same way it does elsewhere — confluence_update_page needs the current version, fetched automatically by the tool, and OpenCode surfaces version-conflict errors as plain text rather than retrying silently, which is the behavior you want.
Tips
- For page creation under a specific parent, split "find the parent" and "create the child" into two explicit instructions — OpenCode skips the lookup step more often than Claude Code does on single-shot prompts.
- Verify newly created pages landed in the right place at least once per session — don't assume parent resolution worked just because the tool call returned success.
- Read-heavy prompts (search, summarize, compare) are where OpenCode performs closest to parity with Claude Code — lean on it there.
Practical Example: Generating an ADR from Source Code in OpenCode
A full worked example: turning a completed feature branch into an Architecture Decision Record on Confluence.
We just merged a change moving our session store from in-memory to
Redis. Read the diff between main and the previous commit on this
branch (git log -p -1). Then:
1. Search the ENG space for an existing ADR template page.
2. If found, use its section structure. If not, use: Context,
Decision, Alternatives Considered, Consequences.
3. Create the new ADR page "ADR-014: Redis-Backed Session Store" as a
child of "Architecture Decisions" in ENG.
4. Include the Redis client config from `config/redis.py` as a code
block.
This is a good test of OpenCode's planning because it chains a git read, a Confluence search, a conditional (template found vs. not), a file read, and a create — five steps with a branch in the middle. In practice, OpenCode handles the linear parts fine but sometimes needs an explicit nudge on the conditional: if no template page exists, it occasionally still tries to search a second time rather than falling back to the default structure you specified. Adding "if the search returns no results, proceed directly to step 3 with the default structure" removes that ambiguity.
The resulting page's code block renders correctly in storage format, and the section headings map cleanly to Confluence's heading styles. One recurring rough edge: OpenCode's Markdown-to-storage-format conversion (delegated to mcp-atlassian itself, not OpenCode) occasionally drops blank lines between paragraphs, producing a wall-of-text effect under "Context" that a human still needs to re-paragraph.
Tips
- For conditional logic ("if X exists, do Y, else do Z"), state the fallback explicitly rather than trusting the agent to infer it — OpenCode's planning is more literal than Claude Code's here.
- Skim the "Context" or narrative sections of AI-generated ADRs for paragraph breaks before publishing — the storage-format conversion sometimes collapses them.
- Keep a real ADR template page in your space and reference it by name in prompts — it anchors the agent's output structure far better than describing the sections inline every time.
Known Limitations for Confluence MCP in OpenCode
Worth being direct about where this combination falls short, since the marketing copy for most MCP servers doesn't mention it:
- CQL generation is less reliable than Claude Code's. For simple searches ("find the page about X") it's fine. For compound queries ("pages modified in the last 30 days by the platform team in ENG"), OpenCode more often produces CQL that Confluence's search API rejects outright, versus Claude Code, which more consistently gets compound CQL right on the first attempt.
- No native diff view for page updates. Unlike the Claude Code VS Code extension, OpenCode's TUI shows the raw tool call arguments, not a rendered before/after diff of the page body — reviewing an update means reading storage-format XML or trusting the summary the agent gives you.
- Parent-page resolution is fragile on single-shot prompts, as covered above — a real limitation for bulk documentation generation where you're creating dozens of pages under different parents in one session.
- Session persistence across OpenCode restarts doesn't carry MCP tool state — if you restart mid-workflow, the agent has no memory of which pages it already created in this "run," so batch operations need external tracking (a checklist file, a git-tracked manifest) if you want to resume safely.
None of these make OpenCode a bad choice for Confluence work — for read-heavy research and single-page generation it's genuinely fine — but for a high-volume "generate 40 API reference pages" batch job, Claude Code's planning reliability is worth the switch.
Tips
- For compound CQL queries, write the CQL yourself and hand it to the agent verbatim rather than describing the query in natural language — it sidesteps OpenCode's weaker CQL generation entirely.
- Track batch page-creation progress in an external file (a simple Markdown checklist works) so an interrupted session can resume without re-creating pages or losing track of what's left.
- For anything requiring careful review of a storage-format diff, do that review in the Confluence web UI's version history rather than trying to eyeball the OpenCode tool-call output.
Tips
Tips
- OpenCode's per-toolpermissionconfig is genuinely better for locking down write access than most alternatives — set it up once per project and reuse it.
- Lean on OpenCode for read/research/single-page-generation work; reach for Claude Code (or write CQL by hand) for anything involving compound search or multi-parent batch creation.
- Next: the same server in Gemini CLI, where output formatting diverges enough from both Claude Code and OpenCode that it's worth a direct comparison.