Skip to content

feat(MCP UI) chat widgets#2052

Open
dimetron wants to merge 21 commits into
kagent-dev:mainfrom
dimetron:feature/chat-mcp-ui-widgets
Open

feat(MCP UI) chat widgets#2052
dimetron wants to merge 21 commits into
kagent-dev:mainfrom
dimetron:feature/chat-mcp-ui-widgets

Conversation

@dimetron

Copy link
Copy Markdown
Contributor

This pull request adds support for rendering interactive MCP UI widgets (MCP Apps) directly within the chat interface. It introduces backend and frontend changes to detect, handle, and render tools that provide UI resources, ensuring a richer and more interactive user experience. The backend now distinguishes between regular tools and those with UI widgets, compacts tool responses sent to the model to prevent unnecessary repeated calls, and exposes new endpoints for MCP-app interactions. The UI is updated to render these widgets inline and broker interactions between the app and the chat.

Backend: MCP App tool detection and handling

  • The CreateToolsets function in registry.go now returns both the toolsets and a set of tool names that support MCP Apps, enabling the agent to treat these tools differently. [1] [2] [3] [4] [5] [6]
  • A new callback (MakeMCPAppModelResultCallback in mcp_apps.go) is added to compact the payload sent to the model for MCP App tools, replacing heavy render payloads with a terminal notice to prevent the model from re-invoking rendering tools unnecessarily. [1] [2]
  • The agent is updated to wire in the MCP App callback only when MCP App tools are present, ensuring efficient handling.

Testing and validation

  • Comprehensive unit tests are added in mcp_apps_test.go to verify correct compaction of responses, error handling, and that only MCP App tools are affected by the new logic.

Documentation and design

  • A detailed design document (EP-2046-chat-mcp-ui-widgets.md) describes the motivation, goals, implementation details, test plan, and open questions for this feature.

Copilot AI review requested due to automatic review settings June 18, 2026 17:19
@github-actions github-actions Bot added the enhancement-proposal Indicates that this PR is for an enhancement proposal label Jun 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds end-to-end support for rendering interactive MCP UI widgets (“MCP Apps”) inline in the chat UI, including: backend endpoints to proxy MCP app tool/resource calls, agent-side toolset detection and model-payload compaction for UI-capable tools, and frontend components/context to render and broker interactions with the embedded app.

Changes:

  • Backend: detect MCP App-capable tools, filter app-only tools, and compact model-visible tool results to avoid repeated render/poll loops.
  • Backend: add /api/mcp-apps/... endpoints to list tools, call tools, and read ui:// resources from RemoteMCPServer.
  • Frontend: render MCP apps within tool call UI; add MCP app inspector route; add (adjacent) file attachments + chat minimap UI.

Reviewed changes

Copilot reviewed 32 out of 33 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
ui/src/lib/messageHandlers.ts Preserves raw tool results + surfaces file artifacts and file parts for rendering on reload.
ui/src/lib/fileUpload.ts Adds client-side file allowlist/size guard and conversion to A2A FilePart.
ui/src/components/ToolDisplay.tsx Adds MCP App renderer embedding for completed tool calls with UI resources.
ui/src/components/mcp/McpServersView.tsx Adds “MCP Apps” navigation entry for servers.
ui/src/components/mcp-apps/McpAppsInspector.tsx Adds a UI to browse/invoke MCP App-capable tools and render their resources.
ui/src/components/mcp-apps/McpAppRenderer.tsx Implements the sandboxed MCP app renderer and host↔app bridging.
ui/src/components/chat/ToolCallDisplay.tsx Threads MCP app metadata + raw tool results into the tool call display pipeline.
ui/src/components/chat/FileAttachment.tsx Renders file parts as thumbnails/download chips.
ui/src/components/chat/ChatMinimap.tsx Adds a scroll minimap for chat history navigation.
ui/src/components/chat/ChatMessage.tsx Renders file attachments and forwards MCP app hooks into tool-call rendering.
ui/src/components/chat/ChatMcpAppsContext.tsx Adds chat-level registry and routing logic for MCP App-capable tools.
ui/src/components/chat/ChatInterface.tsx Wires MCP app message/tool-call promotion, file upload UI, and minimap integration.
ui/src/components/chat/tests/ChatMcpAppsContext.test.tsx Unit tests for app/tool registration behavior and app-only filtering.
ui/src/app/servers/[namespace]/[name]/apps/page.tsx Adds a route for the MCP Apps inspector.
ui/src/app/api/mcp-apps/[namespace]/[name]/tools/[toolName]/call/route.ts Next.js API proxy for MCP app tool calls.
ui/src/app/api/mcp-apps/[namespace]/[name]/resources/route.ts Next.js API proxy for MCP app resource reads.
ui/src/app/api/mcp-apps/_utils.ts Shared proxy helpers + OPTIONS handler for MCP apps API routes.
ui/src/app/actions/mcp-apps.ts Server actions for listing tools, calling tools, and reading resources.
ui/src/app/actions/tests/mcp-apps.test.ts Unit tests for MCP apps server actions path encoding and payloads.
ui/public/sandbox_proxy.html Adds the sandbox proxy document used by the MCP app iframe renderer.
ui/public/mockServiceWorker.js Updates MSW generated worker version.
ui/package.json Adds MCP UI / MCP SDK dependencies.
ui/package-lock.json Locks new MCP UI / MCP SDK dependency tree.
go/core/internal/httpserver/server.go Adds route wiring for MCP Apps endpoints.
go/core/internal/httpserver/handlers/mcpapps.go Implements MCP Apps list-tools/call-tool/read-resource handlers.
go/core/internal/httpserver/handlers/handlers.go Registers the new MCPApps handler.
go/build.err Adds a build log artifact (should not be committed).
go/adk/pkg/mcp/registry.go Toolset creation now identifies MCP App tools and filters app-only tools.
go/adk/pkg/mcp/registry_test.go Adds tests for MCP app detection and app-only visibility logic.
go/adk/pkg/agent/mcp_apps.go Adds model-result compaction callback for MCP App tool renders.
go/adk/pkg/agent/mcp_apps_test.go Tests for model-result compaction behavior.
go/adk/pkg/agent/agent.go Wires MCP App callback only when MCP App-capable tools are present.
design/EP-2046-chat-mcp-ui-widgets.md Design doc describing goals, approach, and test plan.
Files not reviewed (1)
  • ui/package-lock.json: Generated file
Comments suppressed due to low confidence (1)

go/build.err:39

  • go/build.err looks like a locally-captured build log and is now stale (it reports CreateToolsets returning 2 values while the PR updates agent.go accordingly). This file shouldn't be committed; it will also confuse CI/reviewers about the current build state. Please remove it from the PR (and rely on CI logs instead).
# github.com/kagent-dev/kagent/go/adk/pkg/agent
adk/pkg/agent/agent.go:58:14: assignment mismatch: 1 variable but mcp.CreateToolsets returns 2 values


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ui/src/components/chat/ChatMinimap.tsx Outdated
Comment thread ui/public/sandbox_proxy.html
@dimetron dimetron marked this pull request as draft June 18, 2026 17:26
@dimetron dimetron changed the title feature/chat mcp UI widgets feat(MCP UI) chat widgets Jun 18, 2026
Comment thread go/adk/pkg/agent/mcp_apps.go
Comment thread go/adk/pkg/mcp/registry.go Outdated
Comment thread go/adk/pkg/mcp/registry.go Outdated
@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from 4371099 to 794c8f4 Compare June 19, 2026 04:55
@dimetron dimetron marked this pull request as ready for review June 19, 2026 11:16
@dimetron dimetron requested a review from peterj June 19, 2026 11:16
@dimetron dimetron marked this pull request as draft June 19, 2026 12:28
@dimetron

Copy link
Copy Markdown
Contributor Author
Screenshot 2026-06-19 at 14 44 11

@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from 00d6a01 to 9943ffe Compare June 19, 2026 12:48
@dimetron dimetron marked this pull request as ready for review June 19, 2026 12:50
@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from 9943ffe to 09d6d13 Compare June 20, 2026 12:32
Comment thread go/adk/pkg/mcp/registry.go Outdated
Comment thread go/build.err Outdated
Comment thread ui/src/app/api/mcp-apps/[namespace]/[name]/resources/route.ts Outdated
Comment thread ui/src/components/chat/ChatInterface.tsx Outdated
Comment thread ui/src/components/chat/ChatInterface.tsx
Comment thread go/adk/pkg/agent/mcp_apps.go
Comment thread go/adk/pkg/mcp/registry.go Outdated
Comment thread ui/src/app/api/mcp-apps/[namespace]/[name]/tools/[toolName]/call/route.ts Outdated
@dimetron

