Skip to content

[BUG] MCP Apps compaction blanks tool results for headless agents, even when the result contains no UI resource #2519

Description

@aagronow-playon

What happens

kagent replaces an MCP tool's result with MCP_APP_RENDERED_NOTICE whenever the tool's definition declares _meta.ui.resourceUri. It does this even when the result itself contains no UI resource and carries the full text payload. The model receives the notice instead of the data.

Classification comes from the tool definition, not from the result.

Version

0.10.0-rc3, the current release.

kagent-controller   ghcr.io/kagent-dev/kagent/controller:0.10.0-rc3
agent pod           ghcr.io/kagent-dev/kagent/golang-adk:0.10.0-rc3

Reproduction

  1. RemoteMCPServer pointing at https://mcp.atlassian.com/v1/mcp, Atlassian API token via headersFrom.
  2. Declarative Agent, runtime: go, toolNames: [searchJiraIssuesUsingJql, getJiraIssue].
  3. Ask it to describe recent issues on a Jira board.

The agent replies that the results are displayed in an interactive format and states no field from them.

Evidence

1. The server returns the data

Using kagent's capability and client name:

curl -s -X POST https://mcp.atlassian.com/v1/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-06-18",
        "capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}},
        "clientInfo":{"name":"kagent-adk","version":"1"}}}'

tools/call on searchJiraIssuesUsingJql returns:

content block types: ['text']
has structuredContent: True
_meta keys: []
TEXT block: { "issues": [ { "id": "160217", "key": "GF-3687", ... } ] }

Text block with the full JSON, structuredContent present, no UI resource in the result.

2. The tool metadata is capability-gated

Same server, same token, two initialize calls.

capabilities: {} returns 13 tools, none carrying _meta.

capabilities containing io.modelcontextprotocol/ui returns 13 tools, and searchJiraIssuesUsingJql and getJiraIssue carry:

"_meta": {"ui": {"resourceUri": "ui://widget/jira-widget.html", "prefersBorder": true, ...}}

mcpUIClientCapabilities() in go/adk/pkg/mcp/mcp_ui.go advertises the extension on every connection. No per-server setting disables it.

3. kagent classifies from the definition

agentVisibleToolFilter reads mcpToolKindOf(t.Meta) during ListTools and records matching names in appToolNames. Agent log:

{"level":"info","caller":"agent/agent.go:111",
 "msg":"Wiring MCP App model result callback","toolCount":2}

Both of the agent's two tools.

4. The callback discards the payload

compact_mcp_app_response:

compacted = {"content": [{"type": "text", "text": MCP_APP_RENDERED_NOTICE}]}
if "_meta" in response:
    compacted["_meta"] = response["_meta"]
return compacted

The result is not inspected. go/adk/pkg/agent/mcp_apps.go does the same.

The notice reads: "The interactive UI for this tool has been rendered to the user and now updates live inside the app. Treat this as complete and do not call this tool again unless the user explicitly asks for it."

5. Session history keeps the real payload

The compaction runs in before_model against a deep copy, so GET /api/sessions/<id> shows the full tool output. Inspecting a session does not reveal the problem.

Scope

Any agent invoked over A2A or kagent invoke against a server that declares UI tools. There is no widget rendered and no human present, so the model is the only consumer of the tool result.

#2046 scopes MCP Apps to the chat interface. design/EP-2046-chat-mcp-ui-widgets.md lists per-server opt-in as an open question.

Possible fixes

  1. Compact only when the result contains a UI resource, rather than when the definition declares one.
  2. Keep the text content alongside the notice instead of replacing it.
  3. Allow a RemoteMCPServer to opt out of the UI capability.

Checking whether an agent is affected

kubectl logs -n kagent deploy/<agent> | grep "Wiring MCP App model result callback"

A non-zero toolCount means the model does not receive those tools' output.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions