Conversation
Contributor
|
@timonwong, this pull request needs updates before review. It is marked If the PR remains invalid for 7 days, it will be closed and marked Policy findings |
timonwong
force-pushed
the
feat/mcp-apps-host
branch
2 times, most recently
from
October 1, 2026 06:35
9013178 to
3076a58
Compare
timonwong
force-pushed
the
feat/mcp-apps-host
branch
3 times, most recently
from
October 2, 2026 11:26
963e337 to
4b8c2b3
Compare
Persist a small descriptor for tool calls that open an MCP App and add the session/mcp-app Machine RPC, so a renderer can load the originating input and result and proxy the view's resource reads and tool calls through the live agent. Bump the Codex adapter and core contract. Model: claude-opus-5-5
Tool calls carrying an MCP Apps descriptor render as "Opened {app}" with
the app in a sandboxed frame. srcdoc/blob/data frames inherit the
renderer CSP and cannot run inline scripts, so the frame loads a fixed
lody-mcp-app:// sandbox proxy that receives the app HTML over the
SEP-1865 proxy handshake. The view keeps an opaque, credentialless
origin, and the proxy gives it in-memory localStorage, sessionStorage
and cookies: allow-same-origin would leave credentialless storage in
the default session's LevelDB, beyond Electron's clearing APIs. The
bridge proxies app tool calls and resource reads to the owning agent.
Model: claude-opus-5-5
Model: claude-opus-5-5
timonwong
force-pushed
the
feat/mcp-apps-host
branch
from
October 2, 2026 15:14
4b8c2b3 to
9a976d7
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Refs #1182
Problem / pressure
When an agent calls an MCP tool that declares an MCP Apps UI (SEP-1865
_meta.ui.resourceUri, aui://HTML resource), Codex desktop shows an interactive embedded view, such as "Opened NowledgeMem" with an explorable knowledge graph. Lody shows onlyRan mcp.codex_apps.nowledgemem.explore_graph. The data a host needs is dropped twice: the Codex adapter never forwards the app resource, andhistory-applykeeps neither_metanorrawOutputfor MCP calls.This PR exceeds the 1000-line community limit and stays a draft until a maintainer decides on #1182, whether to assign it or to split the work differently.
Summary
The PR has three commits, kept separate for review:
feat: route MCP Apps requests to the owning agent, the data plane:toolCall.mcpAppdescriptor (server,tool,ui://resourceUri, optionalappNameand display mode). It is extracted from_meta.lody.mcpAppand survives updates.mcpAppsclient capability from the real session client only.session/mcp-appMachine RPC on the shared lane withload,resource_readandtool_callops, plus the renderer facade anduseMcpAppHost.MCP_APP_UNAVAILABLE,MCP_APP_ACCESS_DENIED,MCP_APP_AGENT_ERRORandMCP_APP_RESPONSE_TOO_LARGE. The response cap is 16 MiB − 64 KiB, under the 16 MiB local IPC body limit; a real app's HTML is about 7.1 MB before JSON escaping.acp-extension-coreandacp-extension-codex.feat(components): render MCP Apps views for tool calls:lody-mcp-app://sandbox/proxy page, which receives the app HTML through the SEP-1865 sandbox-proxy handshake._meta.ui.csp, deny-by-default.connectDomainsacceptshttps://andwss://origins; other directives accept onlyhttps://.tools/call,resources/read,open-link(http/https), size and display mode, host-context changes, and teardown.test(e2e): cover MCP Apps view hosting: a synthetic ACP agent and app (@LODY-MCPAPP-001). It also checks that the view's in-memory storage works and never reaches Lody's user-data directory.Companion PRs: LodyAI/acp-extension-core#17 (contract) and LodyAI/acp-extension-codex#62 (agent side). Their commits currently exist only on the
timonwongforks, so CI submodule checkout from the LodyAI remotes will fail until those land and the pointers move. The codex pointer here is the same two commits cherry-picked onto8689ac3, the commitmainpins. That keepspnpm-lock.yamlunchanged apart from the new ext-apps devDependency; #62 carries the port onto currentacp-extension-codexmain.View sandbox
MCP App views run in an opaque origin:
sandbox="allow-scripts"pluscredentialless, served by thelody-mcp-app://sandbox/proxy.localStorage,sessionStorage, anddocument.cookie.SecurityError, and nothing reaches disk.A per-app origin with
allow-same-originwas considered and rejected. Chromium keeps each load'scredentiallessstorage partition in Lody's LevelDB permanently, and Electron'sclearData/clearStorageDatacannot remove those entries. Droppingcredentiallesswould instead expose Lody's default-session cookies to views. Persistent per-app storage would need a dedicated in-memory session (<webview>partition), which is left for a follow-up._meta.ui.domainis not honored.Visual explanation
sequenceDiagram participant Codex as Codex App Server participant Adapter as acp-extension-codex participant Daemon as Lody CLI daemon participant UI as Renderer card participant Frame as lody-mcp-app sandbox frame Codex->>Adapter: item mcpToolCall (mcpAppUi.resourceUri) Adapter->>Daemon: tool_call _meta.lody.mcpApp Daemon->>Daemon: history stores descriptor only UI->>Daemon: session/mcp-app load + resource_read Daemon->>Adapter: _lody/mcp_apps/load, /resource/read Adapter->>Codex: mcpServer/resource/read (threadId, originCallId) UI->>Frame: sandbox-resource-ready {html + per-app CSP} Frame->>UI: ui/initialize, then initialized UI->>Frame: tool-input, tool-result Frame->>UI: tools/call explore_graph UI->>Daemon: session/mcp-app tool_call Daemon->>Adapter: _lody/mcp_apps/tool/call Adapter->>Codex: mcpServer/tool/call nowledgemem.explore_graphBefore / after
Ran mcp.codex_apps.nowledgemem.explore_graphThe screenshots come from a real desktop build. The app and its data are synthetic; Lody's styles are unmodified.
Test plan
acp-extension-codexbuild, the machine's Codex ChatGPT login, and the real NowledgeMem app (codex_apps). It completed the handshake, rendered the graph, and completed app-initiatedtools/callround trips (bareexplore_graphforwarded asnowledgemem.explore_graph). It produced no CSP violations, page errors or failed requests. No screenshots are attached because the data is private.hostContext.toolInforejected by the official@modelcontextprotocol/ext-appsschema;codex_apps;componentstests validate the initialize result andhost-context-changedagainst the real ext-apps 2.0.0 schema, as a devDependency.Context handoff
Original user prompt
Show original prompt
Sharing refusal (verbatim)
Shared conversation
Status: user-declined
Reason: The author chose not to publish the authoring conversation and asked to include the original prompt and its key decisions instead, listed below.
Key decisions from the authoring conversation
tools/callandresources/readon their originating server, limited to tools visible to apps, and oncodex_appsto the originating connector.ai-gui/AGENTS.md;localStorage,sessionStorage, and cookies from the proxy. A per-appallow-same-originorigin was tried and dropped because itscredentiallessstorage persisted in Lody's LevelDB beyond Electron's clearing APIs;_meta.ui.domainstays unsupported.