Copy link
Copy Markdown
Contributor Author

Addressed review feedback in fc81d694 and 271361c0:

  • File upload split (fc81d694): removed fileUpload.ts, FileAttachment.tsx, and all chat upload/display wiring from this PR.
  • go/build.err: not present on the branch (removed earlier).
  • @/ imports: MCP apps API routes now use @/app/api/mcp-apps/_utils.
  • Strong types for tool meta (271361c0): classify tools via mcpsdk.Meta + parsed mcpUIMetadata; MakeMCPAppModelResultCallback takes mcp.MCPAppToolNames.
  • Voice input: still present — handleSendMessage stops listening before send (line ~429) and the mic button remains in the composer.

Re: collecting MCP app tool names during CreateToolsets — we can't defer this to CreateGoogleADKAgentWithSubagentSessionIDs without a second ListTools round trip per server. The MCP-UI metadata (ui.resourceUri, visibility) lives on mcpsdk.Tool.Meta, which ADK's mcptoolset drops when converting to tool.Tool, so classification has to happen at the initial ListTools call. fc81d694 already simplified the plumbing by accumulating into a shared MCPAppToolNames map instead of threading per-server return values through addToolsetCreateToolsets.

@dimetron dimetron requested a review from peterj June 23, 2026 12:34
@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from 5bd26fc to ec65548 Compare June 23, 2026 12:38
dimetron added 3 commits June 25, 2026 00:25
Classify MCP App-capable tools during toolset creation and compact
model-visible app tool results so the agent does not re-invoke render
tools. Mirrors the Go ADK MCP Apps integration.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Render MCP App tools as interactive UI widgets via @mcp-ui/client,
sandboxed through a same-origin proxy. Add chat MCP Apps context,
server actions for tool/resource proxying, and inline widget updates
so app-initiated tool calls reuse the same chat widget. List MCP Apps
alongside tools in the servers view and add chat minimap/layout fixes.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Add a RemoteMCPServer and Agent that exercise the public Server
Everything reference MCP server, including its dual-visibility
weather-dashboard UI tool, for verifying the chat MCP UI widget
integration end to end.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from caf3e0f to 1831b16 Compare June 24, 2026 22:26

@EItanya EItanya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is looking like a great start. I just have some notes and clarifying questions to make sure we get to a clean final state

Comment thread go/adk/pkg/mcp/registry.go Outdated
// the model-result compaction callback (see agent.MakeMCPAppModelResultCallback)
// only to these tools. Collect them from CreateToolsets output via
// MCPAppToolNamesFromToolsets.
type MCPAppToolNames map[string]bool

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need a type alias for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not strictly required — MCPAppToolNames is a named map[string]bool that travels across a few signatures (agentVisibleToolFiltermcpAppToolsetMCPAppToolNamesFromToolsetsagent.MakeMCPAppModelResultCallback). The alias gives that value one documented meaning ("set of model-visible tool names that render as MCP App widgets; value always true, only key presence matters") instead of a bare map[string]bool at each call site, and it's the natural home for that doc comment. No behavior is attached. Happy to inline it back to map[string]bool if you'd prefer fewer named types — let me know.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would prefer removing it. The use of a custom type here is a bit arbitrary and the variables will be named anyway.

