Skip to content

[goose2] MCP Apps: hydrate and replay app payloads in Goose2#8632

Open
aharvard wants to merge 5 commits intocodex/translate-acp-host-capabilitiesfrom
codex/mcp-apps-step2-tool-resource-association
Open

[goose2] MCP Apps: hydrate and replay app payloads in Goose2#8632
aharvard wants to merge 5 commits intocodex/translate-acp-host-capabilitiesfrom
codex/mcp-apps-step2-tool-resource-association

Conversation

@aharvard
Copy link
Copy Markdown
Collaborator

@aharvard aharvard commented Apr 17, 2026

Category: improvement
User Impact: Goose2 can now show a session-scoped MCP App payload in chat and replay that same payload when an older chat is reopened.
Stacked On: #8623
Problem: PR #8623 establishes the MCP Apps capability handshake and host identity, but Goose2 still needed a durable way to surface an app payload after an MCP tool call completes. Without that, Goose2 had no backend-owned, replayable representation of the MCP App resource associated with a completed tool interaction.
Solution: This PR introduces backend-owned MCP App association and resource hydration in Goose runtime. When an app-capable tool completes, Goose runtime reads the associated ui:// resource, stores the resulting snapshot on tool_result.meta.goose.mcpApp, and forwards that through ACP tool_call_update._meta.goose for both live updates and replay. Goose2 now renders that payload through McpAppView as structured JSON; the next step is replacing that payload view with the real HTML renderer and bridge.

Sequence

sequenceDiagram
    participant UI as "Goose 2 (ACP client / MCP Apps host)"
    participant ACP as "goose serve (ACP server)"
    participant Runtime as "Goose runtime (MCP client)"
    participant MCP as "MCP server"

    UI->>ACP: prompt
    ACP->>Runtime: agent.reply(...)
    Runtime->>MCP: callTool(...)
    MCP-->>Runtime: CallToolResult
    Runtime->>MCP: readResource(sessionId, ui://...)
    MCP-->>Runtime: ReadResourceResult
    Runtime->>Runtime: attach tool_result.meta.goose.mcpApp
    Runtime-->>ACP: completed ToolResponse
    ACP-->>UI: tool_call_update + _meta.goose.mcpApp
    UI->>UI: build McpApp payload
    UI->>UI: render McpAppView (JSON for now)

    Note over UI,ACP: later, on session reload
    UI->>ACP: loadSession(...)
    ACP->>ACP: replay stored ToolResponse from thread history
    ACP-->>UI: replayed tool_call_update + _meta.goose.mcpApp
    UI->>UI: reconstruct McpAppView from replay
Loading

What This PR Introduces

This is intentionally the "payload-first" step.

This PR is the first backend implementation of MCP App payload hydration and replay in Goose2. Its job is to create a stable, session-scoped payload at tool-completion time, persist that payload with the tool result, and let Goose2 reconstruct the same MCP App view later when a session is reopened.

The current McpAppView is deliberately a structured payload view rather than the final HTML renderer. That keeps this PR focused on the data model, persistence, and replay path. The next PR can then build the real app surface on top of that payload with the iframe, proxy, and host bridge behavior.

Other ACP Clients

Replayed MCP App payloads should remain part of session history even when an older Goose2-created session is later opened by another ACP client. Clients that do not render MCP Apps can ignore _meta.goose.mcpApp, while the session-scoped capability advertisement from #8623 still matters for new prompts: it prevents Goose runtime from doing fresh MCP App resource reads for a client that does not currently advertise MCP Apps support.

Reproduction Steps

  1. Add and activate this MCP server as a Goose extension: https://mcp-app-bench.onrender.com/mcp
  2. Start a new session and prompt run the app bench
  3. Note the JSON payload in view
image

This is part 2 of 3, tracked in #8591

@aharvard aharvard changed the title hydrate and replay mcp app payloads in goose2 [goose2] MCP Apps: hydrate and replay app payloads in Goose2 Apr 17, 2026
@aharvard aharvard marked this pull request as ready for review April 17, 2026 20:50
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bce545c6cc

ℹ️ 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".

Comment thread ui/goose2/src/shared/api/acpNotificationHandler.ts Outdated
Comment thread ui/goose2/src/shared/api/mcpAppToolUpdate.ts Outdated
@aharvard aharvard force-pushed the codex/mcp-apps-step2-tool-resource-association branch from bce545c to 8a24b3c Compare April 20, 2026 14:40
@aharvard aharvard force-pushed the codex/translate-acp-host-capabilities branch from 7a3aad5 to 6a2c3c1 Compare April 22, 2026 13:03
@aharvard aharvard force-pushed the codex/mcp-apps-step2-tool-resource-association branch from 1ebb8db to 14d0b06 Compare April 22, 2026 13:03
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14d0b06d3a

ℹ️ 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".

Comment thread ui/goose2/src/shared/api/acpNotificationHandler.ts Outdated
@aharvard aharvard force-pushed the codex/translate-acp-host-capabilities branch from 6a2c3c1 to 058800e Compare April 22, 2026 15:26
@aharvard aharvard force-pushed the codex/mcp-apps-step2-tool-resource-association branch from 14d0b06 to 4e7991e Compare April 22, 2026 16:15
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e7991ea2f

ℹ️ 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".

Comment on lines 1537 to 1539
let tools = self.get_all_tools_cached(session_id).await.map_err(|e| {
ErrorData::new(
ErrorCode::INTERNAL_ERROR,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep prefixed tool dispatch independent of tool listing failures

In resolve_tool, the new unconditional get_all_tools_cached(session_id) call can fail before the prefixed fallback branch runs, so a valid call like ext__tool now returns an internal error whenever tool listing fails (for example, due to an unrelated extension timing out). The previous behavior allowed prefixed tool calls to route directly to the owning client without requiring a successful global tool refresh, so this change makes dispatch reliability depend on list-tools health rather than call-tool availability.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this reproduces with the current implementation. resolve_tool now intentionally checks the listed tool set first so prefixed calls still respect available_tools filtering and preserve MCP app metadata. Also, fetch_all_tools() already degrades individual list_tools failures to warnings/empty results, so unrelated extension timeouts don’t usually cause get_all_tools_cached() to fail before the prefixed fallback runs. The remaining coupling is only on a true top-level internal cache/list failure, which is much narrower than the scenario described.

Signed-off-by: Andrew Harvard <aharvard@squareup.com>
Signed-off-by: Andrew Harvard <aharvard@squareup.com>
Signed-off-by: Andrew Harvard <aharvard@squareup.com>
Signed-off-by: Andrew Harvard <aharvard@squareup.com>
Signed-off-by: Andrew Harvard <aharvard@squareup.com>
@aharvard aharvard force-pushed the codex/mcp-apps-step2-tool-resource-association branch from 4e7991e to b62e2f1 Compare April 24, 2026 20:03
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b62e2f1c6e

ℹ️ 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".

Comment on lines 301 to 303
case "tool_call": {
const messageId = findStreamingMessageId(sessionId);
if (!messageId) break;
const messageId = ensureLiveAssistantMessage(sessionId, gooseSessionId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard tool events behind an active streaming message

Using ensureLiveAssistantMessage(...) in the tool_call path now creates a new assistant message even when no stream is active. That regresses the previous behavior (which dropped tool events when streamingMessageId was null) and can surface ghost assistant bubbles if late tool notifications arrive after a stop/cancel race, because useChat clears streamingMessageId before backend events are fully drained. The same helper is used in tool_call_update, so both request/update events can be misattributed to a newly created message instead of being ignored.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants