使用说明
MCP用户界面
使用srcdoc和基于消息的资源/工具调用桥将MCP相关UI内容加载到沙盒iframe中的最小web组件主机。
特性
- 本机web组件(无框架依赖关系)。
<mcp-view>解决ui://通过解析器获取资源。- 默认情况下,HTML会被重写以使用
mcp-*用于资源加载的组件。 - 可选的
resource-base模式重写ui://将资源转换为本地浏览器可加载的URL。 - Iframe引导一个小型桥接脚本和实用程序CSS。
- 主题系统
--mcp-变量和内置.mcp-*公用事业类。 - 对可选https/http资源的远程分配列表支持。
- 可选的自动高度模式,用于内容驱动的尺寸调整。
安装
npm install mcp-view
基本用法(ESM)
<script type="module">
import "mcp-view";
</script>
<mcp-view
src="ui://index.html"
base="/mcp/resources/"
style="display:block; width: 100%; height: 420px;"></mcp-view>
基本用法(UMD)
<script src="./dist/mcp-view.umd.min.js"></script>
<mcp-view
src="ui://index.html"
base="/mcp/resources/"
style="display:block; width: 100%; height: 420px;"></mcp-view>
默认的获取解析器使用 base (或 /mcp/resources/ 当省略时)。
使用加载本地资源 resource-base
默认情况下, mcp-ui 负载 ui:// 通过它的信息桥,脚本、风格和媒体。
如果你提供 resource-base, mcp-ui 保持本机标签如下 <script>, <link>,以及 <img> 在iframe文档中重写 ui:// URL改为浏览器可加载的URL。如果没有自定义 resolver 设置, resource-base 也用作根的默认解析器 src 文件。
<mcp-view
src="ui://index.html"
resource-base="/mcp/resources?uri={uri}"></mcp-view>
当您希望与现有的MCP应用程序更好地兼容时,这是推荐的模式,特别是对于正常的脚本加载和模块导入。
resource-base 支持与相同的URL模式 base:
{path}替换已剥离的ui://路径{uri}替换完整编码ui://统一资源标识符- 如果该值包含
?,&uri=已附加 - 否则,剥离的路径将附加到基础路径
如果 resource-base 未设置, mcp-ui 保留了传统的基于网桥的加载器,以实现向后兼容性。
延迟初始渲染
如果主机需要设置以下属性 css, csp, resolver,或 toolCaller 在加载iframe之前,您可以推迟初始渲染。
<mcp-view
defer
src="ui://review/index.html"
resource-base="/mcp/resources?uri={uri}"></mcp-view>
然后,在设置属性后,调用 render() 一次:
const view = document.querySelector("mcp-view");
view.css = { "--mcp-color-bg": "#111" };
view.csp = "default-src 'none'; style-src 'unsafe-inline'";
view.resolver = async(uri) => fetch(`/mcp/resources?uri=${encodeURIComponent(uri)}`);
view.render();
主题化
该组件首先注入默认的CSS变量和实用程序类,然后是您的覆盖。
主题URL(属性)
<mcp-view theme="https://example.com/theme.css"></mcp-view>
theme 作为样式表链接加载。给 ui:// 默认情况下,它使用传统解析器桥的URL,或 resource-base 当启用本地资源加载时。对于http/https,它始终是允许的,不需要手动分配。相对URL解析主机页面。
CSS覆盖(属性)
view.css = `.mcp-button { border-radius: 999px; }`;
CSS变量(对象)
view.css = {
"--mcp-color-primary": "#2d5bff",
"--mcp-color-bg": "#ffffff",
};
不支持以下功能 css;传递字符串或变量对象。
语法变量
默认语法高亮标记作为CSS变量提供:
--mcp-syntax-comment--mcp-syntax-constant--mcp-syntax-keyword--mcp-syntax-entity--mcp-syntax-tag--mcp-syntax-variable--mcp-syntax-string--mcp-syntax-number--mcp-syntax-operator--mcp-syntax-punctuation
工具栏实用程序
<div class="mcp-toolbar">
<button class="mcp-button mcp-button-secondary">One</button>
<button class="mcp-button mcp-button-secondary active" aria-pressed="true">Two</button>
<button class="mcp-button mcp-button-secondary">Three</button>
</div>
将数据传递到iframe
设置 data 主机上的财产。iframe可以访问 window.mcpData 并倾听 mcp-data 事件。
view.data = { input: "hello", output: null };
将完成的工具结果传递给iframe
如果主机已经有一个完整的MCP工具结果,并希望在工具调用后呈现UI,请设置 toolResult 主机上的财产。iframe通过用于实时工具调用的相同客户机端结果处理程序接收它:
window.mcp.ontoolresultapp.ontoolresult
view.toolResult = {
content: [{ type: "text", text: "Rendered in the app" }],
structuredContent: { items: [] },
_meta: { some: "value" },
isError: false,
};
这是为了实现嵌入式应用程序的MCP应用程序兼容性:应用程序在工具调用后收到与正常情况下相同的结果包络形状,而不需要从iframe内部重放工具调用。
这还不是完全符合有线级MCP Apps协议。在内部, mcp-ui 仍然使用自己的网桥消息,例如 mcp:tool-result 而不是发出确切的MCP Apps JSON-RPC通知名称。
自动高度
允许iframe报告其内容高度,以便host元素调整自身大小:
<mcp-view src="ui://index.html" auto-height="true"></mcp-view>
你可以用以下方式限制高度 max-height (像素):
<mcp-view src="ui://index.html" auto-height="true" max-height="800"></mcp-view>
注意:如果您的内容设置 html, body { height: 100%; } 或 min-height: 100%,报告的高度可能至少保持在视口高度。
MCP应用程序兼容性
iframe桥现在公开了一个MCP应用程序风格的来宾API。
重要区别:
mcp-ui旨在在应用程序边界实现实用的MCP应用程序兼容性:嵌入式应用程序可以使用MCP应用程序风格的API并接收MCP风格的结果信封,包括structuredContent.mcp-ui尚未声称完全符合有线级MCP Apps协议。内部主机/iframe网桥仍在使用postMessage消息类型,例如mcp:tool-result,mcp:tool-input,以及mcp:connect.
当前的目标是使iframe中现有的MCP Apps兼容客户端代码能够正确运行。如果需要,稍后可以调整确切的协议和通知命名。
iframe桥现在公开了一个MCP应用程序风格的来宾API:
const app = new App({ name: "My App", version: "1.0.0" });
await app.connect();
支持的客户端API:
app.connect()app.callServerTool(...)app.sendMessage(...)app.ontoolinputapp.ontoolresultapp.onhostcontextchangedapp.getHostContext()
iframe和包导出中也提供了帮助函数:
applyHostStyleVariables(...)applyHostFonts(...)applyDocumentTheme(...)
年长者 window.mcp.* API仍然作为兼容层存在,但新客户端应该更喜欢 App API
宿主样式和CSS变量
mcp-ui 现在支持通过主机上下文进行MCP Apps风格的主机主题化:
hostContext.themehostContext.styles.variableshostContext.styles.css.fonts
在iframe内部,应用程序可以这样应用它们:
const app = new App({ name: "My App", version: "1.0.0" });
app.onhostcontextchanged = (ctx) => {
if (ctx.theme) {
applyDocumentTheme(ctx.theme);
}
if (ctx.styles?.variables) {
applyHostStyleVariables(ctx.styles.variables);
}
if (ctx.styles?.css?.fonts) {
applyHostFonts(ctx.styles.css.fonts);
}
};
await app.connect();
const ctx = app.getHostContext();
if (ctx?.styles?.variables) {
applyHostStyleVariables(ctx.styles.variables);
}
为了向后兼容性, mcp-ui 还将MCP Apps令牌映射到较旧的 --mcp-* 现有应用程序使用的变量。这意味着较旧的应用程序可以继续使用 --mcp-color-*, --mcp-font-*, --mcp-radius-*,以及 --mcp-shadow-* 而较新的应用程序可以使用MCP应用程序变量,例如 --color-background-primary, --font-sans,以及 --border-radius-md 直接。
工具调用
在iframe内部,引导程序注入了两个遗留内容 window.mcp.callTool(params) 助手和MCP Apps兼容 App 桥。工具响应保留了MCP结果包络,因此 structuredContent 直接在iframe中可用。
主机侧:
const view = document.querySelector("mcp-view");
view.toolCaller = async (params) => {
return await runTool(params);
};
Iframe侧:
const result = await window.mcp.callTool({
name: "read_file",
arguments: { path: "src/main.ts" },
});
// If the host tool returns an MCP Apps-style envelope, the iframe receives it.
console.log(result.structuredContent);
如果主机端 toolCaller 返回一个普通值legacy callTool() 用法仍然直接解析该值。如果它返回一个形状像MCP工具结果的对象,例如:
{
content: [{ type: "text", text: "Rendered in the app" }],
structuredContent: { items: [] },
_meta: { some: "value" },
isError: false,
}
然后,该信封将原封不动地转发到iframe。
图层顺序
默认情况下,级联为:defaults→ ui://内容→ 应用程序覆盖。您可以通过以下方式覆盖图层顺序 layers 财产。
view.layers = ["mcp-default", "mcp-content", "mcp-user"];
将自定义图层作为目标 ui:// 样式表,添加 layer="custom" 关于原作 <link>:
<link
rel="stylesheet"
href="ui://application/app.css"
layer="bob">
远程allowlist
默认情况下,远程资源被阻止。通过以下任一方式启用它们:
view.allowedOrigins = ["https://example.com", "cdn.example.com"];
或
view.allowedRemote = (url) => url.startsWith("https://cdn.example.com/");
解析器返回类型
解析器可以返回:
ResponseBlobstringArrayBufferUint8Array
构建
npm run build
捆绑使用(UMD/IIFE)
<script src="./dist/mcp-view.umd.js"></script>
<script>
McpView.defineMcpView();
const view = document.querySelector("mcp-view");
view.src = "ui://application/index.html";
view.resolver = async() => "<html><body>...</body></html>";
</script>
精简版本可在以下网址获得:
dist/mcp-view.esm.min.jsdist/mcp-view.umd.min.js
例子
- UMD:
examples/basic.html - ESM:
examples/basic-esm.html(需要通过HTTP提供服务)
要在本地运行ESM示例:
npx serve
属性与特性
- 属性仅为字符串,适用于HTML标记。
- 属性是JavaScript值(函数、对象、数组)。
使用 src 对于根文档。
使用 css 用于内联重写(字符串或变量对象)。
使用 resolver (属性)用于自定义解析。
使用 base 启用默认的fetch解析器。它解析相对于主页的相对URL。
解析器(属性)
view.resolver = (uri) => fetch(`/mcp-ui?uri=${encodeURIComponent(uri)}`);
默认解析器(属性)
如果你设置 base,组件将解析 ui:// 使用这些规则的URI(按顺序):
{path}被剥离的路径所取代(否ui://).{uri}被替换为完整的URI(URL编码)。- 如果
base包含?,它附加&uri=带有完整的URI(URL编码)。 - 否则,它会将剥离的路径附加到基础。
<mcp-view src="ui://index.html" base="/mcp/resources/"></mcp-view>
<mcp-view src="ui://index.html" base="/mcp/resources/{path}"></mcp-view>
<mcp-view src="ui://index.html" base="/mcp/resources?uri="></mcp-view>
<mcp-view src="ui://index.html" base="/mcp/resources/{uri}"></mcp-view>
如果没有提供解析器 base 已设置,默认为 /mcp/resources/.
ui:// 构造时去掉前缀 {path}.