Comment thread go/adk/pkg/mcp/registry.go Outdated
Comment on lines +314 to +319
// mcpToolKindApp is an agent-visible tool whose result renders as an
// interactive MCP App (UI) widget in the chat (declares a ui.resourceUri).
mcpToolKindApp
// mcpToolKindAppOnly is hidden from the agent and only callable from within
// the rendered MCP App (visibility declares "app" but not "model").
mcpToolKindAppOnly

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These names are confusing to me. So to be clear on purpose, mcpToolKindAppOnly is a tool which is only meant to be called from within the UI element itself?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly — mcpToolKindAppOnly is a tool that is hidden from the model and only invoked from within the rendered MCP App itself (e.g. the widget's own refresh/drill-down buttons). It maps to _meta.ui.visibility: ["app"] without "model".

To make the kinds clearer and align with the spec's visibility vocabulary ("model" / "app"), I renamed mcpToolKindAgentmcpToolKindModel:

  • mcpToolKindModel — regular tool visible to the model (the LLM/agent); no UI. (visibility "model" or absent)
  • mcpToolKindApp — model-visible and declares _meta.ui.resourceUri, so it renders an MCP App widget; the model may call it too.
  • mcpToolKindAppOnly — visible to the app only, hidden from the model.

Each const now has a doc comment tying it back to the _meta.ui fields, in the new mcp_ui.go.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The names are still confusing to me. How about mcpToolKindAppInternal?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 77501c0 — renamed mcpToolKindAppOnlymcpToolKindAppInternal (and its String() label to app_internal), and updated the classifier/filter usages, the registry_test.go cases, and the Python mirror's cross-reference comment. No behavior change.

Comment thread go/adk/pkg/mcp/registry.go Outdated
Comment thread go/adk/pkg/mcp/registry.go
Comment thread go/core/internal/httpserver/handlers/mcpapps.go
dimetron added a commit to dimetron/kagent that referenced this pull request Jun 26, 2026
Extract the MCP Apps (MCP UI) classification helpers out of registry.go
into a dedicated mcp_ui.go, with a file header linking the MCP Apps
extension overview and full spec so the _meta.ui contract is tracked in
one place. Rename mcpToolKindAgent -> mcpToolKindModel to match the spec's
_meta.ui.visibility vocabulary ("model" / "app"). No behavior change.

Addresses review feedback on PR kagent-dev#2052.
dimetron added 2 commits June 26, 2026 15:58
Extract the MCP Apps (MCP UI) classification helpers out of registry.go
into a dedicated mcp_ui.go, with a file header linking the MCP Apps
extension overview and full spec so the _meta.ui contract is tracked in
one place. Rename mcpToolKindAgent -> mcpToolKindModel to match the spec's
_meta.ui.visibility vocabulary ("model" / "app"). No behavior change.

Addresses review feedback on PR kagent-dev#2052.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Resolve send-guard conflict in ui/src/components/chat/ChatInterface.tsx by
adopting upstream's localMessages/comparable-message approach (kagent-dev#2034) over the
branch's earlier high-water-mark implementation. Also picks up configurable
stream timeout (kagent-dev#1973), go default declarative runtime (kagent-dev#2083), and controller
service annotations (kagent-dev#2088).
@dimetron dimetron force-pushed the feature/chat-mcp-ui-widgets branch from e768104 to fe4862a Compare June 26, 2026 13:58
dimetron added 3 commits June 26, 2026 20:45
…-widgets

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
…-widgets

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
…ge blocks

Merging upstream main swapped this branch's server-authoritative send guard
for a content-signature comparison (countSendGuardComparableMessages /
countBackendBackedComparableMessages). That comparison produced false
positives: after a normal turn the UI never reloads from the DB, so
live-streamed messages whose toolResultData.raw_result serializes slightly
differently from the DB copy (notably MCP App widget payloads) made
local < db and wrongly showed "New messages loaded - please review before
sending", blocking the send.

Restore the content-independent high-water mark: count persisted history
items across tasks (a value the DB assigns identically for every tab), set
it on load/reload and after each completed turn, and block only when the
server has advanced past the synced count (another tab acted). Drop the
now-dead comparable-message helpers and rewrite the send-guard tests.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
@dimetron

Copy link
Copy Markdown
Contributor Author

Restored the high-water-mark send guard (commit 9ab5717)

Heads up on the latest push. The merge from main pulled in the content-signature send guard from #2034 (countSendGuardComparableMessages / countBackendBackedComparableMessages), and on this branch it regressed once the MCP UI widgets were in play.

Symptom: after a normal chat turn, the next send was blocked with "New messages loaded — please review before sending" even though nothing had changed and no other tab was open.

Root cause: that guard compares the content signature of locally rendered messages against messages re-extracted from the DB. After a turn the UI does not call reloadSessionFromDB(), so the live-streamed messages stay in memory. Their toolResultData.raw_result serializes slightly differently from the freshly-extracted DB copy — most visibly for MCP App widgets, whose structuredContent payloads are large and nested. So localCount < dbCount and the guard fired a false positive. This also masked the kanban show_task_progress widget: the tool call was blocked before it could ever run.

Fix: reverted to the content-independent high-water mark this branch used before the merge. It counts persisted history items across tasks (task.history.length) — a value the DB assigns identically for every tab, independent of how each tab rendered them (synthetic tool/artifact/summary cards never land there). We set the mark on load/reload and re-read it after each completed turn (refreshServerMark), and block only when the server count exceeds the synced mark, i.e. another tab actually advanced the conversation.

Also removed the now-dead comparable-message helpers from messageHandlers.ts and rewrote the send-guard tests for the high-water behavior (same-tab no-block, cross-tab block, post-block proceed, Cmd/Ctrl+Enter). jest green (32/32), eslint clean.

@EItanya

EItanya commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Posting a quick summary from codex that I think is worth discussing. Specifically these are areas where it claims the implementation deviates from the spec. The answer to some of these may be TODO or other, but just wanted to make sure we discussed them.

  1. Backend lets app-originated calls invoke tools that may not be app-visible.
     /tmp/kagent-pr-2052/go/core/internal/httpserver/handlers/mcpapps.go:86 accepts any {toolName} and calls it directly at line 120. The UI has a client-side check, but it only
     throws when a registry entry exists and is neither app-only nor agent-visible; if the tool is unknown, it still calls the backend. See /tmp/kagent-pr-2052/ui/src/
     components/mcp-apps/McpAppRenderer.tsx:117. The spec says app tools/call requests must be rejected when the target tool does not include "app" visibility. This needs
     server-side enforcement.

  2. Sandbox/CSP handling is not compliant enough for untrusted UI.
     The spec requires web hosts to use an intermediate sandbox with a different origin and to enforce CSP from resource._meta.ui.csp, with restrictive defaults. This PR
     serves /sandbox_proxy.html from the same origin as the host and loads arbitrary HTML with document.write without applying CSP or permissions from resource metadata. See /
     tmp/kagent-pr-2052/ui/src/components/mcp-apps/McpAppRenderer.tsx:70 and /tmp/kagent-pr-2052/ui/public/sandbox_proxy.html:32. This is the biggest security/spec gap.

  3. MCP Apps capability negotiation is missing.
     The spec defines MCP Apps as an extension negotiated via capabilities.extensions["io.modelcontextprotocol/ui"] with supported MIME types. The Go ADK discovery client
     connects with nil options at /tmp/kagent-pr-2052/go/adk/pkg/mcp/mcp_ui.go:194, and the backend MCP Apps proxy does the same at /tmp/kagent-pr-2052/go/core/internal/
     httpserver/handlers/mcpapps.go:256. A conformant server that only registers UI tools when the client advertises support may not expose them to kagent.

  4. Python runtime path does not appear to hide app-only tools from the model.
     The Go path explicitly filters visibility: ["app"] out of the agent-visible tool list in /tmp/kagent-pr-2052/go/adk/pkg/mcp/mcp_ui.go:215. The Python path records tools
     with mcp_app_resource_uri, but I don’t see visibility parsing/filtering in /tmp/kagent-pr-2052/python/packages/kagent-adk/src/kagent/adk/_mcp_toolset.py:146. Unless
     upstream Google ADK already enforces _meta.ui.visibility, Python agents may expose app-only helper tools to the model.

…iation

Address MCP Apps spec-deviation review on PR kagent-dev#2052:

- Reject app-originated tools/call for tools whose _meta.ui.visibility
  excludes "app" (server-side in HandleCallTool) instead of trusting the
  client check. Default ["model","app"] stays app-callable.
- Sandbox proxy builds and injects a Content-Security-Policy from the
  resource's _meta.ui.csp (spec restrictive default + object-src 'none'
  when absent); McpAppRenderer forwards the declared csp via the sandbox prop.
- Advertise the io.modelcontextprotocol/ui extension capability (mime type
  text/html;profile=mcp-app) on both Go MCP clients so capability-gated
  servers expose UI tools.
- KAgentMcpToolset.get_tools hides app-only tools from the model, mirroring
  the Go ADK filter, while keeping them app-callable.

Cross-origin sandbox isolation (serving the proxy from a separate origin)
is tracked as a follow-up.

Signed-off-by: Dmytro Rashko <dimetron@me.com>
@dimetron

Copy link
Copy Markdown
Contributor Author

MCP Apps spec-deviation review — addressed in cf88dc10

Critical review of the four flagged items, with status, blocking impact, and the fix shipped on this branch. None of the fixes change how widgets render or break the server-everything demo.

# Claim Status Severity Blocks merge? Fix possible? Fix description
1 Backend lets app-originated calls invoke non-app-visible tools Valid High (security) Yes Yes — small HandleCallTool lists tools on the session it already opens, parses _meta.ui.visibility, and rejects only when visibility is present and lacks "app" (default / ["app"] / ["model","app"] stay allowed). Client check kept as defense-in-depth.
2a Sandbox applies no CSP (ignores ui.csp) Valid High (security) Yes Yes — small @mcp-ui/client already forwards CSP (sandbox-resource-ready {html, csp} + ?csp=). Host reads _meta.ui.csp and passes it via the sandbox prop; sandbox_proxy.html injects a <meta http-equiv="Content-Security-Policy"> with the spec restrictive default + object-src 'none'. Default keeps script-src 'self' 'unsafe-inline', so widgets still render.
2b Sandbox is same-origin as host (allow-same-origin → no isolation) Valid High (security) Follow-up Architectural Serve the proxy from a distinct origin so allow-same-origin no longer resolves to the host origin. Deploy/topology change — tracked as a follow-up; boundary stays soft until it lands.
3 MCP Apps capability negotiation missing Valid (SHOULD) Low (interop) No Yes — tiny Both Go MCP clients now advertise io.modelcontextprotocol/ui (mimeTypes: ["text/html;profile=mcp-app"]) via ClientCapabilities.Extensions. Purely additive.
4 Python runtime doesn't hide app-only tools from the model Valid Medium (correctness) Yes Yes — small KAgentMcpToolset.get_tools drops tools whose visibility is ["app"] without "model" from the model-visible list (still app-callable), mirroring the Go ADK filter.

Implemented (1, 2a, 3, 4)

  • 1toolAllowsAppCall + visibilityAllowsApp in go/core/internal/httpserver/handlers/mcpapps.go; app calls to non-app-visible tools now return 403 (404 for unknown). Test: TestVisibilityAllowsApp.
  • 2aui/public/sandbox_proxy.html builds + injects a spec CSP; McpAppRenderer captures _meta.ui.csp and forwards it via sandbox.
  • 3mcpUIClientCapabilities in go/adk/pkg/mcp/mcp_ui.go and the connect client in mcpapps.go.
  • 4 — filter in python/.../_mcp_toolset.py. Test: test_get_tools_hides_app_only_tools_from_model.

Follow-up (2b)

Cross-origin sandbox isolation is a deployment/topology change, left as a tracked follow-up.

Verified locally: Go (core + adk) build & tests, Python unit tests (16 passed), UI lint + jest MCP-apps suites.

dimetron added 2 commits June 30, 2026 01:36
…-widgets

Signed-off-by: Dmytro Rashko <dimetron@me.com>

# Conflicts:
#	ui/src/components/chat/AskUserDisplay.tsx
#	ui/src/components/chat/ChatInterface.tsx
Replace the manual visibility loop with slices.Contains in
visibilityAllowsApp (go-lint modernize). Also record the same-origin
sandbox decision (review item 2b) as a ponytail ceiling note in
McpAppRenderer so the accepted trade-off and upgrade path are explicit.

Signed-off-by: Dmytro Rashko <dimetron@me.com>
@EItanya

EItanya commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

FYI @dimetron, this is what's failing the snyk job:
Screenshot 2026-06-30 at 08 55 56

EItanya and others added 5 commits June 30, 2026 08:56
Address review feedback on PR kagent-dev#2052: "AppOnly" read ambiguously, so use
"AppInternal" to make clear this kind is hidden from the model and only
callable from within the rendered MCP App itself. Updates the String()
label, classification/filter usages, tests, and the Python mirror's
cross-reference comment. No behavior change.

Signed-off-by: Dmytro Rashko <dimetron@me.com>
Pin qs >=6.15.2 (resolves to 6.15.3) to remediate the qs.stringify DoS
advisory GHSA-q8mj-m7cp-5q26 flagged by the Snyk PR check, and bump
fast-uri to ^3.1.3. Both are dependency overrides only; no source change.

Signed-off-by: Dmytro Rashko <dimetron@me.com>
…-widgets

Resolve the send-guard conflict in
ui/src/components/chat/__tests__/ChatInterface.sendGuard.test.tsx by keeping
this branch's high-water-mark guard suite. Upstream kagent-dev#2115 fixed the
content-signature guard's tool-call false positive, but this branch already
replaced that guard with a content-independent high-water mark (counts persisted
history items), which is immune to the same class of false positives — including
the MCP App widget payload serialization differences that regressed the
content-signature guard. The upstream test additions exercise the removed
content-signature helpers, so they are dropped here.

Keep upstream's non-conflicting changes: extractMessagesFromTasks contextId/taskId
backfill (messageHandlers.ts), and the A2A executor / Python event_converter id
stamping (defense-in-depth, harmless under the high-water-mark guard).

Signed-off-by: Dmytro Rashko <dimetron@me.com>
Comment on lines +210 to +215
// resolveRemoteMCPServer locates the MCP endpoint for the given ref, supporting
// both RemoteMCPServer (external URL) and the kmcp MCPServer CRD (an in-cluster
// Deployment+Service). An MCPServer is converted to the same RemoteMCPServer
// shape the controller uses for tool discovery, so both kinds share one connect
// path.
func (h *MCPAppsHandler) resolveRemoteMCPServer(ctx context.Context, namespace, name string) (*v1alpha2.RemoteMCPServer, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are a number of issues with this:

  1. The name makes it seem like it's only RemoteMcpServer
  2. The behavior is confusing, if there is a RemoteMCPServer with the same name/ns as an MCPServer than it will be queried, even if the user intended to get the MCPServer

Comment thread go/adk/pkg/mcp/mcp_ui.go Outdated
// attaches the model-result compaction callback (see
// agent.MakeMCPAppModelResultCallback) only to these tools. Collect them from
// CreateToolsets output via MCPAppToolNamesFromToolsets.
type MCPAppToolNames map[string]bool

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I thought we discussed getting rid of this type as it is just cosmetic.

dimetron added 2 commits July 2, 2026 17:10
…server by groupKind

Address EItanya's review on PR kagent-dev#2052:

- Remove the Go MCPAppToolNames type alias (a cosmetic map[string]bool) and
  use map[string]bool directly across mcpAppToolset, MCPAppToolNamesFromToolsets,
  and MakeMCPAppModelResultCallback. Variable names already carry the meaning.
- Rename resolveRemoteMCPServer -> resolveMCPServerEndpoint and take a groupKind
  so a RemoteMCPServer and an MCPServer sharing a namespace/name resolve to the
  CRD the caller actually selected instead of always preferring RemoteMCPServer.
  The name no longer implies RemoteMCPServer only. The UI threads the selected
  server's groupKind through the MCP Apps list/call/read endpoints; an empty
  groupKind preserves the legacy RemoteMCPServer-then-MCPServer fallback.

Signed-off-by: Dmytro Rashko <dimetron@me.com>
@dimetron

dimetron commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@EItanya ready for another look when you get a chance. Both threads from your Jul 1 review are addressed in edab7e7:

  • resolveRemoteMCPServer → renamed to resolveMCPServerEndpoint (no longer implies RemoteMCPServer-only) and now takes a groupKind. Resolution switches on the kind: MCPServer reads only the MCPServer, RemoteMCPServer reads only that one — so a RemoteMCPServer and MCPServer sharing a namespace/name no longer collide. The UI threads the selected server's groupKind through the list/call/read endpoints; an empty groupKind keeps the legacy RemoteMCPServer-then-MCPServer fallback for older callers.
  • MCPAppToolNames type dropped — now plain map[string]bool across mcpAppToolset, MCPAppToolNamesFromToolsets, and MakeMCPAppModelResultCallback; the variables carry the meaning.

Branch is up to date with main and all CI is green.

…-widgets

Signed-off-by: Dmytro Rashko <dimetron@me.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement-proposal Indicates that this PR is for an enhancement proposal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants