mcp-visual-output
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMCP Visual Output
MCP可视化输出
Upgrade plain MCP tool responses to interactive dashboards rendered inside AI conversations. Built on , which bridges the json-render spec system with MCP's tool/resource model -- the AI generates a typed JSON spec, and a sandboxed iframe renders it as an interactive UI.
@json-render/mcpBuilding an MCP server from scratch? Usefor server setup, transport, and security. This skill focuses on the visual output layer after your server is running.ork:mcp-patternsNeed the full component catalog? Seefor all available components, props, and composition patterns.ork:json-render-catalog
将普通的MCP工具响应升级为可在AI对话中渲染的交互式仪表盘。基于构建,它将json-render规范系统与MCP的工具/资源模型相连接——AI生成一个类型化的JSON规范,沙箱iframe将其渲染为交互式UI。
@json-render/mcp从零开始构建MCP服务器? 使用进行服务器设置、传输和安全配置。本技能专注于服务器运行后的可视化输出层。ork:mcp-patterns需要完整的组件目录? 查看获取所有可用组件、属性和组合模式。ork:json-render-catalog
Decision Tree -- Which File to Read
决策树——阅读哪个文件
What are you doing?
|
+-- Setting up visual output for the first time
| +-- New MCP server -----------> rules/mcp-app-setup.md
| +-- Existing MCP server ------> rules/mcp-app-setup.md (registerJsonRenderTool section)
|
+-- Configuring security / sandbox
| +-- CSP declarations ----------> rules/sandbox-csp.md
| +-- Iframe permissions --------> rules/sandbox-csp.md
|
+-- Rendering strategy
| +-- Progressive streaming -----> rules/streaming-output.md
| +-- Dashboard layouts ----------> rules/dashboard-patterns.md
|
+-- API reference
| +-- Server-side API -----------> references/mcp-integration.md
| +-- Component recipes ----------> references/component-recipes.md你正在做什么?
|
+-- 首次设置可视化输出
| +-- 新MCP服务器 -----------> rules/mcp-app-setup.md
| +-- 现有MCP服务器 ------> rules/mcp-app-setup.md(registerJsonRenderTool章节)
|
+-- 配置安全/沙箱
| +-- CSP声明 ----------> rules/sandbox-csp.md
| +-- Iframe权限 --------> rules/sandbox-csp.md
|
+-- 渲染策略
| +-- 渐进式流式传输 -----> rules/streaming-output.md
| +-- 仪表盘布局 ----------> rules/dashboard-patterns.md
|
+-- API参考
| +-- 服务器端API -----------> references/mcp-integration.md
| +-- 组件示例 ----------> references/component-recipes.mdQuick Reference
快速参考
| Category | Rule | Impact | Key Pattern |
|---|---|---|---|
| Setup | | HIGH | createMcpApp() and registerJsonRenderTool() |
| Security | | HIGH | CSP declarations, iframe sandboxing |
| Rendering | | MEDIUM | Progressive rendering via JSON Patch |
| Patterns | | MEDIUM | Stat grids, status badges, data tables |
Total: 4 rules across 3 categories
| 分类 | 规则 | 影响程度 | 核心模式 |
|---|---|---|---|
| 设置 | | 高 | createMcpApp()和registerJsonRenderTool() |
| 安全 | | 高 | CSP声明、Iframe沙箱化 |
| 渲染 | | 中 | 通过JSON Patch实现渐进式渲染 |
| 模式 | | 中 | 统计网格、状态徽章、数据表格 |
总计:3个分类下的4条规则
How It Works
工作原理
- Define a catalog -- typed component schemas using + Zod
defineCatalog() - Register with MCP -- for new servers or
createMcpApp()for existing onesregisterJsonRenderTool() - AI generates specs -- the model produces a JSON spec conforming to the catalog
- Iframe renders it -- a bundled React app inside a sandboxed iframe renders the spec with +
useJsonRenderApp()<Renderer />
The AI never writes HTML or CSS. It produces a structured JSON spec that references catalog components by type. The iframe app renders those components using a pre-built registry.
- 定义目录 —— 使用+ Zod创建类型化组件 schema
defineCatalog() - 注册到MCP —— 新服务器使用,现有服务器使用
createMcpApp()registerJsonRenderTool() - AI生成规范 —— 模型生成符合目录的类型化JSON规范
- Iframe渲染 —— 沙箱iframe中的捆绑React应用使用+
useJsonRenderApp()渲染该规范<Renderer />
AI不会编写HTML或CSS。它生成一个结构化的JSON规范,通过类型引用目录中的组件。Iframe应用使用预构建的注册表渲染这些组件。
Quick Start -- New MCP Server
快速开始——新MCP服务器
typescript
import { createMcpApp } from '@json-render/mcp'
import { catalog } from './catalog'
import bundledHtml from './app.html'
// 1. Create the MCP app (wraps McpServer + registers the render tool)
const app = createMcpApp({
catalog, // component schemas the AI can use
html: bundledHtml, // pre-built iframe app (single HTML file)
})
// 2. Start -- works with stdio, Streamable HTTP, or any MCP transport
app.start()typescript
import { createMcpApp } from '@json-render/mcp'
import { catalog } from './catalog'
import bundledHtml from './app.html'
// 1. 创建MCP应用(包装McpServer并注册渲染工具)
const app = createMcpApp({
catalog, // AI可使用的组件schema
html: bundledHtml, // 预构建的iframe应用(单个HTML文件)
})
// 2. 启动——支持stdio、Streamable HTTP或任何MCP传输方式
app.start()Quick Start -- Enhance Existing Server with Visual Output
快速开始——为现有服务器添加可视化输出
typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { registerJsonRenderTool } from '@json-render/mcp'
import { catalog } from './catalog'
import bundledHtml from './app.html'
const server = new McpServer({ name: 'my-server', version: '1.0.0' })
// Add visual output capability alongside existing tools
registerJsonRenderTool(server, {
catalog,
html: bundledHtml,
})typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { registerJsonRenderTool } from '@json-render/mcp'
import { catalog } from './catalog'
import bundledHtml from './app.html'
const server = new McpServer({ name: 'my-server', version: '1.0.0' })
// 在现有工具之外添加可视化输出能力
registerJsonRenderTool(server, {
catalog,
html: bundledHtml,
})Client-Side Iframe App
客户端Iframe应用
The iframe app receives specs from the MCP host and renders them:
typescript
import { useJsonRenderApp } from '@json-render/mcp/app'
import { Renderer } from '@json-render/react'
import { registry } from './registry'
function App() {
const { spec, loading } = useJsonRenderApp()
if (loading) return <Skeleton />
return <Renderer spec={spec} registry={registry} />
}Iframe应用从MCP主机接收规范并进行渲染:
typescript
import { useJsonRenderApp } from '@json-render/mcp/app'
import { Renderer } from '@json-render/react'
import { registry } from './registry'
function App() {
const { spec, loading } = useJsonRenderApp()
if (loading) return <Skeleton />
return <Renderer spec={spec} registry={registry} />
}Catalog Definition
目录定义
Catalogs define what components the AI can use. Each component has typed props via Zod:
typescript
import { defineCatalog } from '@json-render/core'
import { z } from 'zod'
export const dashboardCatalog = defineCatalog({
StatGrid: {
props: z.object({
items: z.array(z.object({
label: z.string(),
value: z.string(),
trend: z.enum(['up', 'down', 'flat']).optional(),
color: z.enum(['green', 'red', 'yellow', 'blue']).optional(),
})),
}),
children: false,
},
StatusBadge: {
props: z.object({
label: z.string(),
status: z.enum(['success', 'warning', 'error', 'info', 'pending']),
}),
children: false,
},
DataTable: {
props: z.object({
columns: z.array(z.object({ key: z.string(), label: z.string() })),
rows: z.array(z.record(z.string())),
}),
children: false,
},
})目录定义了AI可使用的组件。每个组件通过Zod拥有类型化属性:
typescript
import { defineCatalog } from '@json-render/core'
import { z } from 'zod'
export const dashboardCatalog = defineCatalog({
StatGrid: {
props: z.object({
items: z.array(z.object({
label: z.string(),
value: z.string(),
trend: z.enum(['up', 'down', 'flat']).optional(),
color: z.enum(['green', 'red', 'yellow', 'blue']).optional(),
})),
}),
children: false,
},
StatusBadge: {
props: z.object({
label: z.string(),
status: z.enum(['success', 'warning', 'error', 'info', 'pending']),
}),
children: false,
},
DataTable: {
props: z.object({
columns: z.array(z.object({ key: z.string(), label: z.string() })),
rows: z.array(z.record(z.string())),
}),
children: false,
},
})Example: Eval Results Dashboard
示例:评估结果仪表盘
The AI generates a spec like this -- flat element map, no nesting beyond 2 levels:
json
{
"root": "dashboard",
"elements": {
"dashboard": {
"type": "Card",
"props": { "title": "Eval Results -- v7.21.1" },
"children": ["stats", "table"]
},
"stats": {
"type": "StatGrid",
"props": {
"items": [
{ "label": "Skills Evaluated", "value": "94", "trend": "flat" },
{ "label": "Pass Rate", "value": "97.8%", "trend": "up", "color": "green" },
{ "label": "Avg Score", "value": "8.2/10", "trend": "up" }
]
}
},
"table": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "skill", "label": "Skill" },
{ "key": "score", "label": "Score" },
{ "key": "status", "label": "Status" }
],
"rows": [
{ "skill": "implement", "score": "9.1", "status": "pass" },
{ "skill": "verify", "score": "8.7", "status": "pass" }
]
}
}
}
}AI生成如下规范——扁平化元素映射,嵌套不超过2层:
json
{
"root": "dashboard",
"elements": {
"dashboard": {
"type": "Card",
"props": { "title": "Eval Results -- v7.21.1" },
"children": ["stats", "table"]
},
"stats": {
"type": "StatGrid",
"props": {
"items": [
{ "label": "Skills Evaluated", "value": "94", "trend": "flat" },
{ "label": "Pass Rate", "value": "97.8%", "trend": "up", "color": "green" },
{ "label": "Avg Score", "value": "8.2/10", "trend": "up" }
]
}
},
"table": {
"type": "DataTable",
"props": {
"columns": [
{ "key": "skill", "label": "Skill" },
{ "key": "score", "label": "Score" },
{ "key": "status", "label": "Status" }
],
"rows": [
{ "skill": "implement", "score": "9.1", "status": "pass" },
{ "skill": "verify", "score": "8.7", "status": "pass" }
]
}
}
}
}Key Decisions
核心决策
| Decision | Recommendation |
|---|---|
| New vs existing server | |
| CSP policy | Minimal -- only declare domains you actually need |
| Streaming | Always enable progressive rendering; never wait for full spec |
| Dashboard depth | Keep element trees flat (2-3 levels max) for streamability |
| Component count | 3-5 component types per catalog covers most dashboards |
| Visual vs text | Use visual output for multi-metric views; plain text for single values |
| 决策 | 推荐方案 |
|---|---|
| 新服务器 vs 现有服务器 | 新服务器使用 |
| CSP策略 | 最小化——仅声明实际需要的域名 |
| 流式传输 | 始终启用渐进式渲染;绝不等待完整规范 |
| 仪表盘深度 | 保持元素树扁平化(最多2-3层)以支持流式传输 |
| 组件数量 | 每个目录3-5种组件类型即可覆盖大多数仪表盘场景 |
| 可视化 vs 文本 | 多指标视图使用可视化输出;单一值使用纯文本 |
When to Use Visual Output vs Plain Text
何时使用可视化输出 vs 纯文本
| Scenario | Use Visual Output | Use Plain Text |
|---|---|---|
| Multiple metrics at a glance | Yes -- StatGrid | No |
| Tabular data (5+ rows) | Yes -- DataTable | No |
| Status of multiple systems | Yes -- StatusBadge grid | No |
| Single value answer | No | Yes |
| Error message | No | Yes |
| File content / code | No | Yes |
| 场景 | 使用可视化输出 | 使用纯文本 |
|---|---|---|
| 快速查看多个指标 | 是——使用StatGrid | 否 |
| 表格数据(5行以上) | 是——使用DataTable | 否 |
| 多个系统的状态 | 是——使用StatusBadge网格 | 否 |
| 单一值答案 | 否 | 是 |
| 错误信息 | 否 | 是 |
| 文件内容/代码 | 否 | 是 |
Common Mistakes
常见错误
- Returning raw HTML strings from MCP tools instead of json-render specs (breaks type safety, no streaming)
- Deeply nested component trees that cannot stream progressively (keep flat)
- Using in CSP declarations (security risk, unnecessary)
script-src 'unsafe-inline' - Waiting for the full spec before rendering (defeats progressive rendering)
- Defining 20+ component types in a single catalog (increases prompt token cost)
- Missing bundle in
htmlconfig (iframe has nothing to render)createMcpApp()
- 从MCP工具返回原始HTML字符串而非json-render规范(破坏类型安全,无法流式传输)
- 组件树嵌套过深,无法进行渐进式流式传输(保持扁平化)
- 在CSP声明中使用(安全风险,无必要)
script-src 'unsafe-inline' - 等待完整规范生成后再渲染(违背渐进式渲染的初衷)
- 在单个目录中定义20+种组件类型(增加提示词token成本)
- 配置中缺少
createMcpApp()包(iframe无内容可渲染)html
Related Skills
相关技能
- -- MCP server building, transport, security
ork:mcp-patterns - -- Full component catalog and composition patterns
ork:json-render-catalog - -- Rendering across Claude, Cursor, ChatGPT, web
ork:multi-surface-render - -- GenUI patterns for AI-generated interfaces
ork:ai-ui-generation
- —— MCP服务器构建、传输、安全
ork:mcp-patterns - —— 完整组件目录和组合模式
ork:json-render-catalog - —— 在Claude、Cursor、ChatGPT、网页等多端渲染
ork:multi-surface-render - —— AI生成界面的GenUI模式
ork:ai-ui-generation