mirror of
https://github.com/block/goose.git
synced 2026-07-09 16:09:22 +00:00
chore: Remove legacy MCP-UI proxy support (#10086)
This commit is contained in:
parent
fe7f16b727
commit
f81a0e2471
32 changed files with 62 additions and 1280 deletions
|
|
@ -13,7 +13,6 @@ pub async fn check_token(
|
|||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
if request.uri().path() == "/status"
|
||||
|| request.uri().path() == "/mcp-ui-proxy"
|
||||
|| request.uri().path() == "/mcp-app-proxy"
|
||||
|| request.uri().path() == "/mcp-app-guest"
|
||||
{
|
||||
|
|
|
|||
|
|
@ -394,7 +394,6 @@ derive_utoipa!(IconTheme as IconThemeSchema);
|
|||
super::routes::status::status,
|
||||
super::routes::status::system_info,
|
||||
super::routes::status::diagnostics,
|
||||
super::routes::mcp_ui_proxy::mcp_ui_proxy,
|
||||
super::routes::config_management::validate_config,
|
||||
super::routes::config_management::upsert_config,
|
||||
super::routes::config_management::remove_config,
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
use axum::{
|
||||
extract::Query,
|
||||
http::{header, StatusCode},
|
||||
response::{Html, IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ProxyQuery {
|
||||
secret: String,
|
||||
}
|
||||
|
||||
const MCP_UI_PROXY_HTML: &str = include_str!("templates/mcp_ui_proxy.html");
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/mcp-ui-proxy",
|
||||
params(
|
||||
("secret" = String, Query, description = "Secret key for authentication")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "MCP UI proxy HTML page", content_type = "text/html"),
|
||||
(status = 401, description = "Unauthorized - invalid or missing secret"),
|
||||
)
|
||||
)]
|
||||
async fn mcp_ui_proxy(
|
||||
axum::extract::State(secret_key): axum::extract::State<String>,
|
||||
Query(params): Query<ProxyQuery>,
|
||||
) -> Response {
|
||||
if params.secret != secret_key {
|
||||
return (StatusCode::UNAUTHORIZED, "Unauthorized").into_response();
|
||||
}
|
||||
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
|
||||
(
|
||||
header::HeaderName::from_static("referrer-policy"),
|
||||
"no-referrer",
|
||||
),
|
||||
],
|
||||
Html(MCP_UI_PROXY_HTML),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub fn routes(secret_key: String) -> Router {
|
||||
Router::new()
|
||||
.route("/mcp-ui-proxy", get(mcp_ui_proxy))
|
||||
.with_state(secret_key)
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ pub mod errors;
|
|||
#[cfg(feature = "local-inference")]
|
||||
pub mod local_inference;
|
||||
pub mod mcp_app_proxy;
|
||||
pub mod mcp_ui_proxy;
|
||||
pub mod prompts;
|
||||
pub mod recipe;
|
||||
pub mod recipe_utils;
|
||||
|
|
@ -38,7 +37,6 @@ pub fn configure(state: Arc<crate::state::AppState>, secret_key: String) -> Rout
|
|||
.merge(schedule::routes(state.clone()))
|
||||
.merge(setup::routes(state.clone()))
|
||||
.merge(telemetry::routes(state.clone()))
|
||||
.merge(mcp_ui_proxy::routes(secret_key.clone()))
|
||||
.merge(mcp_app_proxy::routes(secret_key))
|
||||
.merge(session_events::routes(state.clone()))
|
||||
.merge(sampling::routes(state.clone()))
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="referrer" content="no-referrer"/>
|
||||
<!--
|
||||
Permissive CSP so nested content is not constrained by top-level Goose Desktop CSP
|
||||
- default-src: Fallback for other directives (allows same-origin)
|
||||
- script-src: Allow scripts from any origin, inline, eval, wasm, and blob URLs
|
||||
- style-src: Allow styles from any origin and inline styles
|
||||
- font-src: Allow fonts from any origin
|
||||
- img-src: Allow images from any origin
|
||||
- connect-src: Allow network requests to any origin
|
||||
- frame-src: Allow embedding iframes from any origin (required for proxy functionality)
|
||||
- media-src: Allow audio/video media from any origin
|
||||
- base-uri: Restrict <base> tag to same-origin only
|
||||
- upgrade-insecure-requests: Automatically upgrade HTTP to HTTPS
|
||||
-->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src * 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob:; style-src * 'unsafe-inline'; font-src *; img-src *; connect-src *; frame-src *; media-src *; base-uri 'self'; upgrade-insecure-requests"/>
|
||||
<title>MCP-UI Proxy</title>
|
||||
<style>
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
iframe {
|
||||
background-color: transparent;
|
||||
border: 0 none transparent;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const contentType = params.get('contentType');
|
||||
const target = params.get('url');
|
||||
|
||||
// Validate that the URL is a valid HTTP or HTTPS URL
|
||||
function isValidHttpUrl(string) {
|
||||
try {
|
||||
const url = new URL(string);
|
||||
return url.protocol === 'http:' || url.protocol === 'https:';
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType === 'rawhtml') {
|
||||
// Double-iframe raw HTML mode (HTML sent via postMessage)
|
||||
const inner = document.createElement('iframe');
|
||||
inner.style = 'width:100%; height:100%; border:none;';
|
||||
// sandbox will be set from postMessage payload; default minimal before html arrives
|
||||
inner.setAttribute('sandbox', 'allow-scripts');
|
||||
document
|
||||
.body
|
||||
.appendChild(inner);
|
||||
|
||||
// Wait for HTML content from parent
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.source === window.parent) {
|
||||
if (event.data && event.data.type === 'ui-html-content') {
|
||||
const payload = event.data.payload || {};
|
||||
const html = payload.html;
|
||||
const sandbox = payload.sandbox;
|
||||
if (typeof sandbox === 'string') {
|
||||
inner.setAttribute('sandbox', sandbox);
|
||||
}
|
||||
if (typeof html === 'string') {
|
||||
inner.srcdoc = html;
|
||||
}
|
||||
} else {
|
||||
if (inner && inner.contentWindow) {
|
||||
inner
|
||||
.contentWindow
|
||||
.postMessage(event.data, '*');
|
||||
}
|
||||
}
|
||||
} else if (event.source === inner.contentWindow) {
|
||||
// Relay messages from inner to parent
|
||||
window
|
||||
.parent
|
||||
.postMessage(event.data, '*');
|
||||
}
|
||||
});
|
||||
|
||||
// Notify parent that proxy is ready to receive HTML (distinct event)
|
||||
window
|
||||
.parent
|
||||
.postMessage({
|
||||
type: 'ui-proxy-iframe-ready'
|
||||
}, '*');
|
||||
} else if (target) {
|
||||
if (!isValidHttpUrl(target)) {
|
||||
document.body.textContent = 'Error: invalid URL. Only HTTP and HTTPS URLs are allowed.';
|
||||
} else {
|
||||
const inner = document.createElement('iframe');
|
||||
inner.src = target;
|
||||
inner.style = 'width:100%; height:100%; border:none;';
|
||||
// Default external URL sandbox; can be adjusted later by protocol if needed
|
||||
inner.setAttribute('sandbox', 'allow-same-origin allow-scripts');
|
||||
document
|
||||
.body
|
||||
.appendChild(inner);
|
||||
const urlOrigin = new URL(target).origin;
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.source === window.parent) {
|
||||
// listen for messages from the parent and send them to the iframe
|
||||
if (inner.contentWindow) {
|
||||
inner
|
||||
.contentWindow
|
||||
.postMessage(event.data, urlOrigin);
|
||||
} else {
|
||||
console.warn('[MCP-UI Proxy] iframe contentWindow is not available; message not sent');
|
||||
}
|
||||
} else if (event.source === inner.contentWindow) {
|
||||
// listen for messages from the iframe and send them to the parent
|
||||
window
|
||||
.parent
|
||||
.postMessage(event.data, '*');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
document.body.textContent = 'Error: missing url or html parameter';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"label": "MCP Apps and MCP-UI",
|
||||
"label": "MCP Apps",
|
||||
"position": 55,
|
||||
"link": {
|
||||
"type": "doc",
|
||||
|
|
|
|||
|
|
@ -1,43 +1,35 @@
|
|||
---
|
||||
title: Rich Interactive Chat with MCP Apps and MCP-UI
|
||||
title: Rich Interactive Chat with MCP Apps
|
||||
hide_title: true
|
||||
description: Build interactive UI applications that render inside goose Desktop using MCP Apps and MCP-UI
|
||||
description: Build interactive UI applications that render inside goose Desktop using MCP Apps
|
||||
---
|
||||
|
||||
import Card from '@site/src/components/Card';
|
||||
import styles from '@site/src/components/Card/styles.module.css';
|
||||
import VideoCarousel from '@site/src/components/VideoCarousel';
|
||||
import Card from "@site/src/components/Card";
|
||||
import styles from "@site/src/components/Card/styles.module.css";
|
||||
import VideoCarousel from "@site/src/components/VideoCarousel";
|
||||
|
||||
<h1 className={styles.pageTitle}>Rich Interactive Chat with MCP Apps and MCP-UI</h1>
|
||||
<h1 className={styles.pageTitle}>Rich Interactive Chat with MCP Apps</h1>
|
||||
<p className={styles.pageDescription}>
|
||||
goose Desktop supports extensions that transform text-only responses into graphical, interactive experiences. Instead of reading through lists and descriptions, you can click, explore, and interact with UI components directly in your conversations.
|
||||
goose Desktop supports extensions that transform text-only responses into
|
||||
graphical, interactive experiences. Instead of reading through lists and
|
||||
descriptions, you can click, explore, and interact with UI components directly
|
||||
in your conversations.
|
||||
</p>
|
||||
|
||||
<div className="video-container margin-bottom--lg">
|
||||
<iframe
|
||||
class="aspect-ratio"
|
||||
src="https://www.youtube.com/embed/QJHGvsVXhjw"
|
||||
title="Turn Any AI Chat Into an Interactive Experience | MCP-UI"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<div className={styles.categorySection}>
|
||||
<h2 className={styles.categoryTitle}>📚 Documentation & Guides</h2>
|
||||
<div className={styles.cardGrid}>
|
||||
<Card
|
||||
<Card
|
||||
title="Building MCP Apps"
|
||||
description="Step-by-step tutorial to create interactive UI applications that render inside goose Desktop."
|
||||
link="/docs/tutorials/building-mcp-apps"
|
||||
/>
|
||||
<Card
|
||||
title="Using MCP Apps and MCP-UI"
|
||||
description="goose transforms text-based responses into engaging graphical and interactive user experiences."
|
||||
<Card
|
||||
title="Using MCP Apps"
|
||||
description="Launch standalone apps and render interactive UI directly in goose Desktop conversations."
|
||||
link="/docs/guides/interactive-chat/mcp-ui"
|
||||
/>
|
||||
<Card
|
||||
<Card
|
||||
title="Auto Visualiser Extension"
|
||||
description="Generate interactive data visualizations automatically in goose."
|
||||
link="/docs/mcp/autovisualiser-mcp"
|
||||
|
|
@ -45,74 +37,36 @@ import VideoCarousel from '@site/src/components/VideoCarousel';
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.categorySection}>
|
||||
<h2 className={styles.categoryTitle}>🎥 Videos</h2>
|
||||
|
||||
<VideoCarousel
|
||||
id="mcp-apps-videos"
|
||||
videos={[
|
||||
{
|
||||
type: "iframe",
|
||||
src: "https://youtube.com/embed/QcojQ3Fwqsw",
|
||||
title: "What are MCP Apps?",
|
||||
description:
|
||||
"What MCP Apps are and how they render interactive UIs in goose",
|
||||
duration: "1:53",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.categorySection}>
|
||||
<h2 className={styles.categoryTitle}>📝 Featured Blog Posts</h2>
|
||||
<div className={styles.cardGrid}>
|
||||
<Card
|
||||
<Card
|
||||
title="From MCP-UI to MCP Apps: Evolving Interactive Agent UIs"
|
||||
description="A practical migration guide to MCP Apps: the 4 key changes, what broke, and why this shift matters."
|
||||
link="/blog/2026/01/22/mcp-ui-to-mcp-apps"
|
||||
/>
|
||||
<Card
|
||||
<Card
|
||||
title="goose Lands MCP Apps"
|
||||
description="goose ships early support for the draft MCP Apps specification, aligning with the emerging standard for interactive UIs."
|
||||
link="/blog/2026/01/06/mcp-apps"
|
||||
/>
|
||||
<Card
|
||||
title="MCP UI: Bringing the Browser into the Agent"
|
||||
description="MCP-UI servers return content that goose Desktop renders as rich, embeddable UI."
|
||||
link="/blog/2025/08/11/mcp-ui-post-browser-world"
|
||||
/>
|
||||
<Card
|
||||
title="MCP-UI: The Future of Agentic Interfaces"
|
||||
description="AI agents need to move beyond walls of text to rich and interactive UX."
|
||||
link="/blog/2025/08/25/mcp-ui-future-agentic-interfaces"
|
||||
/>
|
||||
<Card
|
||||
title="Auto Visualiser with MCP-UI"
|
||||
description="Automatically render visual representations of data as you interact with it, powered by MCP-UI"
|
||||
link="/blog/2025/08/27/autovisualiser-with-mcp-ui"
|
||||
/>
|
||||
<Card
|
||||
title="How to Make An MCP Server MCP-UI Compatible"
|
||||
description="Making an MCP server MCP-UI compatible requires just a few lines of code"
|
||||
link="/blog/2025/09/08/turn-any-mcp-server-mcp-ui-compatible"
|
||||
/>
|
||||
<Card
|
||||
title="Designing AI for Users, Not Just LLMs"
|
||||
description="Building intent-based AI experiences with MCP-UI."
|
||||
link="/blog/2025/10/14/designing-ai-for-humans"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.categorySection}>
|
||||
<h2 className={styles.categoryTitle}>🎥 More Videos</h2>
|
||||
|
||||
<VideoCarousel
|
||||
id="more-videos"
|
||||
videos={[
|
||||
{
|
||||
type: 'iframe',
|
||||
src: 'https://youtube.com/embed/QcojQ3Fwqsw',
|
||||
title: 'What are MCP Apps?',
|
||||
description: 'Why MCP Apps are different from MCP-UI and what makes them portable, secure, and standardized',
|
||||
duration: '1:53'
|
||||
},
|
||||
{
|
||||
type: 'iframe',
|
||||
src: 'https://youtube.com/embed/Kxj-vFBO_9U',
|
||||
title: 'Building A MCP-UI Server',
|
||||
description: 'Learn how to build your own MCP-UI server from scratch',
|
||||
duration: '40:07'
|
||||
},
|
||||
{
|
||||
type: 'iframe',
|
||||
src: 'https://youtube.com/embed/GS-kmreZDgU',
|
||||
title: 'Livestream - MCP-UI: The Future of Agentic Interfaces',
|
||||
description: 'The goose team talks with MCP-UI creators about the future of visual interfaces',
|
||||
duration: '55:32'
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,24 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
title: Using MCP Apps and MCP-UI
|
||||
sidebar_label: Using MCP Apps and MCP-UI
|
||||
description: Learn how goose renders interactive UI components from MCP Apps and MCP-UI extensions
|
||||
title: Using MCP Apps
|
||||
sidebar_label: Using MCP Apps
|
||||
description: Learn how goose renders interactive UI components from MCP Apps extensions
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import GooseDesktopInstaller from '@site/src/components/GooseDesktopInstaller';
|
||||
import CLIExtensionInstructions from '@site/src/components/CLIExtensionInstructions';
|
||||
import { PanelLeft } from 'lucide-react';
|
||||
|
||||
# Using MCP Apps and MCP-UI
|
||||
# Using MCP Apps
|
||||
|
||||
Extensions built with MCP Apps or MCP-UI allow goose Desktop to provide interactive and engaging user experiences. Instead of reading text responses and typing prompts, you can interact with a graphical and clickable UI.
|
||||
Extensions built with MCP Apps allow goose Desktop to provide interactive and engaging user experiences. Instead of reading text responses and typing prompts, you can interact with a graphical and clickable UI.
|
||||
|
||||
:::info MCP Apps is the official specification
|
||||
[MCP Apps](/docs/tutorials/building-mcp-apps) is now the official MCP specification for interactive UIs. MCP-UI extensions still work in goose, but MCP Apps is the recommended path for new extensions.
|
||||
[MCP Apps](/docs/tutorials/building-mcp-apps) is the official MCP specification for interactive UIs. Use MCP Apps for new interactive extensions.
|
||||
:::
|
||||
|
||||
:::warning Experimental Features
|
||||
The features described in this topic are experimental and in active development. Behavior and support may change in future releases.
|
||||
:::
|
||||
|
||||
## MCP Apps
|
||||
|
||||
MCP Apps bring interactive interfaces to goose through the official [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps). Depending on the extension, apps can be launched in standalone, sandboxed windows or embedded in your chat window.
|
||||
|
||||
### Launching Apps in Standalone Windows
|
||||
|
|
@ -71,49 +65,6 @@ If needed, you can just ask goose whether the UI can be loaded in the chat windo
|
|||
</video>
|
||||
</div>
|
||||
|
||||
## MCP-UI
|
||||
|
||||
MCP-UI is an earlier specification for interactive UIs that renders content embedded in your chat. While MCP Apps is now the recommended approach, MCP-UI extensions continue to work in goose.
|
||||
|
||||
### Try It Out
|
||||
|
||||
See how interactive responses work in goose. For this exercise, we'll add an extension that connects to [MCP-UI Demos](https://mcp-aharvard.netlify.app/) provided by Andrew Harvard.
|
||||
|
||||
<Tabs groupId="interface">
|
||||
<TabItem value="ui" label="goose Desktop" default>
|
||||
<GooseDesktopInstaller
|
||||
extensionId="richdemo"
|
||||
extensionName="Rich Demo"
|
||||
description="Demo interactive extension"
|
||||
type="http"
|
||||
url="https://mcp-aharvard.netlify.app/mcp"
|
||||
/>
|
||||
</TabItem>
|
||||
<TabItem value="cli" label="goose CLI">
|
||||
<CLIExtensionInstructions
|
||||
name="rich_demo"
|
||||
description="Demo interactive extension"
|
||||
type="http"
|
||||
url="https://mcp-aharvard.netlify.app/mcp"
|
||||
timeout={300}
|
||||
/>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
In goose Desktop, ask:
|
||||
|
||||
- `Help me select seats for my flight`
|
||||
|
||||
Instead of just text, you'll see an interactive response with:
|
||||
- A visual seat map with available and occupied seats
|
||||
- Real-time, clickable selection capabilities
|
||||
- A booking confirmation with flight details
|
||||
|
||||
Try out other demos:
|
||||
|
||||
- `Plan my next trip based on my mood`
|
||||
- `What's the weather in Philadelphia?`
|
||||
|
||||
## For Extension Developers
|
||||
|
||||
Add interactivity to your own extensions:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
# Building Custom Extensions with goose
|
||||
|
||||
|
||||
goose allows you to extend its functionality by creating your own custom extensions, which are built as MCP servers. These extensions are compatible with goose because it adheres to the [Model Context Protocol (MCP)][mcp-docs]. MCP is an open protocol that standardizes how applications provide context to LLMs. It enables a consistent way to connect LLMs to various data sources and tools, making it ideal for extending functionality in a structured and interoperable way.
|
||||
|
||||
In this guide, we build an MCP server using the [Python SDK for MCP][mcp-python]. We’ll demonstrate how to create an MCP server that reads Wikipedia articles and converts them to Markdown, integrate it as an extension in goose. You can follow a similar process to develop your own custom extensions for goose.
|
||||
|
|
@ -105,7 +104,7 @@ def read_wikipedia_article(url: str) -> str:
|
|||
# SSRF protection: only allow Wikipedia domains
|
||||
parsed = urlparse(url)
|
||||
hostname = parsed.netloc.lower()
|
||||
|
||||
|
||||
# Allow wikipedia.org or *.wikipedia.org subdomains only
|
||||
if hostname != 'wikipedia.org' and not hostname.endswith('.wikipedia.org'):
|
||||
raise ValueError(f"Only Wikipedia URLs are allowed. Got: {parsed.netloc}")
|
||||
|
|
@ -223,18 +222,18 @@ MCP Inspector requires Node.js and npm installed on your computer.
|
|||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Run your server in development mode:
|
||||
3. Run your server in development mode:
|
||||
|
||||
```bash
|
||||
mcp dev src/mcp_wiki/server.py
|
||||
```
|
||||
|
||||
|
||||
MCP Inspector should open automatically in your browser. On first run, you'll be prompted to install `@modelcontextprotocol/inspector`.
|
||||
|
||||
4. Test the tool:
|
||||
1. Click `Connect` to initialize your MCP server
|
||||
2. On the `Tools` tab, click `List Tools` and click the `read_wikipedia_article` tool
|
||||
3. Enter `https://en.wikipedia.org/wiki/Bangladesh` for the URL and click `Run Tool`
|
||||
3. Enter `https://en.wikipedia.org/wiki/Bangladesh` for the URL and click `Run Tool`
|
||||
|
||||
[](../assets/guides/custom-extension-mcp-inspector.png)
|
||||
|
||||
|
|
@ -242,16 +241,16 @@ MCP Inspector requires Node.js and npm installed on your computer.
|
|||
<TabItem value="cli" label="In the CLI">
|
||||
1. Set up the project environment:
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
2. Activate your virtual environment:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
|
||||
3. Install your project locally:
|
||||
|
||||
```bash
|
||||
|
|
@ -275,6 +274,7 @@ MCP Inspector requires Node.js and npm installed on your computer.
|
|||
options:
|
||||
-h, --help show this help message and exit
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -295,11 +295,13 @@ To add your MCP server as an extension in goose:
|
|||
4. Set the `Type` to `STDIO`
|
||||
5. Provide a name and description for your extension
|
||||
6. In the `Command` field, provide the absolute path to your executable:
|
||||
|
||||
```plaintext
|
||||
uv run /full/path/to/mcp-wiki/.venv/bin/mcp-wiki
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
uv run /Users/smohammed/Development/mcp/mcp-wiki/.venv/bin/mcp-wiki
|
||||
```
|
||||
|
|
@ -341,12 +343,14 @@ goose supports advanced MCP features that can enhance your extensions.
|
|||
**[MCP Sampling](/docs/guides/mcp-sampling)** allows your MCP servers to request AI completions from goose's LLM, transforming simple tools into intelligent agents.
|
||||
|
||||
**Key Benefits:**
|
||||
|
||||
- Your MCP server doesn't need its own OpenAI/Anthropic API key
|
||||
- Tools can analyze data, provide explanations, and make intelligent decisions
|
||||
- Enhanced user experience with smarter, more contextual responses
|
||||
- Secure by design: requests are isolated and attributed automatically
|
||||
|
||||
**Getting Started:**
|
||||
|
||||
- Use the `sampling/createMessage` method in your MCP server to request AI assistance
|
||||
- [goose's implementation](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/mcp_client.rs) currently supports text and image content types
|
||||
- goose automatically advertises sampling capability to all MCP servers
|
||||
|
|
@ -360,6 +364,7 @@ goose supports advanced MCP features that can enhance your extensions.
|
|||
**[MCP Apps](/docs/tutorials/building-mcp-apps)** enable rich, interactive user interfaces instead of text-only responses.
|
||||
|
||||
**Key Benefits:**
|
||||
|
||||
- Return interactive UI components from your MCP server tools
|
||||
- Components render securely in isolated sandboxes within goose Desktop
|
||||
- Real-time user interactions trigger callbacks to your server
|
||||
|
|
@ -368,10 +373,6 @@ goose supports advanced MCP features that can enhance your extensions.
|
|||
|
||||
**Learn More:** [Building MCP Apps Tutorial](/docs/tutorials/building-mcp-apps)
|
||||
|
||||
:::note
|
||||
goose also supports [MCP-UI](/docs/guides/interactive-chat/mcp-ui), but MCP Apps is the recommended path for new extensions.
|
||||
:::
|
||||
|
||||
[mcp-docs]: https://modelcontextprotocol.io/
|
||||
[mcp-python]: https://github.com/modelcontextprotocol/python-sdk
|
||||
[mcp-typescript]: https://github.com/modelcontextprotocol/typescript-sdk
|
||||
|
|
|
|||
|
|
@ -2041,33 +2041,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/mcp-ui-proxy": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"super::routes::mcp_ui_proxy"
|
||||
],
|
||||
"operationId": "mcp_ui_proxy",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "secret",
|
||||
"in": "query",
|
||||
"description": "Secret key for authentication",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP UI proxy HTML page"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - invalid or missing secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/recipes/decode": {
|
||||
"post": {
|
||||
"tags": [
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -3343,32 +3343,6 @@ export type SyncFeaturedModelsResponses = {
|
|||
200: unknown;
|
||||
};
|
||||
|
||||
export type McpUiProxyData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query: {
|
||||
/**
|
||||
* Secret key for authentication
|
||||
*/
|
||||
secret: string;
|
||||
};
|
||||
url: '/mcp-ui-proxy';
|
||||
};
|
||||
|
||||
export type McpUiProxyErrors = {
|
||||
/**
|
||||
* Unauthorized - invalid or missing secret
|
||||
*/
|
||||
401: unknown;
|
||||
};
|
||||
|
||||
export type McpUiProxyResponses = {
|
||||
/**
|
||||
* MCP UI proxy HTML page
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type DecodeRecipeData = {
|
||||
body: DecodeRecipeRequest;
|
||||
path?: never;
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export default function BaseChat({
|
|||
}
|
||||
}, [messages.length]);
|
||||
|
||||
// Listen for global scroll-to-bottom requests (e.g., from MCP UI prompt actions)
|
||||
// Listen for global scroll-to-bottom requests (e.g., from MCP App message actions)
|
||||
useEffect(() => {
|
||||
const handleGlobalScrollRequest = () => {
|
||||
// Add a small delay to ensure content has been rendered
|
||||
|
|
|
|||
|
|
@ -1,401 +0,0 @@
|
|||
import { AppEvents } from '../constants/events';
|
||||
import {
|
||||
UIResourceRenderer,
|
||||
UIActionResultIntent,
|
||||
UIActionResultLink,
|
||||
UIActionResultNotification,
|
||||
UIActionResultPrompt,
|
||||
UIActionResultToolCall,
|
||||
UIActionResult,
|
||||
} from '@mcp-ui/client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { EmbeddedResource } from '../api';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { errorMessage } from '../utils/conversionUtils';
|
||||
import { isProtocolSafe, getProtocol } from '../utils/urlSecurity';
|
||||
import { defineMessages, useIntl } from '../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
toastTitle: {
|
||||
id: 'mcpUIResourceRenderer.toastTitle',
|
||||
defaultMessage: 'MCP-UI {messageType} message',
|
||||
},
|
||||
toastMessageReceived: {
|
||||
id: 'mcpUIResourceRenderer.toastMessageReceived',
|
||||
defaultMessage: 'Message received for {message}.',
|
||||
},
|
||||
toastUnsupported: {
|
||||
id: 'mcpUIResourceRenderer.toastUnsupported',
|
||||
defaultMessage:
|
||||
"Message received for {message}. {messageType} messages aren't supported yet, refer to console for more details.",
|
||||
},
|
||||
openExternalLinkTitle: {
|
||||
id: 'mcpUIResourceRenderer.openExternalLinkTitle',
|
||||
defaultMessage: 'Open External Link',
|
||||
},
|
||||
openProtocolLink: {
|
||||
id: 'mcpUIResourceRenderer.openProtocolLink',
|
||||
defaultMessage: 'Open {protocol} link?',
|
||||
},
|
||||
openLinkDetail: {
|
||||
id: 'mcpUIResourceRenderer.openLinkDetail',
|
||||
defaultMessage: 'This will open: {url}',
|
||||
},
|
||||
cancelButton: {
|
||||
id: 'mcpUIResourceRenderer.cancelButton',
|
||||
defaultMessage: 'Cancel',
|
||||
},
|
||||
openButton: {
|
||||
id: 'mcpUIResourceRenderer.openButton',
|
||||
defaultMessage: 'Open',
|
||||
},
|
||||
});
|
||||
|
||||
interface MCPUIResourceRendererProps {
|
||||
content: EmbeddedResource & { type: 'resource' };
|
||||
appendPromptToChat?: (value: string) => void;
|
||||
}
|
||||
|
||||
// More specific result types using discriminated unions
|
||||
type UIActionHandlerSuccess<T = unknown> = {
|
||||
status: 'success';
|
||||
data?: T;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
type UIActionHandlerError = {
|
||||
status: 'error';
|
||||
error: {
|
||||
code: UIActionErrorCode;
|
||||
message: string;
|
||||
details?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
type UIActionHandlerPending = {
|
||||
status: 'pending';
|
||||
message: string;
|
||||
};
|
||||
|
||||
type UIActionHandlerResult<T = unknown> =
|
||||
| UIActionHandlerSuccess<T>
|
||||
| UIActionHandlerError
|
||||
| UIActionHandlerPending;
|
||||
|
||||
// Strongly typed error codes
|
||||
enum UIActionErrorCode {
|
||||
UNSUPPORTED_ACTION = 'UNSUPPORTED_ACTION',
|
||||
UNKNOWN_ACTION = 'UNKNOWN_ACTION',
|
||||
TOOL_NOT_FOUND = 'TOOL_NOT_FOUND',
|
||||
TOOL_EXECUTION_FAILED = 'TOOL_EXECUTION_FAILED',
|
||||
NAVIGATION_FAILED = 'NAVIGATION_FAILED',
|
||||
PROMPT_FAILED = 'PROMPT_FAILED',
|
||||
INTENT_FAILED = 'INTENT_FAILED',
|
||||
INVALID_PARAMS = 'INVALID_PARAMS',
|
||||
NETWORK_ERROR = 'NETWORK_ERROR',
|
||||
TIMEOUT = 'TIMEOUT',
|
||||
}
|
||||
|
||||
// toast component
|
||||
const ToastComponent = ({
|
||||
messageType,
|
||||
message,
|
||||
isImplemented = true,
|
||||
}: {
|
||||
messageType: string;
|
||||
message?: string;
|
||||
isImplemented?: boolean;
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const title = intl.formatMessage(i18n.toastTitle, { messageType });
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-0 py-2 pr-4">
|
||||
<p className="font-bold">{title}</p>
|
||||
{isImplemented ? (
|
||||
<p>
|
||||
{intl.formatMessage(i18n.toastMessageReceived, {
|
||||
message: <span className="font-bold">{message}</span>,
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
{intl.formatMessage(i18n.toastUnsupported, {
|
||||
message: <span className="font-bold">{message}</span>,
|
||||
messageType: messageType.charAt(0).toUpperCase() + messageType.slice(1),
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function MCPUIResourceRenderer({
|
||||
content,
|
||||
appendPromptToChat,
|
||||
}: MCPUIResourceRendererProps) {
|
||||
const intl = useIntl();
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [proxyUrl, setProxyUrl] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProxyUrl = async () => {
|
||||
try {
|
||||
const gooseApiHost = await window.electron.getGoosedHostPort();
|
||||
const secretKey = await window.electron.getSecretKey();
|
||||
if (gooseApiHost && secretKey) {
|
||||
setProxyUrl(`${gooseApiHost}/mcp-ui-proxy?secret=${encodeURIComponent(secretKey)}`);
|
||||
} else {
|
||||
console.error('Failed to get goosed host/port or secret key');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching MCP-UI Proxy URL:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchProxyUrl().catch(console.error);
|
||||
}, []);
|
||||
|
||||
const handleUIAction = async (actionEvent: UIActionResult): Promise<UIActionHandlerResult> => {
|
||||
// result to pass back to the MCP-UI
|
||||
let result: UIActionHandlerResult;
|
||||
|
||||
const handleToolAction = async (
|
||||
actionEvent: UIActionResultToolCall
|
||||
): Promise<UIActionHandlerResult> => {
|
||||
const { toolName, params } = actionEvent.payload;
|
||||
toast.info(<ToastComponent messageType="tool" message={toolName} isImplemented={false} />, {
|
||||
theme: resolvedTheme,
|
||||
});
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.UNSUPPORTED_ACTION,
|
||||
message: 'Tool calls are not yet implemented',
|
||||
details: { toolName, params },
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const handlePromptAction = async (
|
||||
actionEvent: UIActionResultPrompt
|
||||
): Promise<UIActionHandlerResult> => {
|
||||
const { prompt } = actionEvent.payload;
|
||||
|
||||
if (appendPromptToChat) {
|
||||
try {
|
||||
appendPromptToChat(prompt);
|
||||
window.dispatchEvent(new CustomEvent(AppEvents.SCROLL_CHAT_TO_BOTTOM));
|
||||
return {
|
||||
status: 'success' as const,
|
||||
message: 'Prompt sent to chat successfully',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.PROMPT_FAILED,
|
||||
message: 'Failed to send prompt to chat',
|
||||
details: errorMessage(error),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.UNSUPPORTED_ACTION,
|
||||
message: 'Prompt handling is not implemented - append prop is required',
|
||||
details: { prompt },
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const handleLinkAction = async (
|
||||
actionEvent: UIActionResultLink
|
||||
): Promise<UIActionHandlerResult> => {
|
||||
const { url } = actionEvent.payload;
|
||||
|
||||
try {
|
||||
// Safe protocols open directly, unknown protocols require user confirmation
|
||||
// Dangerous protocols are blocked by main.ts in the open-external handler
|
||||
if (isProtocolSafe(url)) {
|
||||
await window.electron.openExternal(url);
|
||||
return {
|
||||
status: 'success' as const,
|
||||
message: `Opened ${url} in default application`,
|
||||
};
|
||||
}
|
||||
|
||||
// Unknown protocols require user confirmation
|
||||
const protocol = getProtocol(url);
|
||||
if (!protocol) {
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.INVALID_PARAMS,
|
||||
message: `Invalid URL format: ${url}`,
|
||||
details: { url },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const result = await window.electron.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: [
|
||||
intl.formatMessage(i18n.cancelButton),
|
||||
intl.formatMessage(i18n.openButton),
|
||||
],
|
||||
defaultId: 0,
|
||||
title: intl.formatMessage(i18n.openExternalLinkTitle),
|
||||
message: intl.formatMessage(i18n.openProtocolLink, { protocol }),
|
||||
detail: intl.formatMessage(i18n.openLinkDetail, { url }),
|
||||
});
|
||||
|
||||
if (result.response !== 1) {
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.NAVIGATION_FAILED,
|
||||
message: 'User cancelled',
|
||||
details: { url },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
await window.electron.openExternal(url);
|
||||
return {
|
||||
status: 'success' as const,
|
||||
message: `Opened ${url} in default application`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.NAVIGATION_FAILED,
|
||||
message: `Failed to open URL: ${url}`,
|
||||
details: errorMessage(error),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotifyAction = async (
|
||||
actionEvent: UIActionResultNotification
|
||||
): Promise<UIActionHandlerResult> => {
|
||||
const { message } = actionEvent.payload;
|
||||
|
||||
toast.info(<ToastComponent messageType="notify" message={message} isImplemented={true} />, {
|
||||
theme: resolvedTheme,
|
||||
});
|
||||
return {
|
||||
status: 'success' as const,
|
||||
data: {
|
||||
displayedAt: new Date().toISOString(),
|
||||
message: 'Notification displayed',
|
||||
details: actionEvent.payload,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const handleIntentAction = async (
|
||||
actionEvent: UIActionResultIntent
|
||||
): Promise<UIActionHandlerResult> => {
|
||||
toast.info(
|
||||
<ToastComponent
|
||||
messageType="intent"
|
||||
message={actionEvent.payload.intent}
|
||||
isImplemented={false}
|
||||
/>,
|
||||
{
|
||||
theme: resolvedTheme,
|
||||
}
|
||||
);
|
||||
return {
|
||||
status: 'error' as const,
|
||||
error: {
|
||||
code: UIActionErrorCode.UNSUPPORTED_ACTION,
|
||||
message: 'Intent handling is not yet implemented',
|
||||
details: actionEvent.payload,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
switch (actionEvent.type) {
|
||||
case 'tool':
|
||||
result = await handleToolAction(actionEvent);
|
||||
break;
|
||||
|
||||
case 'prompt':
|
||||
result = await handlePromptAction(actionEvent);
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
result = await handleLinkAction(actionEvent);
|
||||
break;
|
||||
|
||||
case 'notify':
|
||||
result = await handleNotifyAction(actionEvent);
|
||||
break;
|
||||
|
||||
case 'intent':
|
||||
result = await handleIntentAction(actionEvent);
|
||||
break;
|
||||
|
||||
default: {
|
||||
const _exhaustiveCheck: never = actionEvent;
|
||||
console.error('Unhandled MCP-UI action type:', _exhaustiveCheck);
|
||||
result = {
|
||||
status: 'error',
|
||||
error: {
|
||||
code: UIActionErrorCode.UNKNOWN_ACTION,
|
||||
message: `Unknown action type`,
|
||||
details: actionEvent,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Unexpected error handling MCP-UI action:', error);
|
||||
result = {
|
||||
status: 'error',
|
||||
error: {
|
||||
code: UIActionErrorCode.UNKNOWN_ACTION,
|
||||
message: 'An unexpected error occurred',
|
||||
details: error instanceof Error ? error.stack : error,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-3 p-4 border border-border-primary rounded-lg bg-background-secondary">
|
||||
<div className="overflow-hidden rounded-sm">
|
||||
<UIResourceRenderer
|
||||
resource={content.resource}
|
||||
onUIAction={handleUIAction}
|
||||
supportedContentTypes={['rawHtml', 'externalUrl']} // Goose does not support remoteDom content
|
||||
htmlProps={{
|
||||
autoResizeIframe: {
|
||||
height: true,
|
||||
width: false, // set to false to allow for responsive design
|
||||
},
|
||||
iframeRenderData: {
|
||||
// iframeRenderData allows us to pass data down to MCP-UIs
|
||||
// MCP-UIs might find stuff like host and theme for conditional rendering
|
||||
// usage of this is experimental, leaving in place for demos
|
||||
host: 'goose',
|
||||
theme: resolvedTheme,
|
||||
},
|
||||
proxy: proxyUrl, // refer to https://mcpui.dev/guide/client/using-a-proxy
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -13,22 +13,16 @@ import {
|
|||
} from '../types/message';
|
||||
import { cn, snakeToTitleCase } from '../utils';
|
||||
import { LoadingStatus } from './ui/Dot';
|
||||
import { ChevronRight, ExternalLink, FlaskConical } from 'lucide-react';
|
||||
import { ChevronRight, ExternalLink } from 'lucide-react';
|
||||
import { TooltipWrapper } from './settings/providers/subcomponents/buttons/TooltipWrapper';
|
||||
import MCPUIResourceRenderer from './MCPUIResourceRenderer';
|
||||
import { isUIResource } from '@mcp-ui/client';
|
||||
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { ContentBlock, EmbeddedResource } from '../api';
|
||||
import type { ContentBlock } from '../api';
|
||||
|
||||
import McpAppRenderer from './McpApps/McpAppRenderer';
|
||||
import ToolApprovalButtons from './ToolApprovalButtons';
|
||||
import { defineMessages, useIntl } from '../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
mcpUiExperimental: {
|
||||
id: 'toolCallWithResponse.mcpUiExperimental',
|
||||
defaultMessage: 'MCP UI is experimental and may change at any time.',
|
||||
},
|
||||
viewSubagentSession: {
|
||||
id: 'toolCallWithResponse.viewSubagentSession',
|
||||
defaultMessage: 'View subagent session',
|
||||
|
|
@ -152,13 +146,6 @@ function getToolResultContent(toolResult: Record<string, unknown>): ContentBlock
|
|||
});
|
||||
}
|
||||
|
||||
function isEmbeddedResource(
|
||||
content: ContentBlock
|
||||
): content is EmbeddedResource & { type: 'resource' } {
|
||||
const c = content as Record<string, unknown>;
|
||||
return c.type === 'resource' && typeof c.resource === 'object' && c.resource !== null;
|
||||
}
|
||||
|
||||
interface McpAppWrapperProps {
|
||||
toolRequest: ToolRequestMessageContent;
|
||||
toolResponse?: ToolResponseMessageContent;
|
||||
|
|
@ -236,7 +223,6 @@ export default function ToolCallWithResponse({
|
|||
confirmationContent,
|
||||
isApprovalClicked,
|
||||
}: ToolCallWithResponseProps) {
|
||||
const intl = useIntl();
|
||||
// Handle both the wrapped ToolResult format and the unwrapped format
|
||||
// The server serializes ToolResult<T> as { status: "success", value: T } or { status: "error", error: string }
|
||||
const toolCallData = toolRequest.toolCall as Record<string, unknown>;
|
||||
|
|
@ -298,28 +284,6 @@ export default function ToolCallWithResponse({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* MCP UI — Inline */}
|
||||
{shouldShowMcpContent &&
|
||||
!hasMcpAppResourceURI &&
|
||||
toolResponse?.toolResult &&
|
||||
getToolResultContent(toolResponse.toolResult).map((content, index) => {
|
||||
if (!isEmbeddedResource(content)) return null;
|
||||
if (isUIResource(content)) {
|
||||
return (
|
||||
<div key={index} className="mt-3">
|
||||
<MCPUIResourceRenderer content={content} appendPromptToChat={append} />
|
||||
<div className="mt-3 p-4 py-3 border border-border-primary rounded-lg bg-background-secondary flex items-center">
|
||||
<FlaskConical className="mr-2" size={20} />
|
||||
<div className="text-sm font-sans">
|
||||
{intl.formatMessage(i18n.mcpUiExperimental)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
|
||||
{/* MCP App */}
|
||||
{shouldShowMcpContent && hasMcpAppResourceURI && sessionId && (
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Wiedergabe im Bild-in-Bild-Modus"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Abbrechen"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Öffnen"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Externen Link öffnen"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Dies öffnet: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "{protocol}-Link öffnen?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Nachricht für {message} empfangen."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI-Nachricht {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Nachricht für {message} empfangen. Nachrichten vom Typ {messageType} werden noch nicht unterstützt, weitere Details finden Sie in der Konsole."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# Element gefunden} other{# Elemente gefunden}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Protokolle"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI ist experimentell und kann sich jederzeit ändern."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Ausgabe"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Playing in Picture-in-Picture"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Cancel"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Open"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Open External Link"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "This will open: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Open {protocol} link?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Message received for {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType} message"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Message received for {message}. {messageType} messages aren't supported yet, refer to console for more details."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# item found} other{# items found}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Logs"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI is experimental and may change at any time."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Output"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Reproduciendo en imagen en imagen"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Cancelar"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Abrir"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Abrir enlace externo"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Esto abrirá: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "¿Abrir enlace {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Mensaje recibido para {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Mensaje {messageType} de MCP-UI"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Mensaje recibido para {message}. Los mensajes {messageType} aún no se admiten, consulta la consola para más detalles."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# elemento encontrado} other{# elementos encontrados}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Logs"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "La UI de MCP es experimental y puede cambiar en cualquier momento."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Salida"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Lecture en Picture-in-Picture"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Annuler"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Ouvrir"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Ouvrir le lien externe"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Cela ouvrira : {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Ouvrir le lien {protocol} ?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Message reçu pour {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Message MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Message reçu pour {message}. Les messages {messageType} ne sont pas encore pris en charge, consultez la console pour plus de détails."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# élément trouvé} other{# éléments trouvés}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Journaux"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "L'interface MCP est expérimentale et peut changer à tout moment."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Sortie"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "पिक्चर-इन-पिक्चर में बजाना"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "रद्द करें"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "खुला"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "बाहरी लिंक खोलें"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "यह खुलेगा: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "{protocol} लिंक खोलें?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "{message} के लिए संदेश प्राप्त हुआ।"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "एमसीपी-यूआई {messageType} संदेश"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "{message} के लिए संदेश प्राप्त हुआ। {messageType} संदेश अभी तक समर्थित नहीं हैं, अधिक विवरण के लिए कंसोल देखें।"
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# आइटम मिला} other{# आइटम मिले}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "लॉग"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP यूआई प्रयोगात्मक है और किसी भी समय बदल सकता है।"
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "आउटपुट"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Memutar dalam Picture-in-Picture"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Batal"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Buka"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Buka Tautan Eksternal"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Ini akan membuka: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Buka tautan {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Pesan diterima untuk {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Pesan MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Pesan diterima untuk {message}. Pesan {messageType} belum didukung, lihat konsol untuk detail lebih lanjut."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# item ditemukan} other{# item ditemukan}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Log"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI bersifat eksperimental dan dapat berubah sewaktu-waktu."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Keluaran"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "In riproduzione in Picture-in-Picture"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Annulla"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Apri"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Apri link esterno"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Questo aprirà: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Aprire il link {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Messaggio ricevuto per {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Messaggio MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Messaggio ricevuto per {message}. I messaggi {messageType} non sono ancora supportati, consulta la console per maggiori dettagli."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# elemento trovato} other{# elementi trovati}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Log"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "L'interfaccia MCP è sperimentale e può cambiare in qualsiasi momento."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Output"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "ピクチャ・イン・ピクチャで再生中"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "キャンセル"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "開く"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "外部リンクを開く"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "次を開きます: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "{protocol}リンクを開きますか?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "{message}のメッセージを受信しました。"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType}メッセージ"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "{message}のメッセージを受信しました。{messageType}メッセージはまだサポートされていません。詳細はコンソールを確認してください。"
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# 件見つかりました} other{# 件見つかりました}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "ログ"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UIは実験的な機能であり、予告なく変更される場合があります。"
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "出力"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "PIP(Picture-in-Picture)에서 재생 중"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "취소"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "열기"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "외부 링크 열기"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "열립니다: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "{protocol} 링크를 열까요?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "{message}에 대한 메시지가 수신되었습니다."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType} 메시지"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "{message}에 대한 메시지가 수신되었습니다. {messageType} 메시지는 아직 지원되지 않습니다. 자세한 내용은 콘솔을 참조하세요."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{#개 항목 발견} other{#개 항목 발견}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "로그"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI는 실험적이며 언제든지 변경될 수 있습니다."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "출력"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Bermain dalam Gambar-dalam-Gambar"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Batal"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Buka"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Buka Pautan Luaran"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Ini akan membuka: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Buka pautan {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Mesej diterima untuk {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Mesej MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Mesej diterima untuk {message}. Mesej {messageType} belum disokong lagi, rujuk konsol untuk butiran lanjut."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# item dijumpai} other{# item dijumpai}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Log"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI adalah eksperimen dan boleh berubah pada bila-bila masa."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Output"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Reproduzindo em Picture-in-Picture"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Cancelar"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Abrir"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Abrir Link Externo"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Isto abrirá: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Abrir link {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Mensagem recebida para {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Mensagem {messageType} do MCP-UI"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Mensagem recebida para {message}. As mensagens {messageType} ainda não são suportadas, consulte o console para mais detalhes."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# item encontrado} other{# itens encontrados}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Registos"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "A IU do MCP é experimental e pode mudar a qualquer momento."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Saída"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Воспроизведение в режиме «картинка в картинке»"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Отмена"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Открыть"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Открыть внешнюю ссылку"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Будет открыто: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Открыть ссылку {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Получено сообщение для {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Сообщение MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Получено сообщение для {message}. Сообщения {messageType} пока не поддерживаются; подробности смотрите в консоли."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count, plural, one {Найден # элемент} few {Найдено # элемента} many {Найдено # элементов} other {Найдено # элемента}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Журналы"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI является экспериментальным и может измениться в любой момент."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Вывод"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Resim İçinde Resim'de Oynatma"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "İptal"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Açık"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Dış Bağlantıyı Aç"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Bu açılacak: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "{protocol} bağlantısı açılsın mı?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "{message} için mesaj alındı."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType} mesajı"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "{message} için mesaj alındı. {messageType} mesajları henüz desteklenmiyor; daha fazla ayrıntı için konsola bakın."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# öğe bulundu} other{# öğe bulundu}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Günlükler"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP Kullanıcı arayüzü deneyseldir ve herhangi bir zamanda değişebilir."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Çıkış"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "Đang phát ở chế độ Hình-trong-hình"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "Hủy"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "Mở"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "Mở liên kết bên ngoài"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "Thao tác này sẽ mở: {url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "Mở liên kết {protocol}?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "Đã nhận thông báo cho {message}."
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "Thông báo MCP-UI {messageType}"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "Đã nhận thông báo cho {message}. Thông báo {messageType} chưa được hỗ trợ, hãy tham khảo console để biết thêm chi tiết."
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{# mục được tìm thấy} other{# mục được tìm thấy}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "Nhật ký"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI là thử nghiệm và có thể thay đổi bất kỳ lúc nào."
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "Đầu ra"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "正在以画中画播放"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "取消"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "打开"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "打开外部链接"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "这将打开:{url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "打开 {protocol} 链接?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "已收到 {message} 的消息。"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType} 消息"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "已收到 {message} 的消息。目前尚不支持 {messageType} 类型的消息,详见控制台。"
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{找到 # 项} other{找到 # 项}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "日志"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI 是实验性功能,随时可能更改。"
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "输出"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1949,30 +1949,6 @@
|
|||
"mcpAppRenderer.playingInPip": {
|
||||
"defaultMessage": "正在子母畫面中播放"
|
||||
},
|
||||
"mcpUIResourceRenderer.cancelButton": {
|
||||
"defaultMessage": "取消"
|
||||
},
|
||||
"mcpUIResourceRenderer.openButton": {
|
||||
"defaultMessage": "開啟"
|
||||
},
|
||||
"mcpUIResourceRenderer.openExternalLinkTitle": {
|
||||
"defaultMessage": "開啟外部連結"
|
||||
},
|
||||
"mcpUIResourceRenderer.openLinkDetail": {
|
||||
"defaultMessage": "這會開啟:{url}"
|
||||
},
|
||||
"mcpUIResourceRenderer.openProtocolLink": {
|
||||
"defaultMessage": "要開啟 {protocol} 連結嗎?"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastMessageReceived": {
|
||||
"defaultMessage": "已收到 {message} 的訊息。"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastTitle": {
|
||||
"defaultMessage": "MCP-UI {messageType} 訊息"
|
||||
},
|
||||
"mcpUIResourceRenderer.toastUnsupported": {
|
||||
"defaultMessage": "已收到 {message} 的訊息。目前尚不支援 {messageType} 訊息,詳情請參閱主控台。"
|
||||
},
|
||||
"mentionPopover.itemsFound": {
|
||||
"defaultMessage": "{count,plural,one{找到 # 個項目} other{找到 # 個項目}}"
|
||||
},
|
||||
|
|
@ -4430,9 +4406,6 @@
|
|||
"toolCallWithResponse.logs": {
|
||||
"defaultMessage": "記錄"
|
||||
},
|
||||
"toolCallWithResponse.mcpUiExperimental": {
|
||||
"defaultMessage": "MCP UI 為實驗性功能,可能隨時變更。"
|
||||
},
|
||||
"toolCallWithResponse.output": {
|
||||
"defaultMessage": "輸出"
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue