You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: Add Support for MCP Apps in the desktop app #10884
I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
With the Model Context Protocol (MCP) specification reaching stability for Apps (v2026-01-26), I suggest that OpenCode implements support for hosting and rendering MCP Apps.
While OpenCode is already a powerful terminal and Desktop APP, adding "MCP Apps" capabilities would allow it to launch and manage rich, interactive GUI applications. Instead of relying solely on text or Markdown, the agent could render a full React-based interface directly on the user's desktop app.
Reacted by Rodrigo Passos, Yiyu Lin, NourEldin Osama, azumukupoe, Anthony Quéré, Bartosz Łukowicz, Ben Crow, Jeppe Reinhold, Josh, Anton Smolianin and 47 more
Reacted by vvcb, Angel Loredo, yyy33 and Matt Smith
This issue might be a duplicate of existing issues. Please check:
[FEATURE]: Generic UI Intent Channel for cross-client plugin-driven UX #6330: [FEATURE]: Generic UI Intent Channel for cross-client plugin-driven UX - This discusses infrastructure for rendering rich UI (forms, dialogs, etc.) across TUI, Desktop, and Web clients, which could be foundational work for MCP Apps support.
Feel free to ignore if this doesn't address your specific case.
Reacted by Bartosz Łukowicz, Jeppe Reinhold, NourEldin Osama and wesleydonk
If this gets implemented, we could use excelidraw mcp server visually.
Reacted by Khue Doan, Trevor Hartman, Matthew Fitzpatrick, Tristan Stahnke, Angel Loredo, Nathnael Desta, ilyes, Sam700n, Pavlo Osadchyi, João Albuquerque Barbosa and 1 more
Before opening anything, I'd like design feedback on a small engine-only first increment. I built it as a throwaway local proof against dev (6bac3b9) so the discussion has something concrete behind it:
Proposed engine contract (no iframe/UI code in the engine)
Preserve MCP Apps data on the terminal tool part under state.metadata.mcp (toolMeta, resultMeta, structuredContent, and ui: { resourceUri, mimeType, html, meta }). Model lowering reads state.content, so the UI payload never reaches the model; embedded ui:// HTML is removed from model-visible content.
Resolve the app only for ui:// URIs with text/html;profile=mcp-app, via the same server connection, behind the existing opencode_read_mcp_resource permission.
A session-scoped callback POST /api/session/:sessionID/mcp/call ({ server, tool, arguments, messageID }) that reuses permission.assert with the originating message's agent — no raw Mcp.Service.callTool bypass; denial → 403, never a silent call.
What the local proof shows (patches + reproducible CI): a model tool call renders an MCP App (an OpenUI counter) in a patched OpenChamber client, a click calls the server back through (3), a visible permission prompt can deny (no MCP call reaches the server) or approve, and the host CSP blocks undeclared network access — on web (Playwright CI) and once on macOS Electron.
Proof, patches and full proposal: https://github.com/sofianbll/opencode-standards-proof (see PROOF.md and proposals/opencode-mcp-apps-v2.md).
Not claimed: this is not a released API, not full MCP Apps conformance, callback results are not persisted to history yet, and app-only tool visibility is not handled.
Thanks for the request! You're asking for the OpenCode desktop app to host and render MCP Apps: interactive UIs that MCP servers provide through ui:// resources, shown in the app instead of only text or Markdown.
The current v2 code doesn't support this yet. It also can't be added as a plugin right now, because plugins can't render UI in the desktop app or read resources from MCP servers. Maintainers will consider it.
@sofianbll's engine-first split looks right to me: engine keeps the data and the permission check, clients decide how to draw it. A few product calls that are cheap to make now and painful to change later:
The same session in the TUI. OpenCode is terminal-first, and one session gets opened from TUI, desktop and web. A tool with an app still returns normal text content, so the TUI shows that, plus one line: Interactive view available: open in desktop or web. Nobody should open a session in the terminal and find a blank tool part.
Don't save the HTML, save the pointer. The proposal stores ui.html in state.metadata.mcp. I'd keep resourceUri and the tool result, and fetch the HTML again when the part is drawn. Saved HTML grows every session file and goes stale when the server updates. If the server isn't connected later, show the text result and Excalidraw app unavailable (server not connected).
Say who's asking. Callback (3) reuses permission.assert, which is good. The prompt should also say the call came from the app, not the model: Excalidraw app wants to run export_scene. Same rules, different sentence. Otherwise a click inside an app looks like the agent going off on its own.
Nothing reaches the model quietly. Apps can send messages and update the model's context. For v1 I'd either skip those, or show each one in the thread (From Excalidraw app: …) before the model sees it. A third-party iframe adding hidden context is the kind of thing that's hard to take back once people rely on it.
Smallest v1: render inline in desktop and web with a height cap and a "pop out" button, app tool calls through the existing permission check, TUI fallback line. Later: preloading, streaming tool input into the app, app messages.
None of this changes your engine contract much. Mostly it's point 2 (pointer instead of HTML) and an origin: "app" field on the callback so point 3 is possible.
Activity
github-actions commentedon Jan 27, 2026
This issue might be a duplicate of existing issues. Please check:
Feel free to ignore if this doesn't address your specific case.
JReinhold commentedon Jan 31, 2026
The announcement of the stabel release of the MCP Apps spec lists other clients' support, and it would be great to see Opencode in this list.
https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/
JairajSustained commentedon Feb 13, 2026
If this gets implemented, we could use excelidraw mcp server visually.
MarcSkovMadsen commentedon Mar 2, 2026
I would like to visialize anything using pyodide and python ecosystem provided by https://panel-extensions.github.io/panel-live/tutorials/getting-started-mcp/
tristan-stahnke-GPS commentedon Mar 4, 2026
@MarcSkovMadsen @JairajSustained : Can you see if my PR works for your use case?
vvcb commentedon Mar 31, 2026
Prefect are working on Prefab (https://prefab.prefect.io/docs/welcome) which looks fabulous and is worth investigating for integration here.
ilyes-i-ben commentedon May 7, 2026
+1
takeseem commentedon Jun 3, 2026
玩呢,还没支持 mcp
MaxiWeissenbacher commentedon Jun 10, 2026
+1
amirsaam commentedon Jun 25, 2026
+1
adrian-pattz commentedon Aug 4, 2026
+1, this is a big missing feature
iwiznia commentedon Aug 24, 2026
+1, this is a complete deal breaker for the desktop app.
sofianbll commentedon Oct 20, 2026
Hi @adamdotdevin (cc @jlongster for #53514) — following up on this issue and on @thdxr's request in #15926 to port MCP Apps to V2.
Before opening anything, I'd like design feedback on a small engine-only first increment. I built it as a throwaway local proof against
dev(6bac3b9) so the discussion has something concrete behind it:Proposed engine contract (no iframe/UI code in the engine)
state.metadata.mcp(toolMeta,resultMeta,structuredContent, andui: { resourceUri, mimeType, html, meta }). Model lowering readsstate.content, so the UI payload never reaches the model; embeddedui://HTML is removed from model-visible content.ui://URIs withtext/html;profile=mcp-app, via the same server connection, behind the existingopencode_read_mcp_resourcepermission.POST /api/session/:sessionID/mcp/call({ server, tool, arguments, messageID }) that reusespermission.assertwith the originating message's agent — no rawMcp.Service.callToolbypass; denial → 403, never a silent call.What the local proof shows (patches + reproducible CI): a model tool call renders an MCP App (an OpenUI counter) in a patched OpenChamber client, a click calls the server back through (3), a visible permission prompt can deny (no MCP call reaches the server) or approve, and the host CSP blocks undeclared network access — on web (Playwright CI) and once on macOS Electron.
Proof, patches and full proposal: https://github.com/sofianbll/opencode-standards-proof (see
PROOF.mdandproposals/opencode-mcp-apps-v2.md).Not claimed: this is not a released API, not full MCP Apps conformance, callback results are not persisted to history yet, and app-only tool visibility is not handled.
Questions
Happy to adapt or drop this — I won't open a PR without a go-ahead per CONTRIBUTING.
opencodejr commentedon Oct 29, 2026
Thanks for the request! You're asking for the OpenCode desktop app to host and render MCP Apps: interactive UIs that MCP servers provide through
ui://resources, shown in the app instead of only text or Markdown.The current v2 code doesn't support this yet. It also can't be added as a plugin right now, because plugins can't render UI in the desktop app or read resources from MCP servers. Maintainers will consider it.
Related issues and PRs:
balabommablock-cpu commentedon Nov 1, 2026
@sofianbll's engine-first split looks right to me: engine keeps the data and the permission check, clients decide how to draw it. A few product calls that are cheap to make now and painful to change later:
The same session in the TUI. OpenCode is terminal-first, and one session gets opened from TUI, desktop and web. A tool with an app still returns normal text content, so the TUI shows that, plus one line:
Interactive view available: open in desktop or web. Nobody should open a session in the terminal and find a blank tool part.Don't save the HTML, save the pointer. The proposal stores
ui.htmlinstate.metadata.mcp. I'd keepresourceUriand the tool result, and fetch the HTML again when the part is drawn. Saved HTML grows every session file and goes stale when the server updates. If the server isn't connected later, show the text result andExcalidraw app unavailable (server not connected).Say who's asking. Callback (3) reuses
permission.assert, which is good. The prompt should also say the call came from the app, not the model:Excalidraw app wants to run export_scene. Same rules, different sentence. Otherwise a click inside an app looks like the agent going off on its own.Nothing reaches the model quietly. Apps can send messages and update the model's context. For v1 I'd either skip those, or show each one in the thread (
From Excalidraw app: …) before the model sees it. A third-party iframe adding hidden context is the kind of thing that's hard to take back once people rely on it.Smallest v1: render inline in desktop and web with a height cap and a "pop out" button, app tool calls through the existing permission check, TUI fallback line. Later: preloading, streaming tool input into the app, app messages.
None of this changes your engine contract much. Mostly it's point 2 (pointer instead of HTML) and an
origin: "app"field on the callback so point 3 is possible.