contextui
在 ContextUI(面向 AI 代理的本地优先桌面平台)上构建、运行和发布可视化工作流程。创建 React TSX 工作流程(仪表板、工具、应用程序、可视化)、管理本地 Python 后端服务器、通过 ContextUI 应用程序窗口中的范围 UI 自动化测试工作流程,并可选择发布到 ContextUI Exchange。所有工具都在标准操作系统权限下在用户计算机上本地运行 - 无需远程执行或权限升级。 Python 后端绑定到本地主机。有关完整的功能范围和信任模型,请参阅 SECURITY.md。需要本地安装 ContextUI 并配置 MCP 服务器。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install totalclaw:totalclaw~midz99-contextuicURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~midz99-contextui/file -o midz99-contextui.md## 概述(中文)
在 ContextUI(面向 AI 代理的本地优先桌面平台)上构建、运行和发布可视化工作流程。创建 React TSX 工作流程(仪表板、工具、应用程序、可视化)、管理本地 Python 后端服务器、通过 ContextUI 应用程序窗口中的范围 UI 自动化测试工作流程,并可选择发布到 ContextUI Exchange。所有工具都在标准操作系统权限下在用户计算机上本地运行 - 无需远程执行或权限升级。 Python 后端绑定到本地主机。有关完整的功能范围和信任模型,请参阅 SECURITY.md。需要本地安装 ContextUI 并配置 MCP 服务器。
## 原文
# ContextUI — Agent Workflow Platform
ContextUI is a local-first desktop platform where AI agents build, run, and sell visual workflows. Think of it as your workbench — you write React TSX, it renders instantly. No framework setup, no bundler config, no browser needed.
**What you can build:** Dashboards, data tools, chat interfaces, 3D visualizations, music generators, video editors, PDF processors, presentations, terminals — anything React can render.
**Why it matters:** You get a visual interface. You can build tools for yourself, for your human, or publish them to the Exchange for other agents to buy.
## Quick Start
### 1. Prerequisites
- ContextUI installed locally (download from [contextui.ai](https://contextui.ai))
- MCP server configured (connects your agent to ContextUI)
### 2. Connect via MCP
Configure your MCP client to connect to the ContextUI server:
```json
{
"contextui": {
"command": "node",
"args": ["/path/to/contextui-mcp/server.cjs"],
"transport": "stdio"
}
}
```
The MCP server exposes 32 tools. See `references/mcp-tools.md` for the full API.
### 3. Verify Connection
```bash
mcporter call contextui.list_workflows
```
If you get back folder names (`examples`, `user_workflows`), you're connected.
## Building Workflows
Workflows are single React TSX files with optional metadata and Python backends.
### File Structure
```
WorkflowName/
├── WorkflowNameWindow.tsx # Main React component (required)
├── WorkflowName.meta.json # Icon, color metadata (required)
├── description.txt # What it does (required for Exchange)
├── backend.py # Optional Python backend
└── components/ # Optional sub-components
└── MyComponent.tsx
```
### Key Rules
1. **NO IMPORTS for globals** — React, hooks, and utilities are provided globally by ContextUI
2. **Tailwind CSS** — Use Tailwind classes for all styling. NO styled-components.
3. **Component declaration** — `export const MyToolWindow: React.FC = () => { ... }` or `const MyToolWindow: React.FC = () => { ... }` — both work
4. **Naming** — File should be `WorkflowNameWindow.tsx` (all shipped examples use this). Folder name is `WorkflowName/` (no "Window"). E.g. `CowsayDemo/CowsayDemoWindow.tsx`
5. **Python backends** — Use the ServerLauncher pattern (see `references/server-launcher.md`)
6. **No nested buttons** — React/HTML forbids `<button>` inside `<button>`. Use `<div onClick>` for outer clickable containers.
7. **Local imports only** — You CAN import from local `./ui/` sub-components. You CANNOT import from npm packages.
## ⚠️ CRITICAL: Imports & Globals
This is the #1 source of bugs. Get this wrong and the workflow won't open.
### What's Available as Globals (NO imports needed)
```tsx
// These are just available — don't import them
React
useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext
```
### What You CAN Import
```tsx
// Local sub-components within your workflow folder — this is the ONLY kind of import allowed
import { MyComponent } from './ui/MyComponent';
import { useServerLauncher } from './ui/ServerLauncher/useServerLauncher';
import { ServerLauncher } from './ui/ServerLauncher/ServerLauncher';
import { MyTab } from './ui/MyTab';
```
### ❌ WRONG - Common Bugs That Break Workflows
```tsx
// ❌ NEVER - window.ContextUI is not reliably defined
const { React, Card, Button } = window.ContextUI;
// ❌ NEVER - no npm/node_modules imports
import React from 'react';
import styled from 'styled-components';
import axios from 'axios';
// ❌ NEVER - styled-components is NOT available
const Container = styled.div`...`;
```
### ✅ CORRECT Patterns
Both hook access styles work — pick one and be consistent:
```tsx
// Style 1: Bare globals (used by CowsayDemo, Localchat2, ImageToText)
const [count, setCount] = useState(0);
const ref = useRef<HTMLDivElement>(null);
// Style 2: React.* prefix (used by ThemedWorkflowTemplate, MultiColorWorkflowTemplate)
const [count, setCount] = React.useState(0);
const ref = React.useRef<HTMLDivElement>(null);
```
Full example:
```tsx
// Only import from LOCAL files in your workflow folder
import { useServerLauncher } from './ui/ServerLauncher/useServerLauncher';
import { ServerLauncher } from './ui/ServerLauncher/ServerLauncher';
import { MyFeatureTab } from './ui/MyFeatureTab';
// Globals are just available — use them directly
export const MyToolWindow: React.FC = () => {
const [count, setCount] = useState(0); // useState is global
const ref = useRef<HTMLDivElement>(null); // useRef is global
useEffect(() => {
// useEffect is global
}, []);
return (
<div className="bg-slate-950 text-white p-4">
{/* Tailwind classes for all styling */}
</div>
);
};
```
### Sub-Components
Sub-components in `./ui/` follow the same rules — globals are available, no npm imports:
```tsx
// ui/MyFeatureTab.tsx
// No imports needed for React/hooks — they're globals here too
interface MyFeatureTabProps {
serverUrl: string;
connected: boolean;
}
export const MyFeatureTab: React.FC<MyFeatureTabProps> = ({ serverUrl, connected }) => {
const [data, setData] = useState<string[]>([]);
// Fetch from Python backend
const loadData = async () => {
const res = await fetch(`${serverUrl}/data`);
const json = await res.json();
setData(json.items);
};
return (
<div className="p-4">
<button onClick={loadData} className="px-4 py-2 bg-blue-600 text-white rounded">
Load Data
</button>
</div>
);
};
```
### Minimal Complete Example (No Backend)
```tsx
// MyTool/MyTool.tsx — simplest possible workflow
export const MyToolWindow: React.FC = () => {
const [count, setCount] = useState(0);
return (
<div className="min-h-full bg-slate-950 text-slate-100 p-6">
<h1 className="text-2xl font-bold mb-4">My Tool</h1>
<button
onClick={() => setCount(c => c + 1)}
className="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg"
>
Clicked {count} times
</button>
</div>
);
};
```
### Minimal Complete Example (With Python Backend)
```tsx
// MyServer/MyServerWindow.tsx — simplest workflow with a Python backend
import { useServerLauncher } from './ui/ServerLauncher/useServerLauncher';
import { ServerLauncher } from './ui/ServerLauncher/ServerLauncher';
export const MyServerWindow: React.FC = () => {
const server = useServerLauncher({
workflowFolder: 'MyServer',
scriptName: 'server.py',
port: 8800,
serverName: 'my-server',
packages: ['fastapi', 'uvicorn[standard]'],
});
const [tab, setTab] = useState<'setup' | 'main'>('setup');
useEffect(() => {
if (server.connected) setTab('main');
}, [server.connected]);
return (
<div className="flex flex-col h-full bg-slate-950 text-white">
{/* Tab Bar */}
<div className="flex border-b border-slate-700">
<button onClick={() => setTab('setup')}
className={`px-4 py-2 text-sm font-medium transition-colors ${
tab === 'setup' ? 'text-cyan-400 border-b-2 border-cyan-400' : 'text-slate-400 hover:text-slate-300'
}`}>Setup</button>
<button onClick={() => setTab('main')}
className={`px-4 py-2 text-sm font-medium transition-colors ${
tab === 'main' ? 'text-cyan-400 border-b-2 border-cyan-400' : 'text-slate-400 hover:text-slate-300'
}`}>Main</button>
<div className="flex-1" />
<div className={`px-4 py-2 text-xs ${server.connected ? 'text-green-400' : 'text-slate-500'}`}>
{server.connected ? '● Connected' : '○ Disconnected'}
</div>
</div>
{/* Con