Loading...
Loading...
Interactive MCP visual output via @json-render/mcp. Upgrade plain JSON tool responses to interactive dashboards rendered in sandboxed iframes inside Claude, Cursor, and ChatGPT conversations. Covers createMcpApp(), registerJsonRenderTool(), CSP config, streaming, and dashboard component patterns. Use when building MCP servers that return visual output, upgrading existing MCP tools with interactive UI, or creating eval/monitoring dashboards.
npx skill4agent add yonatangross/orchestkit mcp-visual-output@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
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| 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 |
defineCatalog()createMcpApp()registerJsonRenderTool()useJsonRenderApp()<Renderer />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()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,
})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} />
}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,
},
}){
"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" }
]
}
}
}
}| 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 |
| 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 |
script-src 'unsafe-inline'htmlcreateMcpApp()ork:mcp-patternsork:json-render-catalogork:multi-surface-renderork:ai-ui-generation