From 900698777bf02ecb5b64d104ff4925de230b0b5e Mon Sep 17 00:00:00 2001 From: Andrew Harvard Date: Fri, 1 Aug 2025 08:09:06 -0400 Subject: [PATCH] integrate MCP UI (#2948) Co-authored-by: Michael Neale --- crates/goose/src/providers/claude_code.rs | 1 - ui/desktop/index.html | 10 +- ui/desktop/package-lock.json | 160 +++++++++++++----- ui/desktop/package.json | 1 + .../src/components/MCPUIResourceRenderer.tsx | 73 ++++++++ .../src/components/ToolCallWithResponse.tsx | 54 ++++-- .../components/context_management/index.ts | 2 +- ui/desktop/src/main.ts | 4 +- ui/desktop/src/types/message.ts | 13 +- 9 files changed, 256 insertions(+), 62 deletions(-) create mode 100644 ui/desktop/src/components/MCPUIResourceRenderer.tsx diff --git a/crates/goose/src/providers/claude_code.rs b/crates/goose/src/providers/claude_code.rs index 93a419beee..833fd4547a 100644 --- a/crates/goose/src/providers/claude_code.rs +++ b/crates/goose/src/providers/claude_code.rs @@ -518,7 +518,6 @@ impl Provider for ClaudeCodeProvider { mod tests { use super::ModelConfig; use super::*; - use temp_env::with_var; #[test] fn test_claude_code_model_config() { diff --git a/ui/desktop/index.html b/ui/desktop/index.html index fc995f1ba9..32e2a9dbc5 100644 --- a/ui/desktop/index.html +++ b/ui/desktop/index.html @@ -1,12 +1,12 @@ - - + + Goose - +
- + \ No newline at end of file diff --git a/ui/desktop/package-lock.json b/ui/desktop/package-lock.json index d75c026d57..88b8419055 100644 --- a/ui/desktop/package-lock.json +++ b/ui/desktop/package-lock.json @@ -12,6 +12,7 @@ "@ai-sdk/openai": "^0.0.72", "@ai-sdk/ui-utils": "^1.0.2", "@hey-api/client-fetch": "^0.8.1", + "@mcp-ui/client": "^5.3.1", "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-dialog": "^1.1.7", @@ -2677,11 +2678,24 @@ "node": ">= 12.13.0" } }, + "node_modules/@mcp-ui/client": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@mcp-ui/client/-/client-5.3.1.tgz", + "integrity": "sha512-COcpCwV+SKd2m9Me++aG9Xm1sb4WMyl+Ak4cepe4EGzMqypjdn5f+KbOMGzs7itFOWhf8LVxeMTFsbeI38dQvg==", + "license": "Apache-2.0", + "dependencies": { + "@modelcontextprotocol/sdk": "*", + "@quilted/threads": "^3.1.3", + "@remote-dom/core": "^1.8.0", + "@remote-dom/react": "^1.2.2", + "react": "^18.3.1", + "react-dom": "^18.3.1" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.15.1.tgz", "integrity": "sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.6", @@ -2705,7 +2719,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "dev": true, "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -2719,7 +2732,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -2740,7 +2752,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -2753,7 +2764,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.6.0" @@ -2763,7 +2773,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" @@ -2773,7 +2782,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -2816,7 +2824,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -2834,7 +2841,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -2844,7 +2850,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2857,7 +2862,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -2867,7 +2871,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2880,7 +2883,6 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2890,7 +2892,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -2903,7 +2904,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2913,7 +2913,6 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -2929,7 +2928,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.5", @@ -2952,7 +2950,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "dev": true, "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -2968,7 +2965,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "dev": true, "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -3102,6 +3098,48 @@ "dev": true, "license": "MIT" }, + "node_modules/@preact/signals-core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.11.0.tgz", + "integrity": "sha512-jglbibeWHuFRzEWVFY/TT7wB1PppJxmcSfUHcK+2J9vBRtiooMfw6tAPttojNYrrpdGViqAYCbPpmWYlMm+eMQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@quilted/events": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@quilted/events/-/events-2.1.3.tgz", + "integrity": "sha512-4fHaSLND8rmZ+tce9/4FNmG5UWTRpFtM54kOekf3tLON4ZLLnYzjjldELD35efd7+lT5+E3cdkacqc56d+kCrQ==", + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@quilted/threads": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@quilted/threads/-/threads-3.3.1.tgz", + "integrity": "sha512-0ASnjTH+hOu1Qwzi9NnsVcsbMhWVx8pEE8SXIHknqcc/1rXAU0QlKw9ARq0W43FAdzyVeuXeXtZN27ZC0iALKg==", + "license": "MIT", + "dependencies": { + "@quilted/events": "^2.1.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@preact/signals-core": "^1.8.0" + }, + "peerDependenciesMeta": { + "@preact/signals-core": { + "optional": true + } + } + }, "node_modules/@radix-ui/colors": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", @@ -4641,6 +4679,61 @@ } } }, + "node_modules/@remote-dom/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@remote-dom/core/-/core-1.9.0.tgz", + "integrity": "sha512-h8OO2NRns2paXO/q5hkfXrwlZKq7oKj9XedGosi7J8OP3+aW7N2Gv4MBBVVQGCfOiZPkOj5m3sQH7FdyUWl7PQ==", + "license": "MIT", + "dependencies": { + "@remote-dom/polyfill": "^1.4.4", + "htm": "^3.1.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@preact/signals-core": "^1.3.0" + }, + "peerDependenciesMeta": { + "@preact/signals-core": { + "optional": true + }, + "preact": { + "optional": true + } + } + }, + "node_modules/@remote-dom/polyfill": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@remote-dom/polyfill/-/polyfill-1.4.5.tgz", + "integrity": "sha512-V1qkKIl/wXyDO0I+tQDH06cBBNyyViZF3IYorkTTBf58dorqOP5Ta51vCCWeekPgdSOPuEKvHhvu6kAaKqVgww==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@remote-dom/react": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@remote-dom/react/-/react-1.2.2.tgz", + "integrity": "sha512-PkvioODONTr1M0StGDYsR4Ssf5M0Rd4+IlWVvVoK3Zrw8nr7+5mJkgNofaj/z7i8Aep78L28PCW8/WduUt4unA==", + "license": "MIT", + "dependencies": { + "@remote-dom/core": "^1.7.0", + "@types/react": "^18.0.0", + "htm": "^3.1.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.19", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", @@ -6620,7 +6713,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -8020,7 +8112,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -9871,7 +9962,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "dev": true, "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -9893,7 +9983,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" @@ -10003,7 +10092,6 @@ "version": "7.5.1", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 16" @@ -10077,7 +10165,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -10114,7 +10201,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -11153,6 +11239,12 @@ "dev": true, "license": "ISC" }, + "node_modules/htm": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", + "integrity": "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==", + "license": "Apache-2.0" + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -11811,7 +11903,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true, "license": "MIT" }, "node_modules/is-reference": { @@ -12019,7 +12110,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -12318,7 +12408,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -14933,7 +15022,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -15307,7 +15395,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15460,7 +15547,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16.20.0" @@ -15780,7 +15866,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -15925,7 +16010,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -15941,7 +16025,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -16856,7 +16939,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -16873,7 +16955,6 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -17204,7 +17285,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -17217,7 +17297,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -18850,7 +18929,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -19492,7 +19570,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -19742,7 +19819,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/ws": { diff --git a/ui/desktop/package.json b/ui/desktop/package.json index c6fb1dc6c6..3986c0305d 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -41,6 +41,7 @@ "@ai-sdk/openai": "^0.0.72", "@ai-sdk/ui-utils": "^1.0.2", "@hey-api/client-fetch": "^0.8.1", + "@mcp-ui/client": "^5.3.1", "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-dialog": "^1.1.7", diff --git a/ui/desktop/src/components/MCPUIResourceRenderer.tsx b/ui/desktop/src/components/MCPUIResourceRenderer.tsx new file mode 100644 index 0000000000..8734cbc519 --- /dev/null +++ b/ui/desktop/src/components/MCPUIResourceRenderer.tsx @@ -0,0 +1,73 @@ +import { UIResourceRenderer, UIActionResult } from '@mcp-ui/client'; +import { ResourceContent } from '../types/message'; +import { useState, useCallback } from 'react'; + +// Extend UIActionResult to include size-change type +type ExtendedUIActionResult = + | UIActionResult + | { + type: 'size-change'; + payload: { + height: string; + }; + }; + +interface MCPUIResourceRendererProps { + content: ResourceContent; +} + +export default function MCPUIResourceRenderer({ content }: MCPUIResourceRendererProps) { + console.log('MCPUIResourceRenderer', content); + const [iframeHeight, setIframeHeight] = useState('200px'); + + const handleUIAction = useCallback(async (result: ExtendedUIActionResult) => { + console.log('Handle action from MCP UI Action:', result); + + // Handle UI actions here + switch (result.type) { + case 'intent': + // TODO: Implement intent handling + break; + + case 'link': + // TODO: Implement link handling + break; + + case 'notify': + // TODO: Implement notification handling + break; + + case 'prompt': + // TODO: Implement prompt handling + break; + + case 'tool': + // TODO: Implement tool handling + break; + + // Currently, `size-change` is non-standard + case 'size-change': { + // We expect the height to be a string with a unit + console.log('Setting iframe height to:', result.payload.height); + setIframeHeight(result.payload.height); + break; + } + } + + return { status: 'handled' }; + }, []); + + return ( +
+
+ +
+
+ ); +} diff --git a/ui/desktop/src/components/ToolCallWithResponse.tsx b/ui/desktop/src/components/ToolCallWithResponse.tsx index edf896a868..f88840ef2a 100644 --- a/ui/desktop/src/components/ToolCallWithResponse.tsx +++ b/ui/desktop/src/components/ToolCallWithResponse.tsx @@ -6,8 +6,9 @@ import { Content, ToolRequestMessageContent, ToolResponseMessageContent } from ' import { cn, snakeToTitleCase } from '../utils'; import Dot, { LoadingStatus } from './ui/Dot'; import { NotificationEvent } from '../hooks/useMessageStream'; -import { ChevronRight, LoaderCircle } from 'lucide-react'; +import { ChevronRight, FlaskConical, LoaderCircle } from 'lucide-react'; import { TooltipWrapper } from './settings/providers/subcomponents/buttons/TooltipWrapper'; +import MCPUIResourceRenderer from './MCPUIResourceRenderer'; interface ToolCallWithResponseProps { isCancelledMessage: boolean; @@ -30,15 +31,47 @@ export default function ToolCallWithResponse({ } return ( -
- -
+ <> +
+ +
+ {/* MCP UI — Inline */} + {toolResponse?.toolResult?.value && + toolResponse.toolResult.value.map((content, index, results) => { + if (content.type === 'resource' && content.resource.uri?.startsWith('ui://')) { + if (index === results.length - 1) { + return ( + <> + + {/* Append a disclaimer if this is the last item in the array */} +
+ +
+ MCP UI is experimental and may change at any time. +
+
+ + ); + } else { + return ; + } + } else { + return null; + } + })} + ); } @@ -536,6 +569,7 @@ function ToolResultView({ result, isStartExpanded }: ToolResultViewProps) { }} /> )} + {result.type === 'resource' &&
{JSON.stringify(result, null, 2)}
} ); diff --git a/ui/desktop/src/components/context_management/index.ts b/ui/desktop/src/components/context_management/index.ts index 22f6533b8d..1f656adbf4 100644 --- a/ui/desktop/src/components/context_management/index.ts +++ b/ui/desktop/src/components/context_management/index.ts @@ -60,7 +60,7 @@ export function convertApiMessageToFrontendMessage( sendToLLM: sendToLLM ?? true, id: generateId(), role: apiMessage.role as Role, - created: apiMessage.created ?? 0, + created: apiMessage.created ?? Math.floor(Date.now() / 1000), content: apiMessage.content .map((apiContent) => mapApiContentToFrontendMessageContent(apiContent)) .filter((content): content is FrontendMessageContent => content !== null), diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 641da5cdbc..81e828610d 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -1727,8 +1727,8 @@ app.whenReady().then(async () => { "connect-src 'self' http://127.0.0.1:* https://api.github.com https://github.com https://objects.githubusercontent.com" + // Don't allow any plugins "object-src 'none';" + - // Don't allow any frames - "frame-src 'none';" + + // Allow all frames (iframes) + "frame-src 'self' https: http:;" + // Font sources - allow self, data URLs, and external fonts "font-src 'self' data: https:;" + // Media sources - allow microphone diff --git a/ui/desktop/src/types/message.ts b/ui/desktop/src/types/message.ts index 1a83938c89..3c45bc75d7 100644 --- a/ui/desktop/src/types/message.ts +++ b/ui/desktop/src/types/message.ts @@ -18,7 +18,18 @@ export interface ImageContent { annotations?: Record; } -export type Content = TextContent | ImageContent; +export interface ResourceContent { + type: 'resource'; + resource: { + uri: string; + mimeType: string; + text?: string; + blob?: string; + }; + annotations?: Record; +} + +export type Content = TextContent | ImageContent | ResourceContent; export interface ToolCall { name: string;