Skip to content

Latest commit

 

History

History
279 lines (226 loc) · 13.1 KB

ARCHITECTURE.md

File metadata and controls

279 lines (226 loc) · 13.1 KB

Architecture Overview

ContextUI is an Electron desktop app with a React (Vite + TypeScript) renderer. It dynamically loads workflow components from the filesystem, manages Python virtual environments for backend servers, and exposes tools via the Model Context Protocol (MCP).

High-Level Architecture

┌─────────────────────────────────────────────────────┐
│                    Electron App                      │
│                                                      │
│  ┌──────────────┐          ┌──────────────────────┐ │
│  │  Main Process │◄── IPC ──►  Renderer Process    │ │
│  │  (electron/)  │          │  (src/)              │ │
│  │               │          │                      │ │
│  │  • Window mgmt│          │  • React UI          │ │
│  │  • File system│          │  • Workflow tabs      │ │
│  │  • Python mgmt│          │  • Module loader      │ │
│  │  • MCP server │          │  • Tool registry      │ │
│  │  • Tool reg.  │          │  • Theme manager      │ │
│  └──────────────┘          └──────────────────────┘ │
│          │                                           │
│          ▼                                           │
│  ┌──────────────┐          ┌──────────────────────┐ │
│  │ Python Venvs  │          │  Workflow Modules     │ │
│  │ (per workflow) │          │  (TSX on filesystem)  │ │
│  └──────────────┘          └──────────────────────┘ │
└─────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────┐
│  MCP Server      │  (stdio transport)
│  Port 8800       │  Tool Registry (HTTP)
└─────────────────┘

Directory Structure

contextui/
├── electron/                       # Main process (Node.js/Electron)
│   ├── main.cjs                    # App lifecycle, window management, IPC handlers
│   ├── preload.cjs                 # Context bridge — exposes IPC to renderer
│   ├── module-bundler.cjs          # TSX → JS bundling via esbuild
│   ├── python-manager.cjs          # Python/venv lifecycle management
│   ├── npm-manager.cjs             # npm package management for workflows
│   ├── tool-registry-server.cjs    # HTTP tool registry (port 8800)
│   ├── mcp-server-standalone.cjs   # MCP server (stdio transport)
│   ├── mcp-registration.cjs        # MCP tool registration
│   ├── external-mcp-client.cjs     # Connect to external MCP servers
│   ├── mcp-app-security.cjs        # Security policies for MCP apps
│   ├── import-parser.cjs           # Parse local imports in TSX files
│   ├── dependency-resolver.cjs     # Resolve workflow dependencies
│   ├── bundle-mcp-server.cjs       # Bundle MCP server for distribution
│   ├── user-paths.cjs              # User data directory management
│   └── defaults/                   # Default configs (html_apps, local_servers)
│
├── src/                            # Renderer process (React + TypeScript)
│   ├── main.tsx                    # Entry point
│   ├── App.tsx                     # Root component
│   ├── components/                 # Shared UI components
│   │   ├── WindowContainer.tsx     # Tab/window container
│   │   ├── WindowManagerComponent.tsx
│   │   ├── DockingContainer.tsx    # Dockable layout system
│   │   ├── MenuBar.tsx             # Application menu bar
│   │   ├── MCPAppRenderer.tsx      # Renders MCP app content
│   │   ├── MCPAppSecurityDialog.tsx
│   │   ├── WelcomeModal.tsx        # First-run welcome screen
│   │   ├── LicenseAgreement.tsx
│   │   ├── PinWorkflowDialog.tsx
│   │   └── SetIconDialog.tsx
│   ├── modules/                    # Built-in app modules (settings/management UIs)
│   │   ├── WorkflowsWindow.tsx     # Workflow browser & launcher
│   │   ├── PythonManagerWindow.tsx  # Python/venv management UI
│   │   ├── ToolManagerWindow.tsx   # Tool registry UI
│   │   ├── MCPServersWindow.tsx    # External MCP server management
│   │   ├── LocalServersWindow.tsx  # Local server management
│   │   ├── NpmManagerWindow.tsx    # npm package management
│   │   ├── HtmlAppsWindow.tsx      # HTML app launcher
│   │   ├── LayoutManagerWindow.tsx # Layout save/restore
│   │   └── DynamicNpmWindow.tsx    # Dynamic npm module loader
│   ├── managers/                   # State & lifecycle managers
│   │   ├── DynamicModuleLoader.ts  # Loads TSX workflows at runtime
│   │   ├── ModuleManager.ts        # Module registration & tracking
│   │   ├── WindowManager.ts        # Window/tab lifecycle
│   │   ├── LayoutManager.ts        # Tab layout persistence
│   │   ├── ToolRegistry.ts         # Client-side tool registry
│   │   ├── ToolClient.ts           # Tool invocation client
│   │   ├── ToolBridge.ts           # Bridge between tools and UI
│   │   ├── MCPBridge.ts            # MCP file-based command bridge
│   │   ├── MCPAppManager.ts        # MCP app lifecycle
│   │   ├── ThemeManager.ts         # Theme/appearance management
│   │   ├── EventBus.ts             # Cross-component event system
│   │   └── UITestBridge.ts         # UI automation for MCP tools
│   ├── errors/                     # Error handling system
│   │   ├── ErrorContext.tsx         # Error state provider
│   │   ├── ErrorLogger.ts          # Error logging
│   │   ├── ErrorConsolePanel.tsx   # Error console UI
│   │   └── ErrorToast.tsx          # Error notifications
│   ├── types/                      # TypeScript type definitions
│   └── utils/
│       └── ipc.ts                  # IPC helper utilities
│
├── example_modules/                # Built-in example workflows
├── docs/                           # Documentation
├── scripts/                        # Build & publish scripts
├── mcp-config.json                 # MCP server configuration
├── vite.config.ts                  # Vite bundler config
├── electron-builder.json           # Electron Builder config
└── package.json

Main Process (electron/)

The main process handles everything that needs Node.js or OS-level access.

main.cjs — App Lifecycle

The core of the application. Handles:

  • Window management — creating and configuring the BrowserWindow
  • IPC handlers — all ipcMain.handle() registrations for renderer communication
  • Workflow scanning — discovers .tsx/.jsx files in workflow directories
  • File watching — monitors workflow folders for changes (via chokidar) and triggers hot-reload
  • Profile management — multiple user profiles with separate workflow directories
  • First-run setup — copies bundled example workflows to user directory

module-bundler.cjs — TSX Compilation

Compiles workflow TSX files into executable JavaScript using esbuild. Handles:

  • Local ./ui/ imports — resolves and bundles sub-components
  • React globals injection — makes useState, useEffect, etc. available without imports
  • Caching — only re-bundles when source files change

python-manager.cjs — Python Management

Manages the full Python lifecycle:

  • Detection — finds system Python installations
  • Installation — downloads and installs Python if needed
  • Virtual environments — creates per-workflow venvs in ~/ContextUI/<profile>/python_venvs/
  • Package management — pip install with GPU-aware builds (CUDA/Metal detection)
  • Server lifecycle — start, stop, and monitor Python backend servers
  • Script execution — run one-off Python scripts

tool-registry-server.cjs — Tool Registry

An HTTP server (port 8800) that acts as the central registry for tools. Workflows and MCP servers register their tools here, and other components can discover and invoke them.

mcp-server-standalone.cjs — MCP Server

Implements the Model Context Protocol over stdio transport. Exposes ContextUI's capabilities as tools that AI agents can call:

  • Workflow management (list, read, launch, close)
  • Python server management (start, stop, status)
  • UI automation (screenshot, click, type, DOM inspection)
  • Tab management (list, switch)

preload.cjs — Context Bridge

Securely exposes IPC channels from the main process to the renderer. Uses a whitelist of allowed channels for both invoke (request/response) and send (fire-and-forget) patterns.

Renderer Process (src/)

The renderer is a React app built with Vite and TypeScript, styled with Tailwind CSS.

Module Loading

The DynamicModuleLoader is the heart of the workflow system:

  1. Scan — main process discovers .tsx/.jsx files in workflow directories
  2. Bundle — module-bundler.cjs compiles TSX → JS with esbuild, resolving local imports
  3. Load — renderer evaluates the bundled code with React globals in scope
  4. Render — component is mounted in a tab via WindowContainer

This enables hot-reload during development — edit a TSX file and the tab updates automatically.

Manager Pattern

Managers in src/managers/ are singleton services that handle specific concerns:

Manager Responsibility
DynamicModuleLoader Runtime TSX loading and evaluation
ModuleManager Module registration and metadata
WindowManager Tab/window lifecycle
LayoutManager Save/restore tab layouts
ToolRegistry Client-side tool discovery
ToolClient Tool invocation
MCPBridge File-based command bridge for MCP
UITestBridge UI automation (click, type, screenshot) for MCP tools
ThemeManager Theme and appearance
EventBus Cross-component event system

Built-In Modules

The src/modules/ directory contains management UIs that ship with the app — the Workflows browser, Python Manager, Tool Manager, MCP Servers panel, etc. These are distinct from user workflows in example_modules/.

Data Flow

Workflow Loading

Filesystem (.tsx files)
    │
    ▼
main.cjs (scan + watch)
    │
    ▼
module-bundler.cjs (esbuild compile)
    │
    ▼ IPC
DynamicModuleLoader (evaluate JS)
    │
    ▼
WindowContainer (render in tab)

Python Backend

useServerLauncher (React hook)
    │
    ▼ IPC
python-manager.cjs
    │
    ├── Create venv
    ├── Install packages (GPU-aware)
    ├── Start uvicorn server
    │
    ▼
Python server on localhost:<port>
    │
    ▼ HTTP (fetch)
React workflow UI

MCP Tool Invocation

AI Agent
    │
    ▼ stdio
mcp-server-standalone.cjs
    │
    ├── Workflow tools → IPC → renderer
    ├── Python tools → python-manager.cjs
    ├── UI tools → UITestBridge → DOM
    │
    ▼
Tool result returned to agent

Key Design Decisions

nodeIntegration: true

Workflows run with full Node.js access. This is intentional — workflows need filesystem access, Python server management, and direct IPC to the main process. ContextUI is a local development tool, not a web browser. See the Security section in the README for details.

esbuild for Bundling

Workflows are compiled with esbuild rather than Vite/webpack. esbuild is fast enough for real-time bundling on file change, and handles the TSX → JS + local import resolution that workflows need.

File-Based MCP Bridge

The MCP bridge (~/ContextUI/.mcp-bridge/) uses JSON files for communication between external processes and the Electron app. A process writes a command file, the app processes it and writes a response file. This avoids the complexity of maintaining persistent connections.

Per-Workflow Venvs

Each workflow with a Python backend gets its own virtual environment. This prevents dependency conflicts between workflows and makes cleanup straightforward — delete the venv folder.

Ports

Port Service Description
5173 Vite dev server Development only — serves the renderer
8800 Tool Registry HTTP API for tool registration and discovery

Python backend servers use workflow-specified ports (typically 8800+).