feat: support ui/update-model-context for MCP Apps#8044
feat: support ui/update-model-context for MCP Apps#8044
ui/update-model-context for MCP Apps#8044Conversation
Implement the ui/update-model-context method from the MCP Apps specification, allowing MCP App views to push context updates to the host agent without triggering immediate action. Context updates are stored in-memory (last-write-wins per app key) and injected alongside MOIM as a synthetic user message on the next agent turn, giving the model awareness of app state. - Add McpAppContext store on Agent with update/collect methods - Add /agent/update_model_context server endpoint - Integrate with MOIM injection in moim.rs - Handle ui/update-model-context in McpAppRenderer - Add 7 unit tests covering store, overwrite, collect, and injection Signed-off-by: Andrew Harvard <aharvard@squareup.com> Co-authored-by: Goose <opensource@block.xyz> Ai-assisted: true
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5b1c8a2bc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5b1c8a2bc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Escape <, >, &, and " in app-provided text and keys before wrapping them in <mcp-app-context> XML blocks. Prevents malformed markup from benign payloads (e.g. code snippets) and blocks malicious apps from closing the tag to spoof extra context sections. Adds a test covering special character escaping. Signed-off-by: Andrew Harvard <aharvard@squareup.com> Co-authored-by: Goose <opensource@block.xyz> Ai-assisted: true
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d191713b6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ui/update-model-context for MCP Apps
Content blocks without a text field (e.g. image, resource) were silently dropped during context collection. Now falls back to JSON serialization so the model sees all block types. Signed-off-by: Andrew Harvard <aharvard@squareup.com> Co-authored-by: Goose <opensource@block.xyz> Ai-assisted: true
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9fa95fbda7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Regarding the per-view key comment — this is a known design decision. The current key scheme ( For the multi-instance question (same resource rendered multiple times in a session), we've opened an issue upstream to get spec guidance: modelcontextprotocol/ext-apps#558 Instance-scoped keys can be added later without breaking the existing API if multi-instance support is needed. |
When serializing non-text content blocks (e.g. images), remove the `data` field before JSON-stringifying. This prevents base64 payloads from bloating the synthetic MOIM message with thousands of wasted tokens. The block metadata (type, mimeType, etc.) is preserved so the model knows a non-text block exists without seeing the raw data. Signed-off-by: Andrew Harvard <aharvard@squareup.com> Co-authored-by: Goose <opensource@block.xyz> Ai-assisted: true
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b80b9fe7a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
structuredContent is view-only hydration data and should never be injected into the model context. Remove it from collect_mcp_app_contexts entirely. Also strip resource.blob from embedded resource content blocks alongside the existing data field stripping, preventing binary payloads from leaking into the synthetic message. Signed-off-by: Andrew Harvard <aharvard@squareup.com> Co-authored-by: Goose <opensource@block.xyz> Ai-assisted: true
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5e2cba51c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| s.replace('&', "&") | ||
| .replace('<', "<") | ||
| .replace('>', ">") | ||
| .replace('"', """) | ||
| } |
There was a problem hiding this comment.
Stop escaping quotes in app-context body text
collect_mcp_app_contexts() serializes non-text blocks to JSON and then runs them through xml_escape(). Because this helper also rewrites " to ", the fallback you inject into Message::user().with_text(...) is no longer valid JSON—apps that send image/resource blocks will reach the model as {"type":...} instead of something it can reliably inspect or copy. Escaping </& is enough for element text here; escaping quotes breaks the metadata this feature is trying to preserve.
Useful? React with 👍 / 👎.
| goose::agents::McpAppContext { | ||
| content: payload.content, | ||
| structured_content: payload.structured_content, |
There was a problem hiding this comment.
Drop structuredContent instead of retaining it unused
This request stores structuredContent on McpAppContext, but the new collection path explicitly never injects that field into the prompt. I checked the production references after this change: outside tests, structured_content is only written here and on the struct, never read. For MCP apps that follow our own guidance and put full hydration payloads in structuredContent, each update now pins that potentially large JSON blob in mcp_app_contexts until another update or session teardown with no effect on model behavior.
Useful? React with 👍 / 👎.
DOsinga
left a comment
There was a problem hiding this comment.
left some comments, but maybe we should talk about this. is moim the right mechanism? if the agent gets reset due to some mechanism, you lose the value the way it is setup right now. should we consider a user invisible message instead? and also what's the role of the structured content here?
| } | ||
|
|
||
| if let Some(moim) = extension_manager | ||
| let moim = extension_manager |
There was a problem hiding this comment.
the way it works right now is that the extension manager does all the moim collection, moving part of that here but not everything makes this fragile. so I would either have the extension manager return a list of moim items and do the construction here (preferred) or send the mcp_app_context to the extension manager
| ); | ||
| } | ||
|
|
||
| #[tokio::test] |
There was a problem hiding this comment.
I would drop these tests. all they do is confirm that if you add a string it makes it to the other end
| } | ||
| } | ||
| // structuredContent is intentionally not injected — it is | ||
| // view-only hydration data and should never reach the model. |
There was a problem hiding this comment.
this gives me pause - shouldn't the structured content go back to the widget then again?
Summary
Implements
ui/update-model-contextfrom the MCP Apps specification, allowing MCP App views to push context updates to the host agent without triggering immediate action.Closes #6472
Demo
goose-update-model-context.mov
How it works
When an MCP App sends
ui/update-model-context:/agent/update_model_context{extension}__{resourceUri}(last-write-wins)The model sees context like:
Design decisions
ui/message, updating context does not kick off an agent turn. It passively waits for the next natural turn.Changes
crates/goose/src/agents/agent.rsMcpAppContextstruct,mcp_app_contextsHashMap,update_mcp_app_context(),collect_mcp_app_contexts(), 4 unit testscrates/goose/src/agents/moim.rsinject_moim()acceptsmcp_app_contextparam, combines with MOIM text, 2 new testscrates/goose/src/agents/mod.rsMcpAppContextcrates/goose-server/src/routes/agent.rsPOST /agent/update_model_contextendpointcrates/goose-server/src/openapi.rsui/desktop/src/components/McpApps/McpAppRenderer.tsxui/update-model-contextfrom MCP Appsui/desktop/openapi.json,api/*Testing