contextui
Build, run, and publish visual workflows on ContextUI — a local-first desktop platform for AI agents. Create React TSX workflows (dashboards, tools, apps, visualizations), manage local Python backend servers, test workflows via scoped UI automation within the ContextUI app window, and optionally publish to the ContextUI Exchange. All tools operate locally on the user's machine under standard OS permissions — no remote execution or privilege escalation. Python backends bind to localhost. See SECURITY.md for the full capability scope and trust model. Requires ContextUI installed locally and MCP server configured.
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install clawskills:clawskills~midz99-contextuicURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~midz99-contextui/file -o midz99-contextui.md# 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>
{/* Content */}
{tab === 'setup' ? (
<ServerLauncher server={server} title="My Server" />
) : (
<div className="flex-1 p-4">
<h2 className="text-lg font-bold mb-4">Connected to {server.serverUrl}</h2>
{/* Your feature UI here */}
</div>
)}
</